File indexing completed on 2023-09-24 09:41:12
0001 /* 0002 * SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> 0003 * SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <KAuthorized> 0009 #include <KCModuleData> 0010 #include <KFileUtils> 0011 #include <KPluginFactory> 0012 #include <KPluginMetaData> 0013 #include <QGuiApplication> 0014 #include <QStandardPaths> 0015 0016 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0017 #include <KServiceTypeTrader> 0018 #endif 0019 #include <kservice.h> 0020 #include <set> 0021 0022 enum MetaDataSource { 0023 SystemSettings = 1, 0024 KInfoCenter = 2, 0025 All = SystemSettings | KInfoCenter, 0026 }; 0027 0028 inline QList<KPluginMetaData> findExternalKCMModules(MetaDataSource source) 0029 { 0030 const auto findExternalModulesInFilesystem = [](const QString &sourceNamespace) { 0031 const QString sourceNamespaceDirName = QStringLiteral("plasma/%1/externalmodules").arg(sourceNamespace); 0032 const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, sourceNamespaceDirName, QStandardPaths::LocateDirectory); 0033 const QStringList files = KFileUtils::findAllUniqueFiles(dirs, QStringList{QStringLiteral("*.desktop")}); 0034 0035 QList<KPluginMetaData> metaDataList; 0036 for (const QString &file : files) { 0037 KService service(file); 0038 QJsonObject kplugin; 0039 kplugin.insert(QLatin1String("Name"), service.name()); 0040 kplugin.insert(QLatin1String("Icon"), service.icon()); 0041 kplugin.insert(QLatin1String("Description"), service.comment()); 0042 0043 QJsonObject root; 0044 root.insert(QLatin1String("KPlugin"), kplugin); 0045 root.insert(QLatin1String("X-KDE-Weight"), service.property(QStringLiteral("X-KDE-Weight")).toInt()); 0046 root.insert(QLatin1String("X-KDE-KInfoCenter-Category"), service.property(QStringLiteral("X-KDE-KInfoCenter-Category")).toString()); 0047 root.insert(QLatin1String("X-KDE-System-Settings-Category"), service.property(QStringLiteral("X-KDE-System-Settings-Category")).toString()); 0048 root.insert(QLatin1String("IsExternalApp"), true); 0049 0050 metaDataList << KPluginMetaData(root, file); 0051 } 0052 return metaDataList; 0053 }; 0054 0055 QList<KPluginMetaData> metaDataList; 0056 if (source & SystemSettings) { 0057 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0058 const auto servicesList = KServiceTypeTrader::self()->query(QStringLiteral("SystemSettingsExternalApp")); 0059 for (const auto &s : servicesList) { 0060 const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + s->entryPath()); 0061 metaDataList << KPluginMetaData::fromDesktopFile(path); 0062 } 0063 #endif 0064 metaDataList << findExternalModulesInFilesystem(QStringLiteral("systemsettings")); 0065 } 0066 0067 if (source & KInfoCenter) { 0068 metaDataList << findExternalModulesInFilesystem(QStringLiteral("kinfocenter")); 0069 } 0070 0071 return metaDataList; 0072 } 0073 0074 inline QList<KPluginMetaData> findKCMsMetaData(MetaDataSource source, bool useSystemsettingsConstraint = true) 0075 { 0076 QList<KPluginMetaData> modules; 0077 std::set<QString> uniquePluginIds; 0078 0079 auto filter = [](const KPluginMetaData &data) { 0080 const auto supportedPlatforms = data.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList()); 0081 return supportedPlatforms.isEmpty() || supportedPlatforms.contains(qGuiApp->platformName()); 0082 }; 0083 0084 // We need the exist calls because otherwise the trader language aborts if the property doesn't exist and the second part of the or is not evaluated 0085 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0086 KService::List services; 0087 #endif 0088 QVector<KPluginMetaData> metaDataList = KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms"), filter); 0089 if (source & SystemSettings) { 0090 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/systemsettings"), filter); 0091 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/systemsettings_qwidgets"), filter); 0092 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0093 services += 0094 KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), 0095 useSystemsettingsConstraint ? QStringLiteral("[X-KDE-System-Settings-Parent-Category] != ''") : QString()); 0096 #endif 0097 } 0098 if (source & KInfoCenter) { 0099 metaDataList << KPluginMetaData::findPlugins(QStringLiteral("plasma/kcms/kinfocenter"), filter); 0100 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0101 services += KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QStringLiteral("[X-KDE-ParentApp] == 'kinfocenter'")); 0102 #endif 0103 } 0104 for (const auto &m : qAsConst(metaDataList)) { 0105 // We check both since porting a module to loading view KPluginMetaData drops ".desktop" from the pluginId() 0106 if (!KAuthorized::authorizeControlModule(m.pluginId()) || !KAuthorized::authorizeControlModule(m.pluginId().append(QStringLiteral(".desktop")))) { 0107 continue; 0108 } 0109 modules << m; 0110 const bool inserted = uniquePluginIds.insert(m.pluginId()).second; 0111 if (!inserted) { 0112 qWarning() << "the plugin" << m.pluginId() << " was found in multiple namespaces"; 0113 } 0114 } 0115 0116 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0117 for (const auto &s : qAsConst(services)) { 0118 if (!s->noDisplay() && !s->exec().isEmpty() && KAuthorized::authorizeControlModule(s->menuId())) { 0119 const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + s->entryPath()); 0120 const KPluginMetaData data = KPluginMetaData::fromDesktopFile(path); 0121 const bool inserted = uniquePluginIds.insert(data.pluginId()).second; 0122 if (inserted) { 0123 modules << data; 0124 } 0125 } 0126 } 0127 #endif 0128 std::stable_sort(modules.begin(), modules.end(), [](const KPluginMetaData &m1, const KPluginMetaData &m2) { 0129 return QString::compare(m1.pluginId(), m2.pluginId(), Qt::CaseInsensitive) < 0; 0130 }); 0131 return modules; 0132 } 0133 0134 inline bool isKinfoCenterKcm(const KPluginMetaData &data) 0135 { 0136 // KServiceTypeTrader compat 0137 if (data.value(QStringLiteral("X-KDE-ParentApp")) == QLatin1String("kinfocenter")) { 0138 return true; 0139 } 0140 // external module or a KCM in the namespace 0141 if (data.fileName().contains(QLatin1String("/kinfocenter/"))) { 0142 return true; 0143 } 0144 return false; 0145 } 0146 0147 inline KCModuleData *loadModuleData(const KPluginMetaData &data) 0148 { 0149 if (!data.isValid()) { 0150 return nullptr; 0151 } 0152 KCModuleData *moduleData = nullptr; 0153 auto loadFromMetaData = [&moduleData](const KPluginMetaData &data) { 0154 if (data.isValid()) { 0155 auto factory = KPluginFactory::loadFactory(data).plugin; 0156 moduleData = factory ? factory->create<KCModuleData>() : nullptr; 0157 } 0158 }; 0159 loadFromMetaData(data); 0160 if (!moduleData) { 0161 loadFromMetaData(KPluginMetaData(QStringLiteral("kcms/") + data.fileName())); 0162 } 0163 return moduleData; 0164 }