[frameworks/kcoreaddons] /: aboutData: Add support for AppStream URLs
Volker Krause <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 311502af5279922df2d84218289fce4160e66163 by Volker Krause.
Committed on 22/07/2026 at 15:38.
Pushed by vkrause into branch 'master'.
aboutData: Add support for AppStream URLs
This adds API to access URLs in the AppStream metadata. Rather than adding
individual properties for each URL type, this adds a generic API with an
enum key, making this easier to extend.
M +1 -0 autotests/data/org.kde.coreaddons.test-app.xml
M +3 -0 autotests/kaboutdatatest.cpp
M +52 -11 src/lib/kaboutdata.cpp
M +53 -0 src/lib/kaboutdata.h
M +6 -0 src/qml/types.h
https://invent.kde.org/frameworks/kcoreaddons/-/commit/311502af5279922df2d84218289fce4160e66163
diff --git a/autotests/data/org.kde.coreaddons.test-app.xml b/autotests/data/org.kde.coreaddons.test-app.xml
index b6029bee..973d97f0 100644
--- a/autotests/data/org.kde.coreaddons.test-app.xml
+++ b/autotests/data/org.kde.coreaddons.test-app.xml
@@ -60,6 +60,7 @@
</releases>
<custom>
<value key="KDE::matrix">#frameworks-devel:kde.org</value>
+ <value key="FOO::something_else">some value</value>
</custom>
<recommends>
<control>keyboard</control>
diff --git a/autotests/kaboutdatatest.cpp b/autotests/kaboutdatatest.cpp
index 359a8852..285f47ec 100644
--- a/autotests/kaboutdatatest.cpp
+++ b/autotests/kaboutdatatest.cpp
@@ -422,6 +422,9 @@ void KAboutDataTest::testAppStream()
QCOMPARE(aboutData.licenses()[0].name(), "LGPL v2"_L1);
QCOMPARE(aboutData.licenses()[0].spdx(), "LGPL-2.0+"_L1);
QCOMPARE(aboutData.homepage(), "https://apps.kde.org/coreaddons-test-app"_L1);
+ QCOMPARE(aboutData.url(KAboutData::Homepage), "https://apps.kde.org/coreaddons-test-app"_L1);
+ QCOMPARE(aboutData.url(KAboutData::VCSBrowser), "https://invent.kde.org/frameworks/kcoreaddons"_L1);
+ QCOMPARE(aboutData.url(KAboutData::XKDEMatrixRoom), "#frameworks-devel:kde.org"_L1);
QCOMPARE(aboutData.bugAddress(), "https://bugs.kde.org/enter_bug.cgi?format=guided&product=frameworks-coreaddons"_L1);
QCOMPARE(aboutData.displayName(), "Test App (untranslated)"_L1);
QCOMPARE(aboutData.shortDescription(), "Test App Description (untranslated)"_L1);
diff --git a/src/lib/kaboutdata.cpp b/src/lib/kaboutdata.cpp
index c3871e2a..52eef2db 100644
--- a/src/lib/kaboutdata.cpp
+++ b/src/lib/kaboutdata.cpp
@@ -603,7 +603,7 @@ public:
QString _shortDescription;
QString _copyrightStatement;
QString _otherText;
- QString _homepageAddress;
+ QHash<KAboutData::UrlType, QString> _urls;
QList<KAboutPerson> _authorList;
QList<KAboutPerson> _creditList;
QList<KAboutPerson> _translatorList;
@@ -653,7 +653,7 @@ KAboutData::KAboutData(const QString &_componentName,
d->_licenseList.append(KAboutLicense(licenseType, this));
d->_copyrightStatement = _copyrightStatement;
d->_otherText = text;
- d->_homepageAddress = homePageAddress;
+ d->_urls[KAboutData::Homepage] = homePageAddress;
d->_bugAddress = bugAddress.toUtf8();
QUrl homePageUrl(homePageAddress);
@@ -909,7 +909,7 @@ KAboutData &KAboutData::setOtherText(const QString &_otherText)
KAboutData &KAboutData::setHomepage(const QString &homepage)
{
- d->_homepageAddress = homepage;
+ d->_urls[KAboutData::Homepage] = homepage;
return *this;
}
@@ -993,7 +993,7 @@ QString KAboutData::shortDescription() const
QString KAboutData::homepage() const
{
- return d->_homepageAddress;
+ return d->_urls.value(KAboutData::Homepage);
}
QString KAboutData::bugAddress() const
@@ -1158,6 +1158,17 @@ QString KAboutData::desktopFileName() const
#endif
}
+KAboutData &KAboutData::setUrl(KAboutData::UrlType type, const QString &url)
+{
+ d->_urls[type] = url;
+ return *this;
+}
+
+QString KAboutData::url(KAboutData::UrlType type) const
+{
+ return d->_urls.value(type);
+}
+
KAboutData &KAboutData::addRelease(KAboutRelease &&release)
{
d->_releaseList.push_back(std::move(release));
@@ -1502,6 +1513,24 @@ struct AppDataDesc {
return l;
}
+struct {
+ QLatin1StringView name;
+ KAboutData::UrlType type;
+} static constexpr const url_type_map[] = {
+ {"homepage"_L1, KAboutData::Homepage},
+ {"bugtracker"_L1, KAboutData::Bugtracker},
+ {"faq"_L1, KAboutData::Faq},
+ {"help"_L1, KAboutData::Help},
+ {"donation"_L1, KAboutData::Donation},
+ {"translate"_L1, KAboutData::Translate},
+ {"contact"_L1, KAboutData::Contact},
+ {"vcs-browser"_L1, KAboutData::VCSBrowser},
+ {"contribute"_L1, KAboutData::Contribute},
+ {"KDE::matrix"_L1, KAboutData::XKDEMatrixRoom},
+ {"KDE::forum"_L1, KAboutData::XKDEForum},
+ {"KDE::mastodon"_L1, KAboutData::XKDEMastodon},
+};
+
KAboutData KAboutData::fromAppStreamFile(const QString &appStreamFileName)
{
KAboutData *aboutData = s_registry->m_appData;
@@ -1525,7 +1554,7 @@ KAboutData KAboutData::fromAppStreamFile(const QString &appStreamFileName)
continue;
}
- if (reader.name() == "component"_L1) {
+ if (reader.name() == "component"_L1 || reader.name() == "custom"_L1) {
// recurse into
} else if (reader.name() == "releases"_L1) {
aboutData->d->_releaseList.clear();
@@ -1551,12 +1580,15 @@ KAboutData KAboutData::fromAppStreamFile(const QString &appStreamFileName)
appSummary[lang] = reader.readElementText();
} else if (reader.name() == "url"_L1) {
const auto type = reader.attributes().value("type"_L1);
- if (type == "homepage"_L1) {
- aboutData->setHomepage(reader.readElementText());
- } else if (type == "bugtracker"_L1) {
- aboutData->setBugAddress(reader.readElementText().toUtf8());
- } else {
- reader.skipCurrentElement();
+ for (const auto &m : url_type_map) {
+ if (m.name != type) {
+ continue;
+ }
+ aboutData->setUrl(m.type, reader.readElementText());
+ if (m.name == "bugtracker"_L1) {
+ aboutData->setBugAddress(aboutData->url(KAboutData::Bugtracker).toUtf8());
+ }
+ break;
}
} else if (reader.name() == "release"_L1) {
const auto version = reader.attributes().value("version"_L1).toString();
@@ -1585,6 +1617,15 @@ KAboutData KAboutData::fromAppStreamFile(const QString &appStreamFileName)
if (!version.isEmpty() && !desc.desc.isEmpty()) {
aboutData->addRelease(KAboutRelease(version, date, desc.desc, desc.rawDesc, url));
}
+ } else if (reader.name() == "value"_L1) {
+ const auto key = reader.attributes().value("key"_L1);
+ for (const auto &m : url_type_map) {
+ if (m.name != key) {
+ continue;
+ }
+ aboutData->setUrl(m.type, reader.readElementText());
+ break;
+ }
} else {
reader.skipCurrentElement();
}
diff --git a/src/lib/kaboutdata.h b/src/lib/kaboutdata.h
index d8b818ac..9b1a640d 100644
--- a/src/lib/kaboutdata.h
+++ b/src/lib/kaboutdata.h
@@ -1635,6 +1635,59 @@ public:
**/
QString desktopFileName() const;
+ /*!
+ * Types of URLs associated with application metadata.
+ *
+ * This includes URL types defined in the AppStream specification
+ * as well as KDE extensions to that.
+ *
+ * \value Homepage The application homepage.
+ * \value Bugtracker The application's bug tracking system, for users to report issues.
+ * \value Faq A FAQ page for this application.
+ * \value Help User reference or documentation.
+ * \value Donation Information on how to donate to this project.
+ * \value Translate Page about how to contribute to translating the application.
+ * \value Contact Page with contact information of the vendor/developer.
+ * \value VCSBrowser Link to the Git forge or similar with the application source code.
+ * \value Contribute Information on how to contribute to this application.
+ *
+ * \value XKDEMatrixRoom Matrix room for this application (KDE::matrix AppStream extension).
+ * \value XKDEForum Web forum/forum section for this application (KDE::forum AppStream extension).
+ * \value XKDEMastodon Mastodon account about this applications (KDE::mastodon AppStream extension).
+ *
+ * \sa https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-url
+ * \since 6.29
+ */
+ enum UrlType {
+ Homepage,
+ Bugtracker,
+ Faq,
+ Help,
+ Donation,
+ Translate,
+ Contact,
+ VCSBrowser,
+ Contribute,
+ // KDE extensions
+ XKDEMatrixRoom,
+ XKDEForum,
+ XKDEMastodon,
+ // TODO: wiki: currently not read from AppStream by apps.kde.org
+ };
+ Q_ENUM(UrlType)
+
+ /*!
+ * Sets the URL of the given type.
+ * \since 6.29
+ */
+ KAboutData &setUrl(KAboutData::UrlType type, const QString &url);
+ /*!
+ * Returns the URL of the given type, if available.
+ * This is typically populated from AppStream application metadata.
+ * \since 6.29
+ */
+ Q_INVOKABLE [[nodiscard]] QString url(KAboutData::UrlType type) const;
+
/*!
* Adds a release note for this application.
* \sa releases()
diff --git a/src/qml/types.h b/src/qml/types.h
index 5a09eeff..d766b38a 100644
--- a/src/qml/types.h
+++ b/src/qml/types.h
@@ -22,4 +22,10 @@ struct KAboutReleaseForeign {
QML_FOREIGN(KAboutRelease)
};
+namespace KAboutDataForeign
+{
+Q_NAMESPACE
+QML_NAMED_ELEMENT(AboutUrlType)
+QML_FOREIGN_NAMESPACE(KAboutData)
+}
#endif