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