File indexing completed on 2024-04-21 04:58:10

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "module_manager.h"
0008 #include <kdesktopfile.h>
0009 #include <kio/deletejob.h>
0010 #include <kconfig.h>
0011 #include <kconfiggroup.h>
0012 #include <QDir>
0013 #include <QUrl>
0014 #include <QStandardPaths>
0015 #include <KSharedConfig>
0016 
0017 #include "sidebar_debug.h"
0018 
0019 // Input data:
0020 // Global dir: list of desktop files.
0021 // Local dir: list of desktop files, for modules added or modified
0022 //      (When the user modifies something, we make a local copy)
0023 // Local KConfig file: config file with list (per profile) of "added" and "removed" desktop files
0024 
0025 // Algorithm:
0026 // Then we create buttons:
0027 // * desktop files from the most-global directory (pre-installed modules),
0028 //   skipping those listed as "removed"
0029 // * desktop files from other (more local) directories, if listed as "added" (for the current profile).
0030 //
0031 // (module not present globally and not "added" are either modified copies of
0032 // not-installed-anymore modules, or modules that were only added for another profile).
0033 
0034 ModuleManager::ModuleManager(KConfigGroup *config)
0035     : m_config(config)
0036 {
0037     m_localPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + relativeDataPath();
0038 }
0039 
0040 QStringList ModuleManager::modules() const
0041 {
0042     QStringList fileNames;
0043     const QStringList addedModules = m_config->readEntry("AddedModules", QStringList());
0044     const QStringList deletedModules = m_config->readEntry("DeletedModules", QStringList());
0045 
0046     const QStringList entries_dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, relativeDataPath(), QStandardPaths::LocateDirectory);
0047     if (entries_dirs.isEmpty()) { // Ooops
0048         qCWarning(SIDEBAR_LOG) << "No global directory found for" << relativeDataPath() << "Installation problem!";
0049         return QStringList();
0050     }
0051     // We only list the most-global dir. Other levels use AddedModules.
0052     QDir globalDir(entries_dirs.last());
0053     //qCDebug(SIDEBAR_LOG) << "Listing" << entries_dirs.last();
0054     const QStringList globalDirEntries = globalDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
0055     for (const QString &globalEntry: globalDirEntries) {
0056         //qCDebug(SIDEBAR_LOG) << " found" << globalEntry;
0057         if (!deletedModules.contains(globalEntry)) {
0058             fileNames.append(globalEntry);
0059         }
0060     }
0061     sortGlobalEntries(fileNames);
0062     //qCDebug(SIDEBAR_LOG) << "Adding local modules:" << addedModules;
0063     for (const QString &module: addedModules) {
0064         if (!fileNames.contains(module)) {
0065             fileNames.append(module);
0066         }
0067     }
0068 
0069     return fileNames;
0070 }
0071 
0072 QVector<KPluginMetaData> ModuleManager::availablePlugins() const
0073 {
0074     return KPluginMetaData::findPlugins(pluginDirectory());
0075 }
0076 
0077 QString ModuleManager::moduleDataPath(const QString &fileName) const
0078 {
0079     return relativeDataPath() + fileName;
0080 }
0081 
0082 QString ModuleManager::moduleFullPath(const QString &fileName) const
0083 {
0084     return QStandardPaths::locate(QStandardPaths::GenericDataLocation, moduleDataPath(fileName));
0085 }
0086 
0087 void ModuleManager::saveOpenViews(const QStringList &fileName)
0088 {
0089     // TODO: this would be best stored per-window, in the session file
0090 
0091     m_config->writeEntry("OpenViews", fileName);
0092     m_config->sync();
0093 }
0094 
0095 void ModuleManager::restoreDeletedButtons()
0096 {
0097     m_config->writeEntry("DeletedModules", QStringList());
0098     m_config->sync();
0099 }
0100 
0101 void ModuleManager::rollbackToDefault()
0102 {
0103     const QString loc = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/konqsidebartng/";
0104     QDir dir(loc);
0105     const QStringList dirEntries = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
0106     for (const QString &subdir: dirEntries) {
0107         if (subdir != "add") {
0108             qCDebug(SIDEBAR_LOG) << "Deleting" << (loc + subdir);
0109             KIO::Job *job = KIO::del(QUrl::fromLocalFile(loc + subdir), KIO::HideProgressInfo);
0110             job->exec();
0111         }
0112     }
0113     m_config->writeEntry("DeletedModules", QStringList());
0114     m_config->writeEntry("AddedModules", QStringList());
0115     m_config->sync();
0116 }
0117 
0118 void ModuleManager::setModuleName(const QString &fileName, const QString &moduleName)
0119 {
0120     // Write the name in the .desktop file of this button.
0121     KConfig desktopFile(m_localPath + fileName, KConfig::SimpleConfig);
0122     KConfigGroup ksc(&desktopFile, "Desktop Entry");
0123     ksc.writeEntry("Name", moduleName);
0124     ksc.writeEntry("Name", moduleName, KConfigBase::Persistent | KConfigBase::Localized);
0125     ksc.sync();
0126 }
0127 
0128 void ModuleManager::setModuleUrl(const QString &fileName, const QUrl &url)
0129 {
0130     KConfig desktopFile(m_localPath + fileName, KConfig::SimpleConfig);
0131     KConfigGroup ksc(&desktopFile, "Desktop Entry");
0132     ksc.writePathEntry("URL", url.toDisplayString());
0133     ksc.sync();
0134 }
0135 
0136 void ModuleManager::setModuleIcon(const QString &fileName, const QString &icon)
0137 {
0138     KConfig desktopFile(m_localPath + fileName, KConfig::SimpleConfig);
0139     KConfigGroup ksc(&desktopFile, "Desktop Entry");
0140     ksc.writePathEntry("Icon", icon);
0141     ksc.sync();
0142 }
0143 
0144 void ModuleManager::setShowHiddenFolders(const QString &fileName, const bool &newState)
0145 {
0146     KConfig desktopFile(m_localPath + fileName, KConfig::SimpleConfig);
0147     KConfigGroup ksc(&desktopFile, "Desktop Entry");
0148     ksc.writeEntry("ShowHiddenFolders", newState);
0149     ksc.sync();
0150 }
0151 
0152 int ModuleManager::getMaxKDEWeight()
0153 {
0154     int curMax = 1; // 0 is reserved for the treeModule
0155     for (const QString &fileName : modules()) {
0156         const QString path = moduleDataPath(fileName);
0157         if (! QStandardPaths::locate(QStandardPaths::GenericDataLocation, path).isEmpty()) {
0158             KSharedConfig::Ptr config = KSharedConfig::openConfig(path,
0159                                         KConfig::NoGlobals,
0160                                         QStandardPaths::GenericDataLocation);
0161             KConfigGroup configGroup(config, "Desktop Entry");
0162             const int weight = configGroup.readEntry("X-KDE-Weight", 0);
0163             if (curMax < weight) {
0164                 curMax = weight;
0165             }
0166         }
0167     }
0168     return curMax;
0169 }
0170 
0171 int ModuleManager::getNextAvailableKDEWeight()
0172 {
0173     return getMaxKDEWeight() + 1;
0174 }
0175 
0176 void ModuleManager::removeModule(const QString &fileName)
0177 {
0178     // Remove the local file (if it exists)
0179     QFile f(m_localPath + fileName);
0180     f.remove();
0181 
0182     // Mark module as deleted (so that we skip global file, if there's one)
0183     QStringList deletedModules = m_config->readEntry("DeletedModules", QStringList());
0184     QStringList addedModules = m_config->readEntry("AddedModules", QStringList());
0185     if ( !addedModules.contains(fileName) && !deletedModules.contains(fileName)) { // only add it to the "deletedModules" list if it is a global module
0186         deletedModules.append(fileName);
0187     }
0188 
0189     addedModules.removeAll(fileName);
0190     m_config->writeEntry("DeletedModules", deletedModules);
0191     m_config->writeEntry("AddedModules", addedModules);
0192     m_config->sync();
0193 }
0194 
0195 void ModuleManager::moduleAdded(const QString &fileName)
0196 {
0197     qCDebug(SIDEBAR_LOG) << fileName;
0198     QStringList deletedModules = m_config->readEntry("DeletedModules", QStringList());
0199     QStringList addedModules = m_config->readEntry("AddedModules", QStringList());
0200     if (!addedModules.contains(fileName)) {
0201         addedModules.append(fileName);
0202     }
0203     deletedModules.removeAll(fileName);
0204     m_config->writeEntry("DeletedModules", deletedModules);
0205     m_config->writeEntry("AddedModules", addedModules);
0206     m_config->sync();
0207 }
0208 
0209 QString ModuleManager::addModuleFromTemplate(QString &templ)
0210 {
0211     if (!templ.contains("%1")) {
0212         qCWarning(SIDEBAR_LOG) << "Template filename should contain %1";
0213     }
0214 
0215     QString filename = templ.arg(QString());
0216     QString myFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + moduleDataPath(filename);
0217 
0218     if (QFile::exists(myFile)) {
0219         for (ulong l = 1; l < ULONG_MAX; l++) {
0220             filename = templ.arg(l);
0221             myFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + moduleDataPath(filename);
0222             if (!QFile::exists(myFile)) {
0223                 break;
0224             } else {
0225                 filename.clear();
0226                 myFile.clear();
0227             }
0228         }
0229     }
0230     templ = filename;
0231     return myFile;
0232 }
0233 
0234 QStringList ModuleManager::localModulePaths(const QString &filter) const
0235 {
0236     return QDir(m_localPath).entryList(QStringList() << filter);
0237 }
0238 
0239 void ModuleManager::sortGlobalEntries(QStringList &fileNames) const
0240 {
0241     QMap<int, QString> sorter;
0242     for (const QString &fileName: fileNames) {
0243         const QString path = moduleDataPath(fileName);
0244         if (QStandardPaths::locate(QStandardPaths::GenericDataLocation, path).isEmpty()) {
0245             // doesn't exist anymore, skip it
0246             qCDebug(SIDEBAR_LOG) << "Skipping" << path;
0247         } else {
0248             KSharedConfig::Ptr config = KSharedConfig::openConfig(path,
0249                                         KConfig::NoGlobals,
0250                                         QStandardPaths::GenericDataLocation);
0251             KConfigGroup configGroup(config, "Desktop Entry");
0252             const int weight = configGroup.readEntry("X-KDE-Weight", 0);
0253             sorter.insert(weight, fileName);
0254 
0255             // While we have the config file open, fix migration issue between old and new history sidebar
0256             if (configGroup.readEntry("X-KDE-TreeModule") == "History") {
0257                 // Old local file still there; remove it
0258                 const QString localFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + path;
0259                 QFile::rename(localFile, localFile + ".orig");
0260                 qCDebug(SIDEBAR_LOG) << "Migration: moving" << localFile << "out of the way";
0261             }
0262         }
0263     }
0264     fileNames = sorter.values();
0265     qCDebug(SIDEBAR_LOG) << fileNames;
0266 }