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 }