File indexing completed on 2024-09-29 07:50:19

0001 /*
0002     SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "factory.h"
0007 
0008 // local
0009 #include "../layouts/importer.h"
0010 
0011 // Qt
0012 #include <QDebug>
0013 #include <QDialogButtonBox>
0014 #include <QDir>
0015 #include <QDirIterator>
0016 #include <QMessageBox>
0017 #include <QProcess>
0018 #include <QTemporaryDir>
0019 #include <QTimer>
0020 #include <QLatin1String>
0021 
0022 // KDE
0023 #include <KDirWatch>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 #include <KNotification>
0027 #include <KPluginMetaData>
0028 #include <KArchive/KTar>
0029 #include <KArchive/KZip>
0030 #include <KArchive/KArchiveEntry>
0031 #include <KArchive/KArchiveDirectory>
0032 #include <KNewStuff3/KNS3/QtQuickDialogWrapper>
0033 
0034 namespace Latte {
0035 namespace Indicator {
0036 
0037 Factory::Factory(QObject *parent)
0038     : QObject(parent)
0039 {
0040     m_parentWidget = new QWidget();
0041 
0042     m_mainPaths = Latte::Layouts::Importer::standardPaths();
0043 
0044     for(int i=0; i<m_mainPaths.count(); ++i) {
0045         m_mainPaths[i] = m_mainPaths[i] + "/latte/indicators";
0046         discoverNewIndicators(m_mainPaths[i]);
0047     }
0048 
0049     //! track paths for changes
0050     for(const auto &dir : m_mainPaths) {
0051         KDirWatch::self()->addDir(dir);
0052     }
0053 
0054     connect(KDirWatch::self(), &KDirWatch::dirty, this, [ & ](const QString & path) {
0055         if (m_indicatorsPaths.contains(path)) {
0056             //! indicator updated
0057             reload(path);
0058         } else if (m_mainPaths.contains(path)){
0059             //! consider indicator addition
0060             discoverNewIndicators(path);
0061         }
0062     });
0063 
0064     connect(KDirWatch::self(), &KDirWatch::deleted, this, [ & ](const QString & path) {
0065         if (m_indicatorsPaths.contains(path)) {
0066             //! indicator removed
0067             removeIndicatorRecords(path);
0068         }
0069     });
0070 
0071     qDebug() << m_plugins["org.kde.latte.default"].name();
0072 }
0073 
0074 Factory::~Factory()
0075 {
0076     m_parentWidget->deleteLater();
0077 }
0078 
0079 bool Factory::pluginExists(QString id) const
0080 {
0081     return m_plugins.contains(id);
0082 }
0083 
0084 int Factory::customPluginsCount()
0085 {
0086     return m_customPluginIds.count();
0087 }
0088 
0089 QStringList Factory::customPluginIds()
0090 {
0091     return m_customPluginIds;
0092 }
0093 
0094 QStringList Factory::customPluginNames()
0095 {
0096     return m_customPluginNames;
0097 }
0098 
0099 QStringList Factory::customLocalPluginIds()
0100 {
0101     return m_customLocalPluginIds;
0102 }
0103 
0104 KPluginMetaData Factory::metadata(QString pluginId)
0105 {
0106     if (m_plugins.contains(pluginId)) {
0107         return m_plugins[pluginId];
0108     }
0109 
0110     return KPluginMetaData();
0111 }
0112 
0113 void Factory::reload(const QString &indicatorPath)
0114 {
0115     QString pluginChangedId;
0116 
0117     if (!indicatorPath.isEmpty() && indicatorPath != "." && indicatorPath != "..") {
0118         QString metadataFile = metadataFileAbsolutePath(indicatorPath);
0119 
0120         if(QFileInfo(metadataFile).exists()) {
0121             KPluginMetaData metadata = KPluginMetaData(metadataFile);
0122 
0123             if (metadataAreValid(metadata)) {
0124                 pluginChangedId = metadata.pluginId();
0125                 QString uiFile = indicatorPath + "/package/" + metadata.value("X-Latte-MainScript");
0126 
0127                 if (!m_plugins.contains(metadata.pluginId())) {
0128                     m_plugins[metadata.pluginId()] = metadata;
0129                 }
0130 
0131                 if (QFileInfo(uiFile).exists()) {
0132                     m_pluginUiPaths[metadata.pluginId()] = QFileInfo(uiFile).absolutePath();
0133                 }
0134 
0135                 if ((metadata.pluginId() != "org.kde.latte.default")
0136                         && (metadata.pluginId() != "org.kde.latte.plasma")
0137                         && (metadata.pluginId() != "org.kde.latte.plasmatabstyle")) {
0138 
0139                     //! find correct alphabetical position
0140                     int newPos = -1;
0141 
0142                     if (!m_customPluginIds.contains(metadata.pluginId())) {
0143                         for (int i=0; i<m_customPluginNames.count(); ++i) {
0144                             if (QString::compare(metadata.name(), m_customPluginNames[i], Qt::CaseInsensitive)<=0) {
0145                                 newPos = i;
0146                                 break;
0147                             }
0148                         }
0149                     }
0150 
0151                     if (!m_customPluginIds.contains(metadata.pluginId())) {
0152                         if (newPos == -1) {
0153                             m_customPluginIds << metadata.pluginId();
0154                         } else {
0155                             m_customPluginIds.insert(newPos, metadata.pluginId());
0156                         }
0157                     }
0158 
0159                     if (!m_customPluginNames.contains(metadata.name())) {
0160                         if (newPos == -1) {
0161                             m_customPluginNames << metadata.name();
0162                         } else {
0163                             m_customPluginNames.insert(newPos, metadata.name());
0164                         }
0165                     }
0166                 }
0167 
0168                 if (indicatorPath.startsWith(QDir::homePath())) {
0169                     m_customLocalPluginIds << metadata.pluginId();
0170                 }
0171             }
0172 
0173             qDebug() << " Indicator Package Loaded ::: " << metadata.name() << " [" << metadata.pluginId() << "]" << " - [" << indicatorPath <<"]";
0174 
0175             /*qDebug() << " Indicator value ::: " << metadata.pluginId();
0176                             qDebug() << " Indicator value ::: " << metadata.fileName();
0177                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-MainScript");
0178                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-ConfigUi");
0179                             qDebug() << " Indicator value ::: " << metadata.value("X-Latte-ConfigXml");*/
0180         }
0181     }
0182 
0183     if (!pluginChangedId.isEmpty()) {
0184         emit indicatorChanged(pluginChangedId);
0185     }
0186 }
0187 
0188 void Factory::discoverNewIndicators(const QString &main)
0189 {
0190     if (!m_mainPaths.contains(main)) {
0191         return;
0192     }
0193 
0194     QDirIterator indicatorsDirs(main, QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::NoIteratorFlags);
0195 
0196     while(indicatorsDirs.hasNext()){
0197         indicatorsDirs.next();
0198         QString iPath = indicatorsDirs.filePath();
0199 
0200         if (!m_indicatorsPaths.contains(iPath)) {
0201             m_indicatorsPaths << iPath;
0202             KDirWatch::self()->addDir(iPath);
0203             reload(iPath);
0204         }
0205     }
0206 }
0207 
0208 void Factory::removeIndicatorRecords(const QString &path)
0209 {
0210     if (m_indicatorsPaths.contains(path)) {
0211         QString pluginId =  path.section('/',-1);
0212         m_plugins.remove(pluginId);
0213         m_pluginUiPaths.remove(pluginId);
0214 
0215         int pos = m_customPluginIds.indexOf(pluginId);
0216 
0217         m_customPluginIds.removeAt(pos);
0218         m_customPluginNames.removeAt(pos);
0219         m_customLocalPluginIds.removeAll(pluginId);
0220 
0221         m_indicatorsPaths.removeAll(path);
0222 
0223         KDirWatch::self()->removeDir(path);
0224 
0225         //! delay informing the removal in case it is just an update
0226         QTimer::singleShot(1000, [this, pluginId]() {
0227             emit indicatorRemoved(pluginId);
0228         });
0229     }
0230 }
0231 
0232 bool Factory::isCustomType(const QString &id) const
0233 {
0234     return ((id != "org.kde.latte.default") && (id != "org.kde.latte.plasma") && (id != "org.kde.latte.plasmatabstyle"));
0235 }
0236 
0237 bool Factory::metadataAreValid(KPluginMetaData &metadata)
0238 {
0239     return metadata.isValid()
0240             && metadata.category() == QLatin1String("Latte Indicator")
0241             && !metadata.value("X-Latte-MainScript").isEmpty();
0242 }
0243 
0244 bool Factory::metadataAreValid(QString &file)
0245 {
0246     if (QFileInfo(file).exists()) {
0247         KPluginMetaData metadata(file);
0248         return metadata.isValid();
0249     }
0250 
0251     return false;
0252 }
0253 
0254 QString Factory::uiPath(QString pluginName) const
0255 {
0256     if (!m_pluginUiPaths.contains(pluginName)) {
0257         return "";
0258     }
0259 
0260     return m_pluginUiPaths[pluginName];
0261 }
0262 
0263 QString Factory::metadataFileAbsolutePath(const QString &directoryPath)
0264 {
0265     QString metadataFile = directoryPath + "/metadata.json";
0266 
0267     if(QFileInfo(metadataFile).exists()) {
0268         return metadataFile;
0269     }
0270 
0271     metadataFile = directoryPath + "/metadata.desktop";
0272 
0273     if(QFileInfo(metadataFile).exists()) {
0274         return metadataFile;
0275     }
0276 
0277     return QString();
0278 }
0279 
0280 Latte::ImportExport::State Factory::importIndicatorFile(QString compressedFile)
0281 {
0282     auto showNotificationError = []() {
0283         auto notification = new KNotification("import-fail", KNotification::CloseOnTimeout);
0284         notification->setText(i18n("Failed to import indicator"));
0285         notification->sendEvent();
0286     };
0287 
0288     auto showNotificationSucceed = [](QString name, bool updated) {
0289         auto notification = new KNotification("import-done", KNotification::CloseOnTimeout);
0290         notification->setText(updated ? i18nc("indicator_name, imported updated","%1 indicator updated successfully", name) :
0291                                         i18nc("indicator_name, imported success","%1 indicator installed successfully", name));
0292         notification->sendEvent();
0293     };
0294 
0295     KArchive *archive;
0296 
0297     KZip *zipArchive = new KZip(compressedFile);
0298     zipArchive->open(QIODevice::ReadOnly);
0299 
0300     //! if the file isnt a zip archive
0301     if (!zipArchive->isOpen()) {
0302         delete zipArchive;
0303 
0304         KTar *tarArchive = new KTar(compressedFile, QStringLiteral("application/x-tar"));
0305         tarArchive->open(QIODevice::ReadOnly);
0306 
0307         if (!tarArchive->isOpen()) {
0308             delete tarArchive;
0309             showNotificationError();
0310             return Latte::ImportExport::FailedState;
0311         } else {
0312             archive = tarArchive;
0313         }
0314     } else {
0315         archive = zipArchive;
0316     }
0317 
0318     QTemporaryDir archiveTempDir;
0319     archive->directory()->copyTo(archiveTempDir.path());
0320 
0321     //metadata file
0322     QString packagePath = archiveTempDir.path();
0323     QString metadataFile = metadataFileAbsolutePath(archiveTempDir.path());
0324 
0325     if (!QFileInfo(metadataFile).exists()){
0326         QDirIterator iter(archiveTempDir.path(), QDir::Dirs | QDir::NoDotAndDotDot);
0327 
0328         while(iter.hasNext() ) {
0329             QString currentPath = iter.next();
0330 
0331             QString tempMetadata = metadataFileAbsolutePath(currentPath);
0332 
0333             if (QFileInfo(tempMetadata).exists()) {
0334                 metadataFile = tempMetadata;
0335                 packagePath = currentPath;
0336             }
0337         }
0338     }
0339 
0340     KPluginMetaData metadata = KPluginMetaData(metadataFile);
0341 
0342     if (metadataAreValid(metadata)) {
0343         QStringList standardPaths = Latte::Layouts::Importer::standardPaths();
0344         QString installPath = standardPaths[0] + "/latte/indicators/" + metadata.pluginId();
0345 
0346         bool updated{QDir(installPath).exists()};
0347 
0348         if (QDir(installPath).exists()) {
0349             QDir(installPath).removeRecursively();
0350         }
0351 
0352         QProcess process;
0353         process.start(QString("mv " +packagePath + " " + installPath));
0354         process.waitForFinished();
0355         QString output(process.readAllStandardOutput());
0356 
0357         showNotificationSucceed(metadata.name(), updated);
0358         return Latte::ImportExport::InstalledState;
0359     }
0360 
0361     showNotificationError();
0362     return Latte::ImportExport::FailedState;
0363 }
0364 
0365 void Factory::removeIndicator(QString id)
0366 {
0367     if (m_plugins.contains(id)) {
0368         QString pluginName = m_plugins[id].name();
0369 
0370         QDialog* dialog = new QDialog(nullptr);
0371         dialog->setWindowTitle(i18n("Remove Indicator Confirmation"));
0372         dialog->setObjectName("warning");
0373         dialog->setAttribute(Qt::WA_DeleteOnClose);
0374 
0375         auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No);
0376 
0377         KMessageBox::createKMessageBox(dialog,
0378                                        buttonbox,
0379                                        QMessageBox::Question,
0380                                        i18n("Do you want to remove completely <b>%1</b> indicator from your system?", pluginName),
0381                                        QStringList(),
0382                                        QString(),
0383                                        0,
0384                                        KMessageBox::NoExec,
0385                                        QString());
0386 
0387         connect(buttonbox, &QDialogButtonBox::accepted, [&, id, pluginName]() {
0388             auto showRemovedSucceed = [](QString name) {
0389                 auto notification = new KNotification("remove-done", KNotification::CloseOnTimeout);
0390                 notification->setText(i18nc("indicator_name, removed success","<b>%1</b> indicator removed successfully", name));
0391                 notification->sendEvent();
0392             };
0393 
0394             qDebug() << "Trying to remove indicator :: " << id;
0395             QProcess process;
0396             process.start(QString("kpackagetool5 -r " +id + " -t Latte/Indicator"));
0397             process.waitForFinished();
0398             showRemovedSucceed(pluginName);
0399         });
0400 
0401         dialog->show();
0402     }
0403 }
0404 
0405 void Factory::downloadIndicator()
0406 {
0407     KNS3::QtQuickDialogWrapper dialog(QStringLiteral("latte-indicators.knsrc"), m_parentWidget);
0408     dialog.exec();
0409 }
0410 
0411 }
0412 }