File indexing completed on 2024-10-06 13:22:22

0001 /*
0002 *  Copyright 2019  Michail Vourlakos <mvourlakos@gmail.com>
0003 *
0004 *  This file is part of Latte-Dock
0005 *
0006 *  Latte-Dock is free software; you can redistribute it and/or
0007 *  modify it under the terms of the GNU General Public License as
0008 *  published by the Free Software Foundation; either version 2 of
0009 *  the License, or (at your option) any later version.
0010 *
0011 *  Latte-Dock is distributed in the hope that it will be useful,
0012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 *  GNU General Public License for more details.
0015 *
0016 *  You should have received a copy of the GNU General Public License
0017 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "factory.h"
0021 
0022 // local
0023 #include "../layouts/importer.h"
0024 
0025 // Qt
0026 #include <QDebug>
0027 #include <QDir>
0028 #include <QDirIterator>
0029 #include <QMessageBox>
0030 #include <QProcess>
0031 #include <QTemporaryDir>
0032 
0033 // KDE
0034 #include <KDirWatch>
0035 #include <KLocalizedString>
0036 #include <KNotification>
0037 #include <KPluginMetaData>
0038 #include <KArchive/KTar>
0039 #include <KArchive/KZip>
0040 #include <KArchive/KArchiveEntry>
0041 #include <KArchive/KArchiveDirectory>
0042 #include <KNewStuff3/KNS3/DownloadDialog>
0043 
0044 namespace Latte {
0045 namespace Indicator {
0046 
0047 Factory::Factory(QObject *parent)
0048     : QObject(parent)
0049 {
0050     m_parentWidget = new QWidget();
0051 
0052     m_watchedPaths = Latte::Layouts::Importer::standardPaths();
0053 
0054     for(int i=0; i<m_watchedPaths.count(); ++i) {
0055         m_watchedPaths[i] = m_watchedPaths[i] + "/latte/indicators";
0056     }
0057 
0058     reload();
0059 
0060     //! track paths for changes
0061     for(const auto &dir : m_watchedPaths) {
0062         KDirWatch::self()->addDir(dir);
0063     }
0064 
0065     connect(KDirWatch::self(), &KDirWatch::dirty, this, [ & ](const QString & path) {
0066         if (m_watchedPaths.contains(path)) {
0067             reload();
0068             emit pluginsUpdated();
0069         }
0070     });
0071 
0072     qDebug() << m_plugins["org.kde.latte.default"].name();
0073 }
0074 
0075 Factory::~Factory()
0076 {
0077     m_parentWidget->deleteLater();
0078 }
0079 
0080 bool Factory::pluginExists(QString id) const
0081 {
0082     return m_plugins.contains(id);
0083 }
0084 
0085 int Factory::customPluginsCount()
0086 {
0087     return m_customPluginIds.count();
0088 }
0089 
0090 QStringList Factory::customPluginIds()
0091 {
0092     return m_customPluginIds;
0093 }
0094 
0095 QStringList Factory::customPluginNames()
0096 {
0097     return m_customPluginNames;
0098 }
0099 
0100 QStringList Factory::customLocalPluginIds()
0101 {
0102     return m_customLocalPluginIds;
0103 }
0104 
0105 KPluginMetaData Factory::metadata(QString pluginId)
0106 {
0107     if (m_plugins.contains(pluginId)) {
0108         return m_plugins[pluginId];
0109     }
0110 
0111     return KPluginMetaData();
0112 }
0113 
0114 void Factory::reload()
0115 {
0116     m_plugins.clear();
0117     m_pluginUiPaths.clear();
0118     m_customPluginIds.clear();
0119     m_customPluginNames.clear();
0120     m_customLocalPluginIds.clear();
0121 
0122     for(const auto &path : m_watchedPaths) {
0123         QDir standard(path);
0124 
0125         if (standard.exists()) {
0126             QStringList pluginDirs = standard.entryList(QStringList(),QDir::AllDirs | QDir::NoSymLinks);
0127 
0128             for (const auto &pluginDir : pluginDirs) {
0129                 if (pluginDir != "." && pluginDir != "..") {
0130                     QString metadataFile = standard.absolutePath() + "/" + pluginDir + "/metadata.desktop";
0131 
0132                     if(QFileInfo(metadataFile).exists()) {
0133                         KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile);
0134 
0135                         if (metadataAreValid(metadata)) {
0136                             QString uiFile = standard.absolutePath() + "/" + pluginDir + "/package/" + metadata.value("X-Latte-MainScript");
0137 
0138                             if (QFileInfo(uiFile).exists() && !m_plugins.contains(metadata.pluginId())) {
0139                                 m_plugins[metadata.pluginId()] = metadata;
0140 
0141                                 if ((metadata.pluginId() != "org.kde.latte.default")
0142                                         && (metadata.pluginId() != "org.kde.latte.plasma")) {
0143                                     m_customPluginIds << metadata.pluginId();
0144                                     m_customPluginNames << metadata.name();
0145                                 }
0146 
0147                                 if (standard.absolutePath().startsWith(QDir::homePath())) {
0148                                     m_customLocalPluginIds << metadata.pluginId();
0149                                 }
0150 
0151                                 m_pluginUiPaths[metadata.pluginId()] = QFileInfo(uiFile).absolutePath();
0152 
0153                                 QString pluginPath = metadata.fileName().remove("metadata.desktop");
0154                                 qDebug() << " Indicator Package Loaded ::: " << metadata.name() << " [" << metadata.pluginId() << "]" << " - [" <<pluginPath<<"]";
0155 
0156                                 /*qDebug() << " Indicator value ::: " << metadata.pluginId();
0157                             qDebug() << " Indicator value ::: " << metadata.fileName();
0158                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-MainScript");
0159                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-ConfigUi");
0160                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-ConfigXml");*/
0161                             }
0162                         }
0163                     }
0164                 }
0165             }
0166         }
0167     }
0168 
0169     emit customPluginsChanged();
0170 }
0171 
0172 bool Factory::metadataAreValid(KPluginMetaData &metadata)
0173 {
0174     return metadata.isValid()
0175             && metadata.category() == "Latte Indicator"
0176             && !metadata.value("X-Latte-MainScript").isEmpty();
0177 }
0178 
0179 bool Factory::metadataAreValid(QString &file)
0180 {
0181     if (QFileInfo(file).exists()) {
0182         KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(file);
0183         return metadata.isValid();
0184     }
0185 
0186     return false;
0187 }
0188 
0189 QString Factory::uiPath(QString pluginName) const
0190 {
0191     if (!m_pluginUiPaths.contains(pluginName)) {
0192         return "";
0193     }
0194 
0195     return m_pluginUiPaths[pluginName];
0196 }
0197 
0198 Latte::Types::ImportExportState Factory::importIndicatorFile(QString compressedFile)
0199 {
0200     auto showNotificationError = []() {
0201         auto notification = new KNotification("import-fail", KNotification::CloseOnTimeout);
0202         notification->setText(i18n("Failed to import indicator"));
0203         notification->sendEvent();
0204     };
0205 
0206     auto showNotificationSucceed = [](QString name, bool updated) {
0207         auto notification = new KNotification("import-done", KNotification::CloseOnTimeout);
0208         notification->setText(updated ? i18nc("indicator_name, imported updated","%0 indicator updated successfully").arg(name) :
0209                                         i18nc("indicator_name, imported success","%0 indicator installed successfully").arg(name));
0210         notification->sendEvent();
0211     };
0212 
0213     KArchive *archive;
0214 
0215     KZip *zipArchive = new KZip(compressedFile);
0216     zipArchive->open(QIODevice::ReadOnly);
0217 
0218     //! if the file isnt a zip archive
0219     if (!zipArchive->isOpen()) {
0220         delete zipArchive;
0221 
0222         KTar *tarArchive = new KTar(compressedFile, QStringLiteral("application/x-tar"));
0223         tarArchive->open(QIODevice::ReadOnly);
0224 
0225         if (!tarArchive->isOpen()) {
0226             delete tarArchive;
0227             showNotificationError();
0228             return Latte::Types::Failed;
0229         } else {
0230             archive = tarArchive;
0231         }
0232     } else {
0233         archive = zipArchive;
0234     }
0235 
0236     QTemporaryDir archiveTempDir;
0237     archive->directory()->copyTo(archiveTempDir.path());
0238 
0239     //metadata file
0240     QString packagePath = archiveTempDir.path();
0241     QString metadataFile = archiveTempDir.path() + "/metadata.desktop";
0242 
0243     if (!QFileInfo(metadataFile).exists()){
0244         QDirIterator iter(archiveTempDir.path(), QDir::Dirs | QDir::NoDotAndDotDot);
0245 
0246         while(iter.hasNext() ) {
0247             QString currentPath = iter.next();
0248 
0249             QString tempMetadata = currentPath + "/metadata.desktop";
0250 
0251             if (QFileInfo(tempMetadata).exists()) {
0252                 metadataFile = tempMetadata;
0253                 packagePath = currentPath;
0254             }
0255         }
0256     }
0257 
0258     KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile);
0259 
0260     if (metadataAreValid(metadata)) {
0261         QStringList standardPaths = Latte::Layouts::Importer::standardPaths();
0262         QString installPath = standardPaths[0] + "/latte/indicators/" + metadata.pluginId();
0263 
0264         bool updated{QDir(installPath).exists()};
0265 
0266         if (QDir(installPath).exists()) {
0267             QDir(installPath).removeRecursively();
0268         }
0269 
0270         QProcess process;
0271         process.start(QString("mv " +packagePath + " " + installPath));
0272         process.waitForFinished();
0273         QString output(process.readAllStandardOutput());
0274 
0275         showNotificationSucceed(metadata.name(), updated);
0276         return Latte::Types::Installed;
0277     }
0278 
0279     showNotificationError();
0280     return Latte::Types::Failed;
0281 }
0282 
0283 void Factory::removeIndicator(QString id)
0284 {
0285     if (m_plugins.contains(id)) {
0286         QString pluginName = m_plugins[id].name();
0287 
0288         auto msg = new QMessageBox(m_parentWidget);
0289         msg->setIcon(QMessageBox::Warning);
0290         msg->setWindowTitle(i18n("Remove Indicator"));
0291         msg->setText(
0292                     i18n("Do you want to remove <b>%0</b> indicator from your system?").arg(pluginName));
0293         msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
0294         msg->setDefaultButton(QMessageBox::No);
0295 
0296         connect(msg, &QMessageBox::finished, this, [ &, msg, id, pluginName](int result) {
0297             auto showRemovedSucceed = [](QString name) {
0298                 auto notification = new KNotification("remove-done", KNotification::CloseOnTimeout);
0299                 notification->setText(i18nc("indicator_name, removed success","<b>%0</b> indicator removed successfully").arg(name));
0300                 notification->sendEvent();
0301             };
0302 
0303             if (result == QMessageBox::Yes) {
0304                 qDebug() << "Trying to remove indicator :: " << id;
0305                 QProcess process;
0306                 process.start(QString("kpackagetool5 -r " +id + " -t Latte/Indicator"));
0307                 process.waitForFinished();
0308                 showRemovedSucceed(pluginName);
0309             }
0310         });
0311 
0312         msg->open();
0313     }
0314 }
0315 
0316 void Factory::downloadIndicator()
0317 {
0318     KNS3::DownloadDialog dialog(QStringLiteral("latte-indicators.knsrc"), m_parentWidget);
0319 
0320     dialog.exec();
0321 }
0322 
0323 }
0324 }