File indexing completed on 2024-09-15 12:59:02
0001 /* 0002 * Copyright 2017 Smith AR <audoban@openmailbox.org> 0003 * Michail Vourlakos <mvourlakos@gmail.com> 0004 * 0005 * This file is part of Latte-Dock 0006 * 0007 * Latte-Dock is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU General Public License as 0009 * published by the Free Software Foundation; either version 2 of 0010 * the License, or (at your option) any later version. 0011 * 0012 * Latte-Dock is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 * GNU General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU General Public License 0018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 0021 #include "importer.h" 0022 0023 // local 0024 #include "manager.h" 0025 #include "../lattecorona.h" 0026 #include "../screenpool.h" 0027 #include "../layout/abstractlayout.h" 0028 #include "../settings/universalsettings.h" 0029 #include "../liblatte2/types.h" 0030 0031 // Qt 0032 #include <QFile> 0033 #include <QTemporaryDir> 0034 0035 // KDE 0036 #include <KArchive/KTar> 0037 #include <KArchive/KArchiveEntry> 0038 #include <KArchive/KArchiveDirectory> 0039 #include <KConfigGroup> 0040 #include <KLocalizedString> 0041 #include <KNotification> 0042 0043 namespace Latte { 0044 namespace Layouts { 0045 0046 Importer::Importer(QObject *parent) 0047 : QObject(parent) 0048 { 0049 m_manager = qobject_cast<Layouts::Manager *>(parent); 0050 } 0051 0052 Importer::~Importer() 0053 { 0054 } 0055 0056 bool Importer::updateOldConfiguration() 0057 { 0058 QFile oldAppletsFile(QDir::homePath() + "/.config/lattedock-appletsrc"); 0059 0060 if (!oldAppletsFile.exists()) { 0061 return false; 0062 } 0063 0064 //! import standard old configuration and create the relevant layouts 0065 importOldLayout(QDir::homePath() + "/.config/lattedock-appletsrc", i18n("My Layout")); 0066 importOldLayout(QDir::homePath() + "/.config/lattedock-appletsrc", i18n("Alternative"), true); 0067 0068 QFile extFile(QDir::homePath() + "/.config/lattedockextrc"); 0069 0070 //! import also the old user layouts into the new architecture 0071 if (extFile.exists()) { 0072 KSharedConfigPtr extFileConfig = KSharedConfig::openConfig(extFile.fileName()); 0073 KConfigGroup externalSettings = KConfigGroup(extFileConfig, "External"); 0074 QStringList userLayouts = externalSettings.readEntry("userLayouts", QStringList()); 0075 0076 for(const auto &userConfig : userLayouts) { 0077 qDebug() << "user layout : " << userConfig; 0078 importOldConfiguration(userConfig); 0079 } 0080 } 0081 0082 m_manager->corona()->universalSettings()->setVersion(2); 0083 m_manager->corona()->universalSettings()->setCurrentLayoutName(i18n("My Layout")); 0084 0085 return true; 0086 } 0087 0088 bool Importer::importOldLayout(QString oldAppletsPath, QString newName, bool alternative, QString exportDirectory) 0089 { 0090 QString newLayoutPath = layoutCanBeImported(oldAppletsPath, newName, exportDirectory); 0091 qDebug() << "New Layout Should be created: " << newLayoutPath; 0092 0093 KSharedConfigPtr oldFile = KSharedConfig::openConfig(oldAppletsPath); 0094 KSharedConfigPtr newFile = KSharedConfig::openConfig(newLayoutPath); 0095 0096 KConfigGroup containments = KConfigGroup(oldFile, "Containments"); 0097 KConfigGroup copiedContainments = KConfigGroup(newFile, "Containments"); 0098 0099 QList<int> systrays; 0100 0101 bool atLeastOneContainmentWasFound{false}; 0102 0103 //! first copy the latte containments that correspond to the correct session 0104 //! and find also the systrays that should be copied also 0105 for(const auto &containmentId : containments.groupList()) { 0106 KConfigGroup containmentGroup = containments.group(containmentId); 0107 0108 QString plugin = containmentGroup.readEntry("plugin", QString()); 0109 Types::SessionType session = (Types::SessionType)containmentGroup.readEntry("session", (int)Types::DefaultSession); 0110 0111 bool shouldImport = false; 0112 0113 if (plugin == "org.kde.latte.containment" && session == Types::DefaultSession && !alternative) { 0114 qDebug() << containmentId << " - " << plugin << " - " << session; 0115 shouldImport = true; 0116 } else if (plugin == "org.kde.latte.containment" && session == Types::AlternativeSession && alternative) { 0117 qDebug() << containmentId << " - " << plugin << " - " << session; 0118 shouldImport = true; 0119 } 0120 0121 // this latte containment should be imported 0122 if (shouldImport) { 0123 auto applets = containments.group(containmentId).group("Applets"); 0124 0125 for(const auto &applet : applets.groupList()) { 0126 KConfigGroup appletSettings = applets.group(applet).group("Configuration"); 0127 0128 int systrayId = appletSettings.readEntry("SystrayContainmentId", "-1").toInt(); 0129 0130 if (systrayId != -1) { 0131 systrays.append(systrayId); 0132 qDebug() << "systray was found in the containment..."; 0133 break; 0134 } 0135 } 0136 0137 KConfigGroup newContainment = copiedContainments.group(containmentId); 0138 containmentGroup.copyTo(&newContainment); 0139 atLeastOneContainmentWasFound = true; 0140 } 0141 } 0142 0143 //! not even one latte containment was found for that layout so we must break 0144 //! the code here 0145 if (!atLeastOneContainmentWasFound) { 0146 return false; 0147 } 0148 0149 //! copy also the systrays that were discovered 0150 for(const auto &containmentId : containments.groupList()) { 0151 int cId = containmentId.toInt(); 0152 0153 if (systrays.contains(cId)) { 0154 KConfigGroup containmentGroup = containments.group(containmentId); 0155 KConfigGroup newContainment = copiedContainments.group(containmentId); 0156 containmentGroup.copyTo(&newContainment); 0157 } 0158 } 0159 0160 copiedContainments.sync(); 0161 0162 KConfigGroup oldGeneralSettings = KConfigGroup(oldFile, "General"); 0163 0164 QStringList layoutLaunchers; 0165 0166 if (!alternative) { 0167 layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_default", QStringList()); 0168 } else { 0169 layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_alternative", QStringList()); 0170 } 0171 0172 //! update also the layout settings correctly 0173 Layout::AbstractLayout newLayout(this, newLayoutPath, newName); 0174 newLayout.setVersion(2); 0175 newLayout.setLaunchers(layoutLaunchers); 0176 0177 //newLayout.setShowInMenu(true); 0178 0179 if (alternative) { 0180 newLayout.setColor("purple"); 0181 } else { 0182 newLayout.setColor("blue"); 0183 } 0184 0185 return true; 0186 } 0187 0188 QStringList Importer::standardPaths(bool localfirst) 0189 { 0190 QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); 0191 0192 if (localfirst) { 0193 return paths; 0194 } else { 0195 QStringList reversed; 0196 0197 for (int i=paths.count()-1; i>=0; i--) { 0198 reversed << paths[i]; 0199 } 0200 0201 return reversed; 0202 } 0203 } 0204 0205 QStringList Importer::standardPathsFor(QString subPath, bool localfirst) 0206 { 0207 QStringList paths = standardPaths(localfirst); 0208 0209 QString separator = subPath.startsWith("/") ? "" : "/"; 0210 0211 for (int i=0; i<paths.count(); ++i) { 0212 paths[i] = paths[i] + separator + subPath; 0213 } 0214 0215 return paths; 0216 } 0217 0218 0219 QString Importer::standardPath(QString subPath, bool localfirst) 0220 { 0221 QStringList paths = standardPaths(localfirst); 0222 0223 for(const auto &pt : paths) { 0224 QString ptF = pt + "/" +subPath; 0225 if (QFileInfo(ptF).exists()) { 0226 return ptF; 0227 } 0228 } 0229 0230 //! in any case that above fails 0231 if (QFileInfo("/usr/share/"+subPath).exists()) { 0232 return "/usr/share/"+subPath; 0233 } 0234 0235 return ""; 0236 } 0237 0238 QString Importer::layoutCanBeImported(QString oldAppletsPath, QString newName, QString exportDirectory) 0239 { 0240 QFile oldAppletsrc(oldAppletsPath); 0241 0242 //! old file doesn't exist 0243 if (!oldAppletsrc.exists()) { 0244 return QString(); 0245 } 0246 0247 KSharedConfigPtr lConfig = KSharedConfig::openConfig(oldAppletsPath); 0248 KConfigGroup m_layoutGroup = KConfigGroup(lConfig, "LayoutSettings"); 0249 int layoutVersion = m_layoutGroup.readEntry("version", 1); 0250 0251 //! old file layout appears to not be old as its version is >=2 0252 if (layoutVersion >= 2) { 0253 return QString(); 0254 } 0255 0256 QDir layoutDir(exportDirectory.isNull() ? QDir::homePath() + "/.config/latte" : exportDirectory); 0257 0258 if (!layoutDir.exists() && exportDirectory.isNull()) { 0259 QDir(QDir::homePath() + "/.config").mkdir("latte"); 0260 } 0261 0262 //! set up the new layout name 0263 if (newName.isEmpty()) { 0264 int extension = oldAppletsrc.fileName().lastIndexOf(".latterc"); 0265 0266 if (extension > 0) { 0267 //! remove the last 8 characters that contain the extension 0268 newName = oldAppletsrc.fileName().remove(extension, 8); 0269 } else { 0270 newName = oldAppletsrc.fileName(); 0271 } 0272 } 0273 0274 QString newLayoutPath = layoutDir.absolutePath() + "/" + newName + ".layout.latte"; 0275 QFile newLayoutFile(newLayoutPath); 0276 0277 QStringList filter; 0278 filter.append(QString(newName + "*.layout.latte")); 0279 QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); 0280 0281 //! if the newLayout already exists provide a newName that doesn't 0282 if (files.count() >= 1) { 0283 int newCounter = files.count() + 1; 0284 0285 newLayoutPath = layoutDir.absolutePath() + "/" + newName + "-" + QString::number(newCounter) + ".layout.latte"; 0286 } 0287 0288 return newLayoutPath; 0289 } 0290 0291 bool Importer::importOldConfiguration(QString oldConfigPath, QString newName) 0292 { 0293 QFile oldConfigFile(oldConfigPath); 0294 0295 if (!oldConfigFile.exists()) { 0296 return false; 0297 } 0298 0299 KTar archive(oldConfigPath, QStringLiteral("application/x-tar")); 0300 archive.open(QIODevice::ReadOnly); 0301 0302 if (!archive.isOpen()) { 0303 return false; 0304 } 0305 0306 auto rootDir = archive.directory(); 0307 QTemporaryDir uniqueTempDir; 0308 QDir tempDir{uniqueTempDir.path()}; 0309 0310 qDebug() << "temp layout directory : " << tempDir.absolutePath(); 0311 0312 if (rootDir) { 0313 if (!tempDir.exists()) 0314 tempDir.mkpath(tempDir.absolutePath()); 0315 0316 for(const auto &name : rootDir->entries()) { 0317 auto fileEntry = rootDir->file(name); 0318 0319 if (fileEntry && (fileEntry->name() == "lattedockrc" 0320 || fileEntry->name() == "lattedock-appletsrc")) { 0321 if (!fileEntry->copyTo(tempDir.absolutePath())) { 0322 qInfo() << i18nc("import/export config", "The extracted file could not be copied!!!"); 0323 archive.close(); 0324 return false; 0325 } 0326 } else { 0327 qInfo() << i18nc("import/export config", "The file has a wrong format!!!"); 0328 archive.close(); 0329 return false; 0330 } 0331 } 0332 } else { 0333 qInfo() << i18nc("import/export config", "The temp directory could not be created!!!"); 0334 archive.close(); 0335 return false; 0336 } 0337 0338 //! only if the above has passed we must process the files 0339 QString appletsPath(tempDir.absolutePath() + "/lattedock-appletsrc"); 0340 QString screensPath(tempDir.absolutePath() + "/lattedockrc"); 0341 0342 if (!QFile(appletsPath).exists() || !QFile(screensPath).exists()) { 0343 return false; 0344 } 0345 0346 0347 if (newName.isEmpty()) { 0348 int lastSlash = oldConfigPath.lastIndexOf("/"); 0349 newName = oldConfigPath.remove(0, lastSlash + 1); 0350 0351 int ext = newName.lastIndexOf(".latterc"); 0352 newName = newName.remove(ext, 8); 0353 } 0354 0355 if (!importOldLayout(appletsPath, newName)) { 0356 return false; 0357 } 0358 0359 //! the old configuration contains also screen values, these must be updated also 0360 KSharedConfigPtr oldScreensConfig = KSharedConfig::openConfig(screensPath); 0361 KConfigGroup m_screensGroup = KConfigGroup(oldScreensConfig, "ScreenConnectors"); 0362 0363 //restore the known ids to connector mappings 0364 for(const QString &key : m_screensGroup.keyList()) { 0365 QString connector = m_screensGroup.readEntry(key, QString()); 0366 int id = key.toInt(); 0367 0368 if (id >= 10 && !m_manager->corona()->screenPool()->knownIds().contains(id)) { 0369 m_manager->corona()->screenPool()->insertScreenMapping(id, connector); 0370 } 0371 } 0372 0373 return true; 0374 } 0375 0376 bool Importer::exportFullConfiguration(QString file) 0377 { 0378 if (QFile::exists(file) && !QFile::remove(file)) { 0379 return false; 0380 } 0381 0382 KTar archive(file, QStringLiteral("application/x-tar")); 0383 0384 if (!archive.open(QIODevice::WriteOnly)) { 0385 return false; 0386 } 0387 0388 archive.addLocalFile(QString(QDir::homePath() + "/.config/lattedockrc"), QStringLiteral("lattedockrc")); 0389 0390 for(const auto &layoutName : availableLayouts()) { 0391 archive.addLocalFile(layoutFilePath(layoutName), QString("latte/" + layoutName + ".layout.latte")); 0392 } 0393 0394 //archive.addLocalDirectory(QString(QDir::homePath() + "/.config/latte"), QStringLiteral("latte")); 0395 0396 archive.close(); 0397 0398 return true; 0399 } 0400 0401 Importer::LatteFileVersion Importer::fileVersion(QString file) 0402 { 0403 if (!QFile::exists(file)) 0404 return UnknownFileType; 0405 0406 if (file.endsWith(".layout.latte")) { 0407 KSharedConfigPtr lConfig = KSharedConfig::openConfig(QFileInfo(file).absoluteFilePath()); 0408 KConfigGroup layoutGroup = KConfigGroup(lConfig, "LayoutSettings"); 0409 int version = layoutGroup.readEntry("version", 1); 0410 0411 if (version == 2) 0412 return Importer::LayoutVersion2; 0413 else 0414 return Importer::UnknownFileType; 0415 } 0416 0417 if (!file.endsWith(".latterc")) { 0418 return Importer::UnknownFileType; 0419 } 0420 0421 KTar archive(file, QStringLiteral("application/x-tar")); 0422 archive.open(QIODevice::ReadOnly); 0423 0424 //! if the file isnt a tar archive 0425 if (!archive.isOpen()) { 0426 return Importer::UnknownFileType; 0427 } 0428 0429 QTemporaryDir archiveTempDir; 0430 0431 bool version1rc = false; 0432 bool version1applets = false; 0433 0434 bool version2rc = false; 0435 bool version2LatteDir = false; 0436 bool version2layout = false; 0437 0438 archive.directory()->copyTo(archiveTempDir.path()); 0439 0440 0441 //rc file 0442 QString rcFile(archiveTempDir.path() + "/lattedockrc"); 0443 0444 if (QFile(rcFile).exists()) { 0445 KSharedConfigPtr lConfig = KSharedConfig::openConfig(rcFile); 0446 KConfigGroup universalGroup = KConfigGroup(lConfig, "UniversalSettings"); 0447 int version = universalGroup.readEntry("version", 1); 0448 0449 if (version == 1) { 0450 version1rc = true; 0451 } else if (version == 2) { 0452 version2rc = true; 0453 } 0454 } 0455 0456 //applets file 0457 QString appletsFile(archiveTempDir.path() + "/lattedock-appletsrc"); 0458 0459 if (QFile(appletsFile).exists() && version1rc) { 0460 KSharedConfigPtr lConfig = KSharedConfig::openConfig(appletsFile); 0461 KConfigGroup generalGroup = KConfigGroup(lConfig, "LayoutSettings"); 0462 int version = generalGroup.readEntry("version", 1); 0463 0464 if (version == 1) { 0465 version1applets = true; 0466 } else if (version == 2) { 0467 version2layout = true; 0468 } 0469 } 0470 0471 //latte directory 0472 QString latteDir(archiveTempDir.path() + "/latte"); 0473 0474 if (QDir(latteDir).exists()) { 0475 version2LatteDir = true; 0476 } 0477 0478 if (version1applets && version1applets) { 0479 return ConfigVersion1; 0480 } else if (version2rc && version2LatteDir) { 0481 return ConfigVersion2; 0482 } 0483 0484 return Importer::UnknownFileType; 0485 } 0486 0487 bool Importer::importHelper(QString fileName) 0488 { 0489 LatteFileVersion version = fileVersion(fileName); 0490 0491 if ((version != ConfigVersion1) && (version != ConfigVersion2)) { 0492 return false; 0493 } 0494 0495 KTar archive(fileName, QStringLiteral("application/x-tar")); 0496 archive.open(QIODevice::ReadOnly); 0497 0498 if (!archive.isOpen()) { 0499 return false; 0500 } 0501 0502 QString latteDirPath(QDir::homePath() + "/.config/latte"); 0503 QDir latteDir(latteDirPath); 0504 0505 if (latteDir.exists()) { 0506 latteDir.removeRecursively(); 0507 } 0508 0509 archive.directory()->copyTo(QString(QDir::homePath() + "/.config")); 0510 0511 return true; 0512 } 0513 0514 QString Importer::importLayoutHelper(QString fileName) 0515 { 0516 LatteFileVersion version = fileVersion(fileName); 0517 0518 if (version != LayoutVersion2) { 0519 return QString(); 0520 } 0521 0522 QString newLayoutName = Layout::AbstractLayout::layoutName(fileName); 0523 newLayoutName = uniqueLayoutName(newLayoutName); 0524 0525 QString newPath = QDir::homePath() + "/.config/latte/" + newLayoutName + ".layout.latte"; 0526 QFile(fileName).copy(newPath); 0527 0528 QFileInfo newFileInfo(newPath); 0529 0530 if (newFileInfo.exists() && !newFileInfo.isWritable()) { 0531 QFile(newPath).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); 0532 } 0533 0534 return newLayoutName; 0535 0536 } 0537 0538 QStringList Importer::availableLayouts() 0539 { 0540 QDir layoutDir(QDir::homePath() + "/.config/latte"); 0541 QStringList filter; 0542 filter.append(QString("*.layout.latte")); 0543 QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); 0544 0545 QStringList layoutNames; 0546 0547 for(const auto &file : files) { 0548 layoutNames.append(Layout::AbstractLayout::layoutName(file)); 0549 } 0550 0551 return layoutNames; 0552 } 0553 0554 0555 QString Importer::nameOfConfigFile(const QString &fileName) 0556 { 0557 int lastSlash = fileName.lastIndexOf("/"); 0558 QString tempLayoutFile = fileName; 0559 QString layoutName = tempLayoutFile.remove(0, lastSlash + 1); 0560 0561 int ext = layoutName.lastIndexOf(".latterc"); 0562 layoutName = layoutName.remove(ext, 8); 0563 0564 return layoutName; 0565 } 0566 0567 bool Importer::layoutExists(QString layoutName) 0568 { 0569 return QFile::exists(layoutFilePath(layoutName)); 0570 } 0571 0572 0573 QString Importer::layoutFilePath(QString layoutName) 0574 { 0575 return QString(QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte"); 0576 } 0577 0578 QString Importer::uniqueLayoutName(QString name) 0579 { 0580 int pos_ = name.lastIndexOf(QRegExp(QString("[-][0-9]+"))); 0581 0582 if (layoutExists(name) && pos_ > 0) { 0583 name = name.left(pos_); 0584 } 0585 0586 int i = 2; 0587 0588 QString namePart = name; 0589 0590 while (layoutExists(name)) { 0591 name = namePart + "-" + QString::number(i); 0592 i++; 0593 } 0594 0595 return name; 0596 } 0597 0598 QStringList Importer::checkRepairMultipleLayoutsLinkedFile() 0599 { 0600 QString linkedFilePath = QDir::homePath() + "/.config/latte/" + Layout::AbstractLayout::MultipleLayoutsName + ".layout.latte"; 0601 KSharedConfigPtr filePtr = KSharedConfig::openConfig(linkedFilePath); 0602 KConfigGroup linkedContainments = KConfigGroup(filePtr, "Containments"); 0603 0604 //! layoutName and its Containments 0605 QHash<QString, QStringList> linkedLayoutContainmentGroups; 0606 0607 for(const auto &cId : linkedContainments.groupList()) { 0608 QString layoutName = linkedContainments.group(cId).readEntry("layoutId", QString()); 0609 0610 if (!layoutName.isEmpty()) { 0611 qDebug() << layoutName; 0612 linkedLayoutContainmentGroups[layoutName].append(cId); 0613 linkedContainments.group(cId).writeEntry("layoutId", QString()); 0614 } 0615 } 0616 0617 QStringList updatedLayouts; 0618 0619 for(const auto &layoutName : linkedLayoutContainmentGroups.uniqueKeys()) { 0620 if (layoutName != Layout::AbstractLayout::MultipleLayoutsName && layoutExists(layoutName)) { 0621 updatedLayouts << layoutName; 0622 KSharedConfigPtr layoutFilePtr = KSharedConfig::openConfig(layoutFilePath(layoutName)); 0623 KConfigGroup origLayoutContainments = KConfigGroup(layoutFilePtr, "Containments"); 0624 0625 //Clear old containments 0626 origLayoutContainments.deleteGroup(); 0627 0628 //Update containments 0629 for(const auto &cId : linkedLayoutContainmentGroups[layoutName]) { 0630 KConfigGroup newContainment = origLayoutContainments.group(cId); 0631 linkedContainments.group(cId).copyTo(&newContainment); 0632 linkedContainments.group(cId).deleteGroup(); 0633 } 0634 0635 origLayoutContainments.sync(); 0636 } 0637 } 0638 0639 //! clear all remaining ghost containments 0640 for(const auto &cId : linkedContainments.groupList()) { 0641 linkedContainments.group(cId).deleteGroup(); 0642 } 0643 0644 linkedContainments.sync(); 0645 0646 return updatedLayouts; 0647 } 0648 0649 } 0650 }