File indexing completed on 2024-11-17 04:55:41
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "KNSResource.h" 0008 #include "KNSBackend.h" 0009 #include <KLocalizedString> 0010 #include <KNSCore/EngineBase> 0011 #include <KNSCore/Transaction> 0012 #include <KShell> 0013 #include <QProcess> 0014 #include <QRegularExpression> 0015 0016 #include "ReviewsBackend/Rating.h" 0017 #include <appstream/AppStreamUtils.h> 0018 #include <attica/provider.h> 0019 #include <utils.h> 0020 0021 KNSResource::KNSResource(const KNSCore::Entry &entry, QStringList categories, KNSBackend *parent) 0022 : AbstractResource(parent) 0023 , m_categories(std::move(categories)) 0024 , m_entry(entry) 0025 , m_lastStatus(entry.status()) 0026 { 0027 connect(this, &KNSResource::stateChanged, parent, &KNSBackend::updatesCountChanged); 0028 } 0029 0030 KNSResource::~KNSResource() = default; 0031 0032 AbstractResource::State KNSResource::state() 0033 { 0034 switch (m_entry.status()) { 0035 case KNSCore::Entry::Invalid: 0036 return Broken; 0037 case KNSCore::Entry::Downloadable: 0038 return None; 0039 case KNSCore::Entry::Installed: 0040 return Installed; 0041 case KNSCore::Entry::Updateable: 0042 return Upgradeable; 0043 case KNSCore::Entry::Deleted: 0044 case KNSCore::Entry::Installing: 0045 case KNSCore::Entry::Updating: 0046 return None; 0047 } 0048 return None; 0049 } 0050 0051 KNSBackend *KNSResource::knsBackend() const 0052 { 0053 return qobject_cast<KNSBackend *>(parent()); 0054 } 0055 0056 QVariant KNSResource::icon() const 0057 { 0058 const QString thumbnail = m_entry.previewUrl(KNSCore::Entry::PreviewSmall1); 0059 return thumbnail.isEmpty() ? knsBackend()->iconName() : m_entry.previewUrl(KNSCore::Entry::PreviewSmall1); 0060 } 0061 0062 QString KNSResource::comment() 0063 { 0064 QString ret = m_entry.shortSummary(); 0065 if (ret.isEmpty()) { 0066 ret = m_entry.summary(); 0067 int newLine = ret.indexOf(QLatin1Char('\n')); 0068 if (newLine > 0) { 0069 ret.truncate(newLine); 0070 } 0071 ret.remove(QRegularExpression(QStringLiteral("\\[\\/?[a-z]*\\]"))); 0072 ret.remove(QRegularExpression(QStringLiteral("<[^>]*>"))); 0073 } 0074 return ret; 0075 } 0076 0077 QString KNSResource::longDescription() 0078 { 0079 QString ret = m_entry.summary(); 0080 if (m_entry.shortSummary().isEmpty()) { 0081 const int newLine = ret.indexOf(QLatin1Char('\n')); 0082 if (newLine < 0) 0083 ret.clear(); 0084 else 0085 ret = ret.mid(newLine + 1).trimmed(); 0086 } 0087 ret.remove(QLatin1Char('\r')); 0088 ret.replace(QStringLiteral("[li]"), QStringLiteral("\n* ")); 0089 // Get rid of all BBCode markup we don't handle above 0090 ret.remove(QRegularExpression(QStringLiteral("\\[\\/?[a-z]*\\]"))); 0091 // Find anything that looks like a link (but which also is not some html 0092 // tag value or another already) and make it a link 0093 static const QRegularExpression urlRegExp( 0094 QStringLiteral("(^|\\s)(http[-a-zA-Z0-9@:%_\\+.~#?&//=]{2,256}\\.[a-z]{2,4}\\b(\\/[-a-zA-Z0-9@:;%_\\+.~#?&//=]*)?)"), 0095 QRegularExpression::CaseInsensitiveOption); 0096 ret.replace(urlRegExp, QStringLiteral("<a href=\"\\2\">\\2</a>")); 0097 return ret; 0098 } 0099 0100 QString KNSResource::name() const 0101 { 0102 return m_entry.name(); 0103 } 0104 0105 QString KNSResource::packageName() const 0106 { 0107 return m_entry.uniqueId(); 0108 } 0109 0110 QStringList KNSResource::categories() 0111 { 0112 return m_categories; 0113 } 0114 0115 QUrl KNSResource::homepage() 0116 { 0117 return m_entry.homepage(); 0118 } 0119 0120 void KNSResource::setEntry(const KNSCore::Entry &entry) 0121 { 0122 const bool diff = entry.status() != m_lastStatus; 0123 m_entry = entry; 0124 if (diff) { 0125 m_lastStatus = entry.status(); 0126 Q_EMIT stateChanged(); 0127 } 0128 } 0129 0130 KNSCore::Entry KNSResource::entry() const 0131 { 0132 return m_entry; 0133 } 0134 0135 QJsonArray KNSResource::licenses() 0136 { 0137 return {{AppStreamUtils::license(m_entry.license())}}; 0138 } 0139 0140 quint64 KNSResource::size() 0141 { 0142 const auto downloadInfo = m_entry.downloadLinkInformationList(); 0143 return downloadInfo.isEmpty() ? 0 : downloadInfo.at(0).size * 1024; 0144 } 0145 0146 QString KNSResource::installedVersion() const 0147 { 0148 return !m_entry.version().isEmpty() ? m_entry.version() : m_entry.releaseDate().toString(); 0149 } 0150 0151 QString KNSResource::availableVersion() const 0152 { 0153 return !m_entry.updateVersion().isEmpty() ? m_entry.updateVersion() 0154 : !m_entry.updateReleaseDate().isNull() ? m_entry.updateReleaseDate().toString() 0155 : !m_entry.version().isEmpty() ? m_entry.version() 0156 : releaseDate().toString(); 0157 } 0158 0159 QString KNSResource::origin() const 0160 { 0161 return m_entry.providerId(); 0162 } 0163 0164 QString KNSResource::displayOrigin() const 0165 { 0166 if (auto providers = knsBackend()->engine()->atticaProviders(); !providers.isEmpty()) { 0167 auto provider = providers.constFirst(); 0168 if (provider->name() == QLatin1String("api.kde-look.org")) { 0169 return i18nc("The name of the KDE Store", "KDE Store"); 0170 } 0171 return providers.constFirst()->name(); 0172 } 0173 return QUrl(m_entry.providerId()).host(); 0174 } 0175 0176 QString KNSResource::section() 0177 { 0178 return m_entry.category(); 0179 } 0180 0181 static bool isAnimated(const QString &path) 0182 { 0183 static const QVector<QLatin1String> s_extensions = {QLatin1String(".gif"), QLatin1String(".apng"), QLatin1String(".webp"), QLatin1String(".avif")}; 0184 return kContains(s_extensions, [path](const QLatin1String &postfix) { 0185 return path.endsWith(postfix); 0186 }); 0187 } 0188 0189 static void appendIfValid(Screenshots &list, const QUrl &thumbnail, const QUrl &screenshot) 0190 { 0191 if (thumbnail.isEmpty() || screenshot.isEmpty()) { 0192 return; 0193 } 0194 list += {thumbnail, screenshot, isAnimated(thumbnail.path())}; 0195 } 0196 0197 void KNSResource::fetchScreenshots() 0198 { 0199 Screenshots ret; 0200 appendIfValid(ret, QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewSmall1)), QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewBig1))); 0201 appendIfValid(ret, QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewSmall2)), QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewBig2))); 0202 appendIfValid(ret, QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewSmall3)), QUrl(m_entry.previewUrl(KNSCore::Entry::PreviewBig3))); 0203 Q_EMIT screenshotsFetched(ret); 0204 } 0205 0206 void KNSResource::fetchChangelog() 0207 { 0208 Q_EMIT changelogFetched(m_entry.changelog()); 0209 } 0210 0211 QStringList KNSResource::extends() const 0212 { 0213 return knsBackend()->extends(); 0214 } 0215 0216 QUrl KNSResource::url() const 0217 { 0218 return QUrl(QStringLiteral("kns://") + knsBackend()->name() + QLatin1Char('/') + QUrl(m_entry.providerId()).host() + QLatin1Char('/') + m_entry.uniqueId()); 0219 } 0220 0221 bool KNSResource::canExecute() const 0222 { 0223 return knsBackend()->engine()->hasAdoptionCommand(); 0224 } 0225 0226 void KNSResource::invokeApplication() const 0227 { 0228 KNSCore::Transaction::adopt(knsBackend()->engine(), m_entry); 0229 } 0230 0231 QString KNSResource::executeLabel() const 0232 { 0233 return knsBackend()->engine()->useLabel(); 0234 } 0235 0236 QDate KNSResource::releaseDate() const 0237 { 0238 return m_entry.updateReleaseDate().isNull() ? m_entry.releaseDate() : m_entry.updateReleaseDate(); 0239 } 0240 0241 QVector<int> KNSResource::linkIds() const 0242 { 0243 QVector<int> ids; 0244 const auto linkInfo = m_entry.downloadLinkInformationList(); 0245 for (const auto &e : linkInfo) { 0246 if (e.isDownloadtypeLink) 0247 ids << e.id; 0248 } 0249 return ids; 0250 } 0251 0252 QUrl KNSResource::donationURL() 0253 { 0254 return QUrl(m_entry.donationLink()); 0255 } 0256 0257 Rating *KNSResource::ratingInstance() 0258 { 0259 if (!m_rating) { 0260 const int noc = m_entry.numberOfComments(); 0261 const int rating = m_entry.rating(); 0262 Q_ASSERT(rating <= 100); 0263 m_rating.reset(new Rating(packageName(), noc, rating / 10)); 0264 } 0265 return m_rating.data(); 0266 } 0267 0268 QString KNSResource::author() const 0269 { 0270 return m_entry.author().name(); 0271 }