File indexing completed on 2024-04-28 15:29:23

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "plugin.h"
0010 
0011 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 90)
0012 
0013 QT_WARNING_PUSH
0014 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0015 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0016 
0017 #include "part.h"
0018 
0019 #include <KConfigGroup>
0020 #include <KDesktopFile>
0021 #include <KLocalizedString>
0022 #include <KPluginFactory>
0023 #include <KPluginLoader>
0024 #include <KPluginMetaData>
0025 #include <KSharedConfig>
0026 #include <KXMLGUIFactory>
0027 #if !KPARTS_BUILD_DEPRECATED_SINCE(5, 77) || KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76)
0028 #include <KAboutData>
0029 #endif
0030 
0031 #include <QDir>
0032 #include <QFile>
0033 #include <QFileInfo>
0034 #include <QStandardPaths>
0035 
0036 using namespace KParts;
0037 
0038 class KParts::PluginPrivate
0039 {
0040 public:
0041     QString m_parentInstance;
0042     QString m_library; // filename of the library
0043 };
0044 
0045 Plugin::Plugin(QObject *parent)
0046     : QObject(parent)
0047     , d(new PluginPrivate())
0048 {
0049     // qDebug() << className();
0050 }
0051 
0052 Plugin::~Plugin() = default;
0053 
0054 QString Plugin::xmlFile() const
0055 {
0056     QString path = KXMLGUIClient::xmlFile();
0057 
0058     if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) {
0059         return path;
0060     }
0061 
0062     QString absPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->m_parentInstance + QLatin1Char('/') + path);
0063     Q_ASSERT(!absPath.isEmpty());
0064     return absPath;
0065 }
0066 
0067 QString Plugin::localXMLFile() const
0068 {
0069     QString path = KXMLGUIClient::xmlFile();
0070 
0071     if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) {
0072         return path;
0073     }
0074 
0075     QString absPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->m_parentInstance + QLatin1Char('/') + path;
0076     return absPath;
0077 }
0078 
0079 // static
0080 QList<Plugin::PluginInfo> Plugin::pluginInfos(const QString &componentName)
0081 {
0082     QList<PluginInfo> plugins;
0083 
0084     QMap<QString, QStringList> sortedPlugins;
0085 
0086     const QStringList dirs =
0087         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, componentName + QLatin1String("/kpartplugins"), QStandardPaths::LocateDirectory);
0088     for (const QString &dir : dirs) {
0089         const auto rcfiles = QDir(dir).entryList(QStringList(QStringLiteral("*.rc")));
0090         for (const QString &file : rcfiles) {
0091             const QFileInfo fInfo(dir + QLatin1Char('/') + file);
0092             QMap<QString, QStringList>::Iterator mapIt = sortedPlugins.find(fInfo.fileName());
0093             if (mapIt == sortedPlugins.end()) {
0094                 mapIt = sortedPlugins.insert(fInfo.fileName(), QStringList());
0095             }
0096             mapIt.value().append(fInfo.absoluteFilePath());
0097         }
0098     }
0099 
0100     QMap<QString, QStringList>::ConstIterator mapIt = sortedPlugins.constBegin();
0101     QMap<QString, QStringList>::ConstIterator mapEnd = sortedPlugins.constEnd();
0102     for (; mapIt != mapEnd; ++mapIt) {
0103         PluginInfo info;
0104         QString doc;
0105         info.m_absXMLFileName = KXMLGUIClient::findMostRecentXMLFile(mapIt.value(), doc);
0106         if (info.m_absXMLFileName.isEmpty()) {
0107             continue;
0108         }
0109 
0110         // qDebug() << "found KParts Plugin : " << info.m_absXMLFileName;
0111         info.m_relXMLFileName = QLatin1String("kpartplugins/") + mapIt.key();
0112 
0113         info.m_document.setContent(doc);
0114         if (info.m_document.documentElement().isNull()) {
0115             continue;
0116         }
0117 
0118         plugins.append(info);
0119     }
0120 
0121     return plugins;
0122 }
0123 
0124 void Plugin::loadPlugins(QObject *parent, const QString &componentName)
0125 {
0126     loadPlugins(parent, pluginInfos(componentName), componentName);
0127 }
0128 
0129 void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos, const QString &componentName)
0130 {
0131     for (const auto &pluginInfo : pluginInfos) {
0132         const QString library = pluginInfo.m_document.documentElement().attribute(QStringLiteral("library"));
0133 
0134         if (library.isEmpty() || hasPlugin(parent, library)) {
0135             continue;
0136         }
0137 
0138         Plugin *plugin = loadPlugin(parent, library, pluginInfo.m_document.documentElement().attribute(QStringLiteral("X-KDE-PluginKeyword")));
0139 
0140         if (plugin) {
0141             plugin->d->m_parentInstance = componentName;
0142             plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false);
0143             plugin->setDOMDocument(pluginInfo.m_document);
0144         }
0145     }
0146 }
0147 
0148 void Plugin::loadPlugins(QObject *parent, const QList<PluginInfo> &pluginInfos)
0149 {
0150     loadPlugins(parent, pluginInfos, QString());
0151 }
0152 
0153 // static
0154 Plugin *Plugin::loadPlugin(QObject *parent, const QString &libname, const QString &keyword)
0155 {
0156     KPluginLoader loader(libname);
0157     KPluginFactory *factory = loader.factory();
0158 
0159     if (!factory) {
0160         return nullptr;
0161     }
0162 
0163     Plugin *plugin = factory->create<Plugin>(keyword, parent);
0164     if (!plugin) {
0165         return nullptr;
0166     }
0167     plugin->d->m_library = libname;
0168     return plugin;
0169 }
0170 
0171 QList<KParts::Plugin *> Plugin::pluginObjects(QObject *parent)
0172 {
0173     QList<KParts::Plugin *> objects;
0174 
0175     if (!parent) {
0176         return objects;
0177     }
0178 
0179     objects = parent->findChildren<Plugin *>(QString(), Qt::FindDirectChildrenOnly);
0180     return objects;
0181 }
0182 
0183 bool Plugin::hasPlugin(QObject *parent, const QString &library)
0184 {
0185     const QObjectList plugins = parent->children();
0186 
0187     return std::any_of(plugins.begin(), plugins.end(), [&library](QObject *p) {
0188         Plugin *plugin = qobject_cast<Plugin *>(p);
0189         return (plugin && plugin->d->m_library == library);
0190     });
0191 }
0192 
0193 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 77)
0194 void Plugin::setComponentData(const KAboutData &pluginData)
0195 {
0196     // backward-compatible registration, usage deprecated
0197 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76)
0198     QT_WARNING_PUSH
0199     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0200     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0201     KAboutData::registerPluginData(pluginData);
0202     QT_WARNING_POP
0203 #endif
0204 
0205     KXMLGUIClient::setComponentName(pluginData.componentName(), pluginData.displayName());
0206 }
0207 #endif
0208 
0209 void Plugin::setMetaData(const KPluginMetaData &metaData)
0210 {
0211     // backward-compatible registration, usage deprecated
0212 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 76)
0213     QT_WARNING_PUSH
0214     QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0215     QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0216     KAboutData::registerPluginData(KAboutData::fromPluginMetaData(metaData));
0217     QT_WARNING_POP
0218 #endif
0219 
0220     KXMLGUIClient::setComponentName(metaData.pluginId(), metaData.name());
0221 }
0222 
0223 void Plugin::loadPlugins(QObject *parent,
0224                          KXMLGUIClient *parentGUIClient,
0225                          const QString &componentName,
0226                          bool enableNewPluginsByDefault,
0227                          int interfaceVersionRequired)
0228 {
0229     KConfigGroup cfgGroup(KSharedConfig::openConfig(componentName + QLatin1String("rc")), "KParts Plugins");
0230     const QList<PluginInfo> plugins = pluginInfos(componentName);
0231     for (const auto &pluginInfo : plugins) {
0232         QDomElement docElem = pluginInfo.m_document.documentElement();
0233         QString library = docElem.attribute(QStringLiteral("library"));
0234         QString keyword;
0235 
0236         if (library.isEmpty()) {
0237             continue;
0238         }
0239 
0240         // Check configuration
0241         const QString name = docElem.attribute(QStringLiteral("name"));
0242 
0243         bool pluginEnabled = enableNewPluginsByDefault;
0244         if (cfgGroup.hasKey(name + QLatin1String("Enabled"))) {
0245             pluginEnabled = cfgGroup.readEntry(name + QLatin1String("Enabled"), false);
0246         } else { // no user-setting, load plugin default setting
0247             QString relPath = componentName + QLatin1Char('/') + pluginInfo.m_relXMLFileName;
0248             relPath.truncate(relPath.lastIndexOf(QLatin1Char('.'))); // remove extension
0249             relPath += QLatin1String(".desktop");
0250             // qDebug() << "looking for " << relPath;
0251             const QString desktopfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relPath);
0252             if (!desktopfile.isEmpty()) {
0253                 // qDebug() << "loadPlugins found desktop file for " << name << ": " << desktopfile;
0254                 KDesktopFile _desktop(desktopfile);
0255                 const KConfigGroup desktop = _desktop.desktopGroup();
0256                 keyword = desktop.readEntry("X-KDE-PluginKeyword", "");
0257                 pluginEnabled = desktop.readEntry("X-KDE-PluginInfo-EnabledByDefault", enableNewPluginsByDefault);
0258                 if (interfaceVersionRequired != 0) {
0259                     const int version = desktop.readEntry("X-KDE-InterfaceVersion", 1);
0260                     if (version != interfaceVersionRequired) {
0261                         // qDebug() << "Discarding plugin " << name << ", interface version " << version << ", expected " << interfaceVersionRequired;
0262                         pluginEnabled = false;
0263                     }
0264                 }
0265             } else {
0266                 // qDebug() << "loadPlugins no desktop file found in " << relPath;
0267             }
0268         }
0269 
0270         // search through already present plugins
0271         const QObjectList pluginList = parent->children();
0272 
0273         bool pluginFound = false;
0274         for (auto *p : pluginList) {
0275             Plugin *plugin = qobject_cast<Plugin *>(p);
0276             if (plugin && plugin->d->m_library == library) {
0277                 // delete and unload disabled plugins
0278                 if (!pluginEnabled) {
0279                     // qDebug() << "remove plugin " << name;
0280                     KXMLGUIFactory *factory = plugin->factory();
0281                     if (factory) {
0282                         factory->removeClient(plugin);
0283                     }
0284                     delete plugin;
0285                 }
0286 
0287                 pluginFound = true;
0288                 break;
0289             }
0290         }
0291 
0292         // if the plugin is already loaded or if it's disabled in the
0293         // configuration do nothing
0294         if (pluginFound || !pluginEnabled) {
0295             continue;
0296         }
0297 
0298         // qDebug() << "load plugin " << name << " " << library << " " << keyword;
0299         Plugin *plugin = loadPlugin(parent, library, keyword);
0300 
0301         if (plugin) {
0302             plugin->d->m_parentInstance = componentName;
0303             plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false);
0304             plugin->setDOMDocument(pluginInfo.m_document);
0305             parentGUIClient->insertChildClient(plugin);
0306         }
0307     }
0308 }
0309 
0310 #include "moc_plugin.cpp"
0311 
0312 #endif