1/*
2 This file is part of KNewStuff2.
3 SPDX-FileCopyrightText: 2002 Cornelius Schumacher <schumacher@kde.org>
4 SPDX-FileCopyrightText: 2003-2007 Josef Spillner <spillner@kde.org>
5 SPDX-FileCopyrightText: 2009 Frederik Gladhorn <gladhorn@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.1-or-later
8*/
9
10#include "entry.h"
11#include "entry_p.h"
12
13#include <QDomElement>
14#include <QMetaEnum>
15#include <QStringList>
16#include <QXmlStreamReader>
17#include <knewstuffcore_debug.h>
18
19#include "xmlloader_p.h"
20
21using namespace KNSCore;
22
23Entry::Entry()
24 : d(new EntryPrivate())
25{
26}
27
28Entry::Entry(const Entry &other)
29 : d(other.d)
30{
31}
32
33Entry &Entry::operator=(const Entry &other)
34{
35 d = other.d;
36 return *this;
37}
38
39bool Entry::operator<(const KNSCore::Entry &other) const
40{
41 return d->mUniqueId < other.d->mUniqueId;
42}
43
44bool Entry::operator==(const KNSCore::Entry &other) const
45{
46 return d->mUniqueId == other.d->mUniqueId && d->mProviderId == other.d->mProviderId;
47}
48
49Entry::~Entry() = default;
50
51bool Entry::isValid() const
52{
53 return !d->mUniqueId.isEmpty(); // This should not use the uniqueId getter due to the fallback!
54}
55
56QString Entry::name() const
57{
58 return d->mName;
59}
60
61void Entry::setName(const QString &name)
62{
63 d->mName = name;
64}
65
66QString Entry::uniqueId() const
67{
68 return d->mUniqueId.isEmpty() ? d->mRequestedUniqueId : d->mUniqueId;
69}
70
71void Entry::setUniqueId(const QString &id)
72{
73 d->mUniqueId = id;
74}
75
76QString Entry::providerId() const
77{
78 return d->mProviderId;
79}
80
81void Entry::setProviderId(const QString &id)
82{
83 d->mProviderId = id;
84}
85
86QStringList KNSCore::Entry::tags() const
87{
88 return d->mTags;
89}
90
91void KNSCore::Entry::setTags(const QStringList &tags)
92{
93 d->mTags = tags;
94}
95
96QString Entry::category() const
97{
98 return d->mCategory;
99}
100
101void Entry::setCategory(const QString &category)
102{
103 d->mCategory = category;
104}
105
106QUrl Entry::homepage() const
107{
108 return d->mHomepage;
109}
110
111void Entry::setHomepage(const QUrl &page)
112{
113 d->mHomepage = page;
114}
115
116Author Entry::author() const
117{
118 return d->mAuthor;
119}
120
121void Entry::setAuthor(const KNSCore::Author &author)
122{
123 d->mAuthor = author;
124}
125
126QString Entry::license() const
127{
128 return d->mLicense;
129}
130
131void Entry::setLicense(const QString &license)
132{
133 d->mLicense = license;
134}
135
136QString Entry::summary() const
137{
138 return d->mSummary;
139}
140
141void Entry::setSummary(const QString &summary)
142{
143 d->mSummary = summary;
144}
145
146QString Entry::shortSummary() const
147{
148 return d->mShortSummary;
149}
150
151void Entry::setShortSummary(const QString &summary)
152{
153 d->mShortSummary = summary;
154}
155
156void Entry::setChangelog(const QString &changelog)
157{
158 d->mChangelog = changelog;
159}
160
161QString Entry::changelog() const
162{
163 return d->mChangelog;
164}
165
166QString Entry::version() const
167{
168 return d->mVersion;
169}
170
171void Entry::setVersion(const QString &version)
172{
173 d->mVersion = version;
174}
175
176QDate Entry::releaseDate() const
177{
178 return d->mReleaseDate;
179}
180
181void Entry::setReleaseDate(const QDate &releasedate)
182{
183 d->mReleaseDate = releasedate;
184}
185
186QString Entry::payload() const
187{
188 return d->mPayload;
189}
190
191void Entry::setPayload(const QString &url)
192{
193 d->mPayload = url;
194}
195
196QDate Entry::updateReleaseDate() const
197{
198 return d->mUpdateReleaseDate;
199}
200
201void Entry::setUpdateReleaseDate(const QDate &releasedate)
202{
203 d->mUpdateReleaseDate = releasedate;
204}
205
206QString Entry::updateVersion() const
207{
208 return d->mUpdateVersion;
209}
210
211void Entry::setUpdateVersion(const QString &version)
212{
213 d->mUpdateVersion = version;
214}
215
216QString Entry::previewUrl(PreviewType type) const
217{
218 return d->mPreviewUrl[type];
219}
220
221void Entry::setPreviewUrl(const QString &url, PreviewType type)
222{
223 d->mPreviewUrl[type] = url;
224}
225
226QImage Entry::previewImage(PreviewType type) const
227{
228 return d->mPreviewImage[type];
229}
230
231void Entry::setPreviewImage(const QImage &image, PreviewType type)
232{
233 d->mPreviewImage[type] = image;
234}
235
236int Entry::rating() const
237{
238 return d->mRating;
239}
240
241void Entry::setRating(int rating)
242{
243 d->mRating = rating;
244}
245
246int Entry::numberOfComments() const
247{
248 return d->mNumberOfComments;
249}
250
251void Entry::setNumberOfComments(int comments)
252{
253 d->mNumberOfComments = comments;
254}
255
256int Entry::downloadCount() const
257{
258 return d->mDownloadCount;
259}
260
261void Entry::setDownloadCount(int downloads)
262{
263 d->mDownloadCount = downloads;
264}
265
266int Entry::numberFans() const
267{
268 return d->mNumberFans;
269}
270
271void Entry::setNumberFans(int fans)
272{
273 d->mNumberFans = fans;
274}
275
276QString Entry::donationLink() const
277{
278 return d->mDonationLink;
279}
280
281void Entry::setDonationLink(const QString &link)
282{
283 d->mDonationLink = link;
284}
285
286int Entry::numberKnowledgebaseEntries() const
287{
288 return d->mNumberKnowledgebaseEntries;
289}
290void Entry::setNumberKnowledgebaseEntries(int num)
291{
292 d->mNumberKnowledgebaseEntries = num;
293}
294
295QString Entry::knowledgebaseLink() const
296{
297 return d->mKnowledgebaseLink;
298}
299void Entry::setKnowledgebaseLink(const QString &link)
300{
301 d->mKnowledgebaseLink = link;
302}
303
304Entry::Source Entry::source() const
305{
306 return d->mSource;
307}
308
309void Entry::setEntryType(Entry::EntryType type)
310{
311 d->mEntryType = type;
312}
313
314Entry::EntryType Entry::entryType() const
315{
316 return d->mEntryType;
317}
318
319void Entry::setSource(Source source)
320{
321 d->mSource = source;
322}
323
324KNSCore::Entry::Status Entry::status() const
325{
326 return d->mStatus;
327}
328
329void Entry::setStatus(KNSCore::Entry::Status status)
330{
331 d->mStatus = status;
332}
333
334void KNSCore::Entry::setInstalledFiles(const QStringList &files)
335{
336 d->mInstalledFiles = files;
337}
338
339QStringList KNSCore::Entry::installedFiles() const
340{
341 return d->mInstalledFiles;
342}
343
344QStringList KNSCore::Entry::uninstalledFiles() const
345{
346 return d->mUnInstalledFiles;
347}
348
349int KNSCore::Entry::downloadLinkCount() const
350{
351 return d->mDownloadLinkInformationList.size();
352}
353
354QList<KNSCore::Entry::DownloadLinkInformation> KNSCore::Entry::downloadLinkInformationList() const
355{
356 const auto infos = d->mDownloadLinkInformationList;
357 QList<KNSCore::Entry::DownloadLinkInformation> ret;
358 ret.reserve(asize: infos.size());
359 for (const auto &info : infos) {
360 ret.append(t: {.name = info.name,
361 .priceAmount = info.priceAmount,
362 .distributionType = info.distributionType,
363 .descriptionLink = info.descriptionLink,
364 .id = info.id,
365 .isDownloadtypeLink = info.isDownloadtypeLink,
366 .size = info.size,
367 .tags = info.tags});
368 }
369 return ret;
370}
371
372void KNSCore::Entry::appendDownloadLinkInformation(const KNSCore::Entry::DownloadLinkInformation &info)
373{
374 d->mDownloadLinkInformationList.append(t: {.name = info.name,
375 .priceAmount = info.priceAmount,
376 .distributionType = info.distributionType,
377 .descriptionLink = info.descriptionLink,
378 .id = info.id,
379 .isDownloadtypeLink = info.isDownloadtypeLink,
380 .size = info.size,
381 .tags = info.tags,
382 .version = QString()});
383}
384
385void Entry::clearDownloadLinkInformation()
386{
387 d->mDownloadLinkInformationList.clear();
388}
389
390static QXmlStreamReader::TokenType readNextSkipComments(QXmlStreamReader *xml)
391{
392 do {
393 xml->readNext();
394 } while (xml->tokenType() == QXmlStreamReader::Comment || (xml->tokenType() == QXmlStreamReader::Characters && xml->text().trimmed().isEmpty()));
395 return xml->tokenType();
396}
397
398static QString readText(QXmlStreamReader *xml)
399{
400 Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
401 QString ret;
402 const auto token = readNextSkipComments(xml);
403 if (token == QXmlStreamReader::Characters) {
404 ret = xml->text().toString();
405 }
406 return ret;
407}
408
409static QString readStringTrimmed(QXmlStreamReader *xml)
410{
411 Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
412 QString ret = readText(xml).trimmed();
413
414 if (xml->tokenType() == QXmlStreamReader::Characters) {
415 readNextSkipComments(xml);
416 }
417 Q_ASSERT(xml->tokenType() == QXmlStreamReader::EndElement);
418 return ret;
419}
420
421static int readInt(QXmlStreamReader *xml)
422{
423 Q_ASSERT(xml->tokenType() == QXmlStreamReader::StartElement);
424 int ret = readText(xml).toInt();
425
426 xml->readNext();
427 Q_ASSERT(xml->tokenType() == QXmlStreamReader::EndElement);
428 return ret;
429}
430
431bool KNSCore::Entry::setEntryXML(QXmlStreamReader &reader)
432{
433 if (reader.name() != QLatin1String("stuff")) {
434 qCWarning(KNEWSTUFFCORE) << "Parsing Entry from invalid XML. Reader tag name was expected to be \"stuff\", but was found as:" << reader.name();
435 return false;
436 }
437
438 d->mCategory = reader.attributes().value(QStringLiteral("category")).toString();
439
440 while (!reader.atEnd()) {
441 const auto token = readNextSkipComments(xml: &reader);
442 if (token == QXmlStreamReader::EndElement) {
443 break;
444 } else if (token != QXmlStreamReader::StartElement) {
445 continue;
446 }
447
448 if (reader.name() == QLatin1String("name")) {
449 // TODO maybe do something with the language attribute? QString lang = e.attribute("lang");
450 d->mName = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
451 } else if (reader.name() == QLatin1String("author")) {
452 // ### careful, the following variables are string views that become invalid when we
453 // proceed with reading from reader, such as the readStringTrimmed call below does!
454 const auto email = reader.attributes().value(QStringLiteral("email"));
455 const auto jabber = reader.attributes().value(QStringLiteral("im"));
456 const auto homepage = reader.attributes().value(QStringLiteral("homepage"));
457 d->mAuthor.setEmail(email.toString());
458 d->mAuthor.setJabber(jabber.toString());
459 d->mAuthor.setHomepage(homepage.toString());
460 d->mAuthor.setName(readStringTrimmed(xml: &reader));
461 } else if (reader.name() == QLatin1String("providerid")) {
462 d->mProviderId = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
463 } else if (reader.name() == QLatin1String("homepage")) {
464 d->mHomepage = QUrl(reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements));
465 } else if (reader.name() == QLatin1String("licence")) { // krazy:exclude=spelling
466 d->mLicense = readStringTrimmed(xml: &reader);
467 } else if (reader.name() == QLatin1String("summary")) {
468 d->mSummary = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
469 } else if (reader.name() == QLatin1String("changelog")) {
470 d->mChangelog = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
471 } else if (reader.name() == QLatin1String("version")) {
472 d->mVersion = readStringTrimmed(xml: &reader);
473 } else if (reader.name() == QLatin1String("releasedate")) {
474 d->mReleaseDate = QDate::fromString(string: readStringTrimmed(xml: &reader), format: Qt::ISODate);
475 } else if (reader.name() == QLatin1String("preview")) {
476 // TODO support for all 6 image links
477 d->mPreviewUrl[PreviewSmall1] = readStringTrimmed(xml: &reader);
478 } else if (reader.name() == QLatin1String("previewBig")) {
479 d->mPreviewUrl[PreviewBig1] = readStringTrimmed(xml: &reader);
480 } else if (reader.name() == QLatin1String("payload")) {
481 d->mPayload = readStringTrimmed(xml: &reader);
482 } else if (reader.name() == QLatin1String("rating")) {
483 d->mRating = readInt(xml: &reader);
484 } else if (reader.name() == QLatin1String("downloads")) {
485 d->mDownloadCount = readInt(xml: &reader);
486 } else if (reader.name() == QLatin1String("category")) {
487 d->mCategory = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
488 } else if (reader.name() == QLatin1String("signature")) {
489 d->mSignature = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
490 } else if (reader.name() == QLatin1String("checksum")) {
491 d->mChecksum = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
492 } else if (reader.name() == QLatin1String("installedfile")) {
493 d->mInstalledFiles.append(t: reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements));
494 } else if (reader.name() == QLatin1String("id")) {
495 d->mUniqueId = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements);
496 } else if (reader.name() == QLatin1String("tags")) {
497 d->mTags = reader.readElementText(behaviour: QXmlStreamReader::SkipChildElements).split(sep: QLatin1Char(','));
498 } else if (reader.name() == QLatin1String("status")) {
499 const auto statusText = readText(xml: &reader);
500 if (statusText == QLatin1String("installed")) {
501 qCDebug(KNEWSTUFFCORE) << "Found an installed entry in registry";
502 d->mStatus = KNSCore::Entry::Installed;
503 } else if (statusText == QLatin1String("updateable")) {
504 d->mStatus = KNSCore::Entry::Updateable;
505 }
506 if (reader.tokenType() == QXmlStreamReader::Characters) {
507 readNextSkipComments(xml: &reader);
508 }
509 }
510 Q_ASSERT_X(reader.tokenType() == QXmlStreamReader::EndElement,
511 Q_FUNC_INFO,
512 QStringLiteral("token name was %1 and the type was %2").arg(reader.name().toString(), reader.tokenString()).toLocal8Bit().data());
513 }
514
515 // Validation
516 if (d->mName.isEmpty()) {
517 qCWarning(KNEWSTUFFCORE) << "Entry: no name given";
518 return false;
519 }
520
521 if (d->mUniqueId.isEmpty()) {
522 if (!d->mPayload.isEmpty()) {
523 d->mUniqueId = d->mPayload;
524 } else {
525 d->mUniqueId = d->mName;
526 }
527 }
528
529 if (d->mPayload.isEmpty()) {
530 qCWarning(KNEWSTUFFCORE) << "Entry: no payload URL given for: " << d->mName << " - " << d->mUniqueId;
531 return false;
532 }
533 return true;
534}
535
536bool KNSCore::Entry::setEntryXML(const QDomElement &xmldata)
537{
538 if (xmldata.tagName() != QLatin1String("stuff")) {
539 qCWarning(KNEWSTUFFCORE) << "Parsing Entry from invalid XML";
540 return false;
541 }
542
543 d->mCategory = xmldata.attribute(QStringLiteral("category"));
544
545 QDomNode n;
546 for (n = xmldata.firstChild(); !n.isNull(); n = n.nextSibling()) {
547 QDomElement e = n.toElement();
548 if (e.tagName() == QLatin1String("name")) {
549 // TODO maybe do something with the language attribute? QString lang = e.attribute("lang");
550 d->mName = e.text().trimmed();
551 } else if (e.tagName() == QLatin1String("author")) {
552 QString email = e.attribute(QStringLiteral("email"));
553 QString jabber = e.attribute(QStringLiteral("im"));
554 QString homepage = e.attribute(QStringLiteral("homepage"));
555 d->mAuthor.setName(e.text().trimmed());
556 d->mAuthor.setEmail(email);
557 d->mAuthor.setJabber(jabber);
558 d->mAuthor.setHomepage(homepage);
559 } else if (e.tagName() == QLatin1String("providerid")) {
560 d->mProviderId = e.text();
561 } else if (e.tagName() == QLatin1String("homepage")) {
562 d->mHomepage = QUrl(e.text());
563 } else if (e.tagName() == QLatin1String("licence")) { // krazy:exclude=spelling
564 d->mLicense = e.text().trimmed();
565 } else if (e.tagName() == QLatin1String("summary")) {
566 d->mSummary = e.text();
567 } else if (e.tagName() == QLatin1String("changelog")) {
568 d->mChangelog = e.text();
569 } else if (e.tagName() == QLatin1String("version")) {
570 d->mVersion = e.text().trimmed();
571 } else if (e.tagName() == QLatin1String("releasedate")) {
572 d->mReleaseDate = QDate::fromString(string: e.text().trimmed(), format: Qt::ISODate);
573 } else if (e.tagName() == QLatin1String("preview")) {
574 // TODO support for all 6 image links
575 d->mPreviewUrl[PreviewSmall1] = e.text().trimmed();
576 } else if (e.tagName() == QLatin1String("previewBig")) {
577 d->mPreviewUrl[PreviewBig1] = e.text().trimmed();
578 } else if (e.tagName() == QLatin1String("payload")) {
579 d->mPayload = e.text().trimmed();
580 } else if (e.tagName() == QLatin1String("rating")) {
581 d->mRating = e.text().toInt();
582 } else if (e.tagName() == QLatin1String("downloads")) {
583 d->mDownloadCount = e.text().toInt();
584 } else if (e.tagName() == QLatin1String("category")) {
585 d->mCategory = e.text();
586 } else if (e.tagName() == QLatin1String("signature")) {
587 d->mSignature = e.text();
588 } else if (e.tagName() == QLatin1String("checksum")) {
589 d->mChecksum = e.text();
590 } else if (e.tagName() == QLatin1String("installedfile")) {
591 // TODO KF6 Add a "installeddirectory" entry to avoid
592 // all the issues with the "/*" notation which is currently used as a workaround
593 d->mInstalledFiles.append(t: e.text());
594 } else if (e.tagName() == QLatin1String("id")) {
595 d->mUniqueId = e.text();
596 } else if (e.tagName() == QLatin1String("tags")) {
597 d->mTags = e.text().split(sep: QLatin1Char(','));
598 } else if (e.tagName() == QLatin1String("status")) {
599 QString statusText = e.text();
600 if (statusText == QLatin1String("installed")) {
601 qCDebug(KNEWSTUFFCORE) << "Found an installed entry in registry";
602 d->mStatus = KNSCore::Entry::Installed;
603 } else if (statusText == QLatin1String("updateable")) {
604 d->mStatus = KNSCore::Entry::Updateable;
605 }
606 }
607 }
608
609 // Validation
610 if (d->mName.isEmpty()) {
611 qCWarning(KNEWSTUFFCORE) << "Entry: no name given";
612 return false;
613 }
614
615 if (d->mUniqueId.isEmpty()) {
616 if (!d->mPayload.isEmpty()) {
617 d->mUniqueId = d->mPayload;
618 } else {
619 d->mUniqueId = d->mName;
620 }
621 }
622
623 if (d->mPayload.isEmpty()) {
624 qCWarning(KNEWSTUFFCORE) << "Entry: no payload URL given for: " << d->mName << " - " << d->mUniqueId;
625 return false;
626 }
627 return true;
628}
629
630/**
631 * get the xml string for the entry
632 */
633QDomElement KNSCore::Entry::entryXML() const
634{
635 Q_ASSERT(!d->mUniqueId.isEmpty());
636 Q_ASSERT(!d->mProviderId.isEmpty());
637
638 QDomDocument doc;
639
640 QDomElement el = doc.createElement(QStringLiteral("stuff"));
641 el.setAttribute(QStringLiteral("category"), value: d->mCategory);
642
643 QString name = d->mName;
644
645 QDomElement e;
646 e = addElement(doc, parent&: el, QStringLiteral("name"), value: name);
647 // todo: add language attribute
648 (void)addElement(doc, parent&: el, QStringLiteral("providerid"), value: d->mProviderId);
649
650 QDomElement author = addElement(doc, parent&: el, QStringLiteral("author"), value: d->mAuthor.name());
651 if (!d->mAuthor.email().isEmpty()) {
652 author.setAttribute(QStringLiteral("email"), value: d->mAuthor.email());
653 }
654 if (!d->mAuthor.homepage().isEmpty()) {
655 author.setAttribute(QStringLiteral("homepage"), value: d->mAuthor.homepage());
656 }
657 if (!d->mAuthor.jabber().isEmpty()) {
658 author.setAttribute(QStringLiteral("im"), value: d->mAuthor.jabber());
659 }
660 // FIXME: 'jabber' or 'im'? consult with kopete guys...
661 addElement(doc, parent&: el, QStringLiteral("homepage"), value: d->mHomepage.url());
662 (void)addElement(doc, parent&: el, QStringLiteral("licence"), value: d->mLicense); // krazy:exclude=spelling
663 (void)addElement(doc, parent&: el, QStringLiteral("version"), value: d->mVersion);
664 if ((d->mRating > 0) || (d->mDownloadCount > 0)) {
665 (void)addElement(doc, parent&: el, QStringLiteral("rating"), value: QString::number(d->mRating));
666 (void)addElement(doc, parent&: el, QStringLiteral("downloads"), value: QString::number(d->mDownloadCount));
667 }
668 if (!d->mSignature.isEmpty()) {
669 (void)addElement(doc, parent&: el, QStringLiteral("signature"), value: d->mSignature);
670 }
671 if (!d->mChecksum.isEmpty()) {
672 (void)addElement(doc, parent&: el, QStringLiteral("checksum"), value: d->mChecksum);
673 }
674 for (const QString &file : std::as_const(t&: d->mInstalledFiles)) {
675 (void)addElement(doc, parent&: el, QStringLiteral("installedfile"), value: file);
676 }
677 if (!d->mUniqueId.isEmpty()) {
678 addElement(doc, parent&: el, QStringLiteral("id"), value: d->mUniqueId);
679 }
680
681 (void)addElement(doc, parent&: el, QStringLiteral("releasedate"), value: d->mReleaseDate.toString(format: Qt::ISODate));
682
683 e = addElement(doc, parent&: el, QStringLiteral("summary"), value: d->mSummary);
684 e = addElement(doc, parent&: el, QStringLiteral("changelog"), value: d->mChangelog);
685 e = addElement(doc, parent&: el, QStringLiteral("preview"), value: d->mPreviewUrl[PreviewSmall1]);
686 e = addElement(doc, parent&: el, QStringLiteral("previewBig"), value: d->mPreviewUrl[PreviewBig1]);
687 e = addElement(doc, parent&: el, QStringLiteral("payload"), value: d->mPayload);
688 e = addElement(doc, parent&: el, QStringLiteral("tags"), value: d->mTags.join(sep: QLatin1Char(',')));
689
690 if (d->mStatus == KNSCore::Entry::Installed) {
691 (void)addElement(doc, parent&: el, QStringLiteral("status"), QStringLiteral("installed"));
692 }
693 if (d->mStatus == KNSCore::Entry::Updateable) {
694 (void)addElement(doc, parent&: el, QStringLiteral("status"), QStringLiteral("updateable"));
695 }
696
697 return el;
698}
699
700void KNSCore::Entry::setEntryDeleted()
701{
702 setStatus(Entry::Deleted);
703 d->mUnInstalledFiles = installedFiles();
704 setInstalledFiles(QStringList());
705}
706
707void KNSCore::Entry::setEntryRequestedId(const QString &id)
708{
709 d->mRequestedUniqueId = id;
710}
711
712QString KNSCore::replaceBBCode(const QString &unformattedText)
713{
714 QString text(unformattedText);
715 text.replace(before: QLatin1String("[b]"), after: QLatin1String("<b>"));
716 text.replace(before: QLatin1String("[/b]"), after: QLatin1String("</b>"));
717 text.replace(before: QLatin1String("[i]"), after: QLatin1String("<i>"));
718 text.replace(before: QLatin1String("[/i]"), after: QLatin1String("</i>"));
719 text.replace(before: QLatin1String("[u]"), after: QLatin1String("<i>"));
720 text.replace(before: QLatin1String("[/u]"), after: QLatin1String("</i>"));
721 text.replace(before: QLatin1String("\\\""), after: QLatin1String("\""));
722 text.replace(before: QLatin1String("\\\'"), after: QLatin1String("\'"));
723 text.replace(before: QLatin1String("[li]"), after: QLatin1String("* ")); // TODO: better replacement for list elements?
724 text.remove(QStringLiteral("[/li]"));
725 text.remove(QStringLiteral("[url]"));
726 text.remove(QStringLiteral("[/url]"));
727 return text;
728}
729
730QDebug KNSCore::operator<<(QDebug debug, const KNSCore::Entry &entry)
731{
732 QDebugStateSaver saver(debug);
733
734 const static QMetaEnum metaEnum = QMetaEnum::fromType<KNSCore::Entry::Status>();
735 bool deleted = entry.status() == Entry::Status::Deleted;
736
737 debug.nospace() << "KNSCore::Entry(uniqueId: " << entry.uniqueId() << ", name:" << entry.name() << ", status: " << metaEnum.valueToKey(value: entry.status())
738 << ", " << (deleted ? "uninstalled" : "installed") << "Files: " // When the entry is installed, it can not have uninstalledFiles
739 << (deleted ? entry.uninstalledFiles() : entry.installedFiles()) << ')';
740 return debug;
741}
742
743#include "moc_entry.cpp"
744

source code of knewstuff/src/core/entry.cpp