1 /*
2     SPDX-FileCopyrightText: 2017 Smith AR <audoban@openmailbox.org>
3     SPDX-FileCopyrightText: 2017 Michail Vourlakos <mvourlakos@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "importer.h"
9 
10 // local
11 #include <coretypes.h>
12 #include "manager.h"
13 #include "../lattecorona.h"
14 #include "../screenpool.h"
15 #include "../layout/abstractlayout.h"
16 #include "../settings/universalsettings.h"
17 #include "../templates/templatesmanager.h"
18 #include "../tools/commontools.h"
19 
20 // Qt
21 #include <QFile>
22 #include <QLatin1String>
23 
24 // KDE
25 #include <KArchive/KTar>
26 #include <KArchive/KArchiveEntry>
27 #include <KArchive/KArchiveDirectory>
28 #include <KConfigGroup>
29 #include <KLocalizedString>
30 #include <KNotification>
31 
32 
33 enum SessionType
34 {
35     DefaultSession = 0,
36     AlternativeSession
37 };
38 
39 namespace Latte {
40 namespace Layouts {
41 
Importer(QObject * parent)42 Importer::Importer(QObject *parent)
43     : QObject(parent)
44 {
45     m_manager = qobject_cast<Layouts::Manager *>(parent);
46 
47     qDebug() << " IMPORTER, STORAGE TEMP DIR ::: " << m_storageTmpDir.path();
48 }
49 
~Importer()50 Importer::~Importer()
51 {
52 }
53 
updateOldConfiguration()54 bool Importer::updateOldConfiguration()
55 {
56     QFile oldAppletsFile(Latte::configPath() + "/lattedock-appletsrc");
57 
58     if (!oldAppletsFile.exists()) {
59         return false;
60     }
61 
62     //! import standard old configuration and create the relevant layouts
63     importOldLayout(Latte::configPath() + "/lattedock-appletsrc", i18n("My Layout"));
64     importOldLayout(Latte::configPath() + "/lattedock-appletsrc", i18n("Alternative"), true);
65 
66     QFile extFile(Latte::configPath() + "/lattedockextrc");
67 
68     //! import also the old user layouts into the new architecture
69     if (extFile.exists()) {
70         KSharedConfigPtr extFileConfig = KSharedConfig::openConfig(extFile.fileName());
71         KConfigGroup externalSettings = KConfigGroup(extFileConfig, "External");
72         QStringList userLayouts = externalSettings.readEntry("userLayouts", QStringList());
73 
74         for(const auto &userConfig : userLayouts) {
75             qDebug() << "user layout : " << userConfig;
76             importOldConfiguration(userConfig);
77         }
78     }
79 
80     m_manager->corona()->universalSettings()->setVersion(2);
81     m_manager->corona()->universalSettings()->setSingleModeLayoutName(i18n("My Layout"));
82 
83     return true;
84 }
85 
importOldLayout(QString oldAppletsPath,QString newName,bool alternative,QString exportDirectory)86 bool Importer::importOldLayout(QString oldAppletsPath, QString newName, bool alternative, QString exportDirectory)
87 {
88     QString newLayoutPath = layoutCanBeImported(oldAppletsPath, newName, exportDirectory);
89     qDebug() << "New Layout Should be created: " << newLayoutPath;
90 
91     KSharedConfigPtr oldFile = KSharedConfig::openConfig(oldAppletsPath);
92     KSharedConfigPtr newFile = KSharedConfig::openConfig(newLayoutPath);
93 
94     KConfigGroup containments = KConfigGroup(oldFile, "Containments");
95     KConfigGroup copiedContainments = KConfigGroup(newFile, "Containments");
96 
97     QList<int> systrays;
98 
99     bool atLeastOneContainmentWasFound{false};
100 
101     //! first copy the latte containments that correspond to the correct session
102     //! and find also the systrays that should be copied also
103     for(const auto &containmentId : containments.groupList()) {
104         KConfigGroup containmentGroup = containments.group(containmentId);
105 
106         QString plugin = containmentGroup.readEntry("plugin", QString());
107         SessionType session = (SessionType)containmentGroup.readEntry("session", (int)DefaultSession);
108 
109         bool shouldImport = false;
110 
111         if (plugin == QLatin1String("org.kde.latte.containment") && session == DefaultSession && !alternative) {
112             qDebug() << containmentId << " - " << plugin << " - " << session;
113             shouldImport = true;
114         } else if (plugin == QLatin1String("org.kde.latte.containment") && session == AlternativeSession && alternative) {
115             qDebug() << containmentId << " - " << plugin << " - " << session;
116             shouldImport = true;
117         }
118 
119         // this latte containment should be imported
120         if (shouldImport) {
121             auto applets = containments.group(containmentId).group("Applets");
122 
123             for(const auto &applet : applets.groupList()) {
124                 KConfigGroup appletSettings = applets.group(applet).group("Configuration");
125 
126                 int systrayId = appletSettings.readEntry("SystrayContainmentId", "-1").toInt();
127 
128                 if (systrayId != -1) {
129                     systrays.append(systrayId);
130                     qDebug() << "systray was found in the containment...";
131                     break;
132                 }
133             }
134 
135             KConfigGroup newContainment = copiedContainments.group(containmentId);
136             containmentGroup.copyTo(&newContainment);
137             atLeastOneContainmentWasFound = true;
138         }
139     }
140 
141     //! not even one latte containment was found for that layout so we must break
142     //! the code here
143     if (!atLeastOneContainmentWasFound) {
144         return false;
145     }
146 
147     //! copy also the systrays that were discovered
148     for(const auto &containmentId : containments.groupList()) {
149         int cId = containmentId.toInt();
150 
151         if (systrays.contains(cId)) {
152             KConfigGroup containmentGroup = containments.group(containmentId);
153             KConfigGroup newContainment = copiedContainments.group(containmentId);
154             containmentGroup.copyTo(&newContainment);
155         }
156     }
157 
158     copiedContainments.sync();
159 
160     KConfigGroup oldGeneralSettings = KConfigGroup(oldFile, "General");
161 
162     QStringList layoutLaunchers;
163 
164     if (!alternative) {
165         layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_default", QStringList());
166     } else {
167         layoutLaunchers = oldGeneralSettings.readEntry("globalLaunchers_alternative", QStringList());
168     }
169 
170     //! update also the layout settings correctly
171     Layout::AbstractLayout newLayout(this, newLayoutPath, newName);
172     newLayout.setVersion(2);
173     newLayout.setLaunchers(layoutLaunchers);
174 
175     //newLayout.setShowInMenu(true);
176 
177     if (alternative) {
178         newLayout.setColor("purple");
179     } else {
180         newLayout.setColor("blue");
181     }
182 
183     return true;
184 }
185 
standardPaths(bool localfirst)186 QStringList Importer::standardPaths(bool localfirst)
187 {
188     QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
189 
190     if (localfirst) {
191         return paths;
192     } else {
193         QStringList reversed;
194 
195         for (int i=paths.count()-1; i>=0; i--) {
196             reversed << paths[i];
197         }
198 
199         return reversed;
200     }
201 }
202 
standardPathsFor(QString subPath,bool localfirst)203 QStringList Importer::standardPathsFor(QString subPath, bool localfirst)
204 {
205     QStringList paths = standardPaths(localfirst);
206 
207     QString separator = subPath.startsWith("/") ? "" : "/";
208 
209     for (int i=0; i<paths.count(); ++i) {
210         paths[i] = paths[i] + separator + subPath;
211     }
212 
213     return paths;
214 }
215 
standardPath(QString subPath,bool localfirst)216 QString Importer::standardPath(QString subPath, bool localfirst)
217 {
218     QStringList paths = standardPaths(localfirst);
219 
220     for(const auto &pt : paths) {
221         QString ptF = pt + "/" +subPath;
222         if (QFileInfo(ptF).exists()) {
223             return ptF;
224         }
225     }
226 
227     //! in any case that above fails
228     if (QFileInfo("/usr/share/"+subPath).exists()) {
229         return "/usr/share/"+subPath;
230     }
231 
232     return "";
233 }
234 
storageTmpDir() const235 QString Importer::storageTmpDir() const
236 {
237     return m_storageTmpDir.path();
238 }
239 
layoutCanBeImported(QString oldAppletsPath,QString newName,QString exportDirectory)240 QString Importer::layoutCanBeImported(QString oldAppletsPath, QString newName, QString exportDirectory)
241 {
242     QFile oldAppletsrc(oldAppletsPath);
243 
244     //! old file doesn't exist
245     if (!oldAppletsrc.exists()) {
246         return QString();
247     }
248 
249     KSharedConfigPtr lConfig = KSharedConfig::openConfig(oldAppletsPath);
250     KConfigGroup m_layoutGroup = KConfigGroup(lConfig, "LayoutSettings");
251     int layoutVersion = m_layoutGroup.readEntry("version", 1);
252 
253     //! old file layout appears to not be old as its version is >=2
254     if (layoutVersion >= 2) {
255         return QString();
256     }
257 
258     QDir layoutDir(exportDirectory.isNull() ? layoutUserDir() : exportDirectory);
259 
260     if (!layoutDir.exists() && exportDirectory.isNull()) {
261         QDir(Latte::configPath()).mkdir("latte");
262     }
263 
264     //! set up the new layout name
265     if (newName.isEmpty()) {
266         int extension = oldAppletsrc.fileName().lastIndexOf(".latterc");
267 
268         if (extension > 0) {
269             //! remove the last 8 characters that contain the extension
270             newName = oldAppletsrc.fileName().remove(extension, 8);
271         } else {
272             newName = oldAppletsrc.fileName();
273         }
274     }
275 
276     QString newLayoutPath = layoutDir.absolutePath() + "/" + newName + ".layout.latte";
277     QFile newLayoutFile(newLayoutPath);
278 
279     QStringList filter;
280     filter.append(QString(newName + "*.layout.latte"));
281     QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
282 
283     //! if the newLayout already exists provide a newName that doesn't
284     if (files.count() >= 1) {
285         int newCounter = files.count() + 1;
286 
287         newLayoutPath = layoutDir.absolutePath() + "/" + newName + "-" + QString::number(newCounter) + ".layout.latte";
288     }
289 
290     return newLayoutPath;
291 }
292 
importOldConfiguration(QString oldConfigPath,QString newName)293 bool Importer::importOldConfiguration(QString oldConfigPath, QString newName)
294 {
295     QFile oldConfigFile(oldConfigPath);
296 
297     if (!oldConfigFile.exists()) {
298         return false;
299     }
300 
301     KTar archive(oldConfigPath, QStringLiteral("application/x-tar"));
302     archive.open(QIODevice::ReadOnly);
303 
304     if (!archive.isOpen()) {
305         return false;
306     }
307 
308     auto rootDir = archive.directory();
309     QTemporaryDir uniqueTempDir;
310     QDir tempDir{uniqueTempDir.path()};
311 
312     qDebug() << "temp layout directory : " << tempDir.absolutePath();
313 
314     if (rootDir) {
315         if (!tempDir.exists())
316             tempDir.mkpath(tempDir.absolutePath());
317 
318         for(const auto &name : rootDir->entries()) {
319             auto fileEntry = rootDir->file(name);
320 
321             if (fileEntry && (fileEntry->name() == QLatin1String("lattedockrc")
322                               || fileEntry->name() == QLatin1String("lattedock-appletsrc"))) {
323                 if (!fileEntry->copyTo(tempDir.absolutePath())) {
324                     qInfo() << i18nc("import/export config", "The extracted file could not be copied!!!");
325                     archive.close();
326                     return false;
327                 }
328             } else {
329                 qInfo() << i18nc("import/export config", "The file has a wrong format!!!");
330                 archive.close();
331                 return false;
332             }
333         }
334     } else {
335         qInfo() << i18nc("import/export config", "The temp directory could not be created!!!");
336         archive.close();
337         return false;
338     }
339 
340     //! only if the above has passed we must process the files
341     QString appletsPath(tempDir.absolutePath() + "/lattedock-appletsrc");
342     QString screensPath(tempDir.absolutePath() + "/lattedockrc");
343 
344     if (!QFile(appletsPath).exists() || !QFile(screensPath).exists()) {
345         return false;
346     }
347 
348 
349     if (newName.isEmpty()) {
350         int lastSlash = oldConfigPath.lastIndexOf("/");
351         newName = oldConfigPath.remove(0, lastSlash + 1);
352 
353         int ext = newName.lastIndexOf(".latterc");
354         newName = newName.remove(ext, 8);
355     }
356 
357     if (!importOldLayout(appletsPath, newName)) {
358         return false;
359     }
360 
361     //! the old configuration contains also screen values, these must be updated also
362     /*
363     * do not use any deprecated screen ids
364     * KSharedConfigPtr oldScreensConfig = KSharedConfig::openConfig(screensPath);
365     KConfigGroup m_screensGroup = KConfigGroup(oldScreensConfig, "ScreenConnectors");
366 
367     //restore the known ids to connector mappings
368     for(const QString &key : m_screensGroup.keyList()) {
369         QString connector = m_screensGroup.readEntry(key, QString());
370         int id = key.toInt();
371 
372         if (id >= 10 && !m_manager->corona()->screenPool()->hasScreenId(id)) {
373             m_manager->corona()->screenPool()->insertScreenMapping(id, connector);
374         }
375     }*/
376 
377     return true;
378 }
379 
exportFullConfiguration(QString file)380 bool Importer::exportFullConfiguration(QString file)
381 {
382     if (QFile::exists(file) && !QFile::remove(file)) {
383         return false;
384     }
385 
386     KTar archive(file, QStringLiteral("application/x-tar"));
387 
388     if (!archive.open(QIODevice::WriteOnly)) {
389         return false;
390     }
391 
392     archive.addLocalFile(QString(Latte::configPath() + "/lattedockrc"), QStringLiteral("lattedockrc"));
393 
394     for(const auto &layoutName : availableLayouts()) {
395         archive.addLocalFile(layoutUserFilePath(layoutName), QString("latte/" + layoutName + ".layout.latte"));
396     }
397 
398     //! custom templates
399     QDir templatesDir(Latte::configPath() + "/latte/templates");
400     QStringList filters;
401     filters.append(QString("*.layout.latte"));
402     QStringList templates = templatesDir.entryList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
403 
404     for (int i=0; i<templates.count(); ++i) {
405         QString templatePath = templatesDir.path() + "/" + templates[i];
406         archive.addLocalFile(templatePath, QString("latte/templates/" + templates[i]));
407     }
408 
409     filters.clear();
410     filters.append(QString("*.view.latte"));
411     templates = templatesDir.entryList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks);
412 
413     for (int i=0; i<templates.count(); ++i) {
414         QString templatePath = templatesDir.path() + "/" + templates[i];
415         archive.addLocalFile(templatePath, QString("latte/templates/" + templates[i]));
416     }
417 
418     archive.close();
419 
420     return true;
421 }
422 
fileVersion(QString file)423 Importer::LatteFileVersion Importer::fileVersion(QString file)
424 {
425     if (!QFile::exists(file))
426         return UnknownFileType;
427 
428     if (file.endsWith(".layout.latte")) {
429         KSharedConfigPtr lConfig = KSharedConfig::openConfig(QFileInfo(file).absoluteFilePath());
430         KConfigGroup layoutGroup = KConfigGroup(lConfig, "LayoutSettings");
431         int version = layoutGroup.readEntry("version", 1);
432 
433         if (version == 2)
434             return Importer::LayoutVersion2;
435         else
436             return Importer::UnknownFileType;
437     }
438 
439     if (!file.endsWith(".latterc")) {
440         return Importer::UnknownFileType;
441     }
442 
443     KTar archive(file, QStringLiteral("application/x-tar"));
444     archive.open(QIODevice::ReadOnly);
445 
446     //! if the file isnt a tar archive
447     if (!archive.isOpen()) {
448         return Importer::UnknownFileType;
449     }
450 
451     QTemporaryDir archiveTempDir;
452 
453     bool version1rc = false;
454     bool version1applets = false;
455 
456     bool version2rc = false;
457     bool version2LatteDir = false;
458     bool version2layout = false;
459 
460     archive.directory()->copyTo(archiveTempDir.path());
461 
462 
463     //rc file
464     QString rcFile(archiveTempDir.path() + "/lattedockrc");
465 
466     if (QFile(rcFile).exists()) {
467         KSharedConfigPtr lConfig = KSharedConfig::openConfig(rcFile);
468         KConfigGroup universalGroup = KConfigGroup(lConfig, "UniversalSettings");
469         int version = universalGroup.readEntry("version", 1);
470 
471         if (version == 1) {
472             version1rc = true;
473         } else if (version == 2) {
474             version2rc = true;
475         }
476     }
477 
478     //applets file
479     QString appletsFile(archiveTempDir.path() + "/lattedock-appletsrc");
480 
481     if (QFile(appletsFile).exists() && version1rc) {
482         KSharedConfigPtr lConfig = KSharedConfig::openConfig(appletsFile);
483         KConfigGroup generalGroup = KConfigGroup(lConfig, "LayoutSettings");
484         int version = generalGroup.readEntry("version", 1);
485 
486         if (version == 1) {
487             version1applets = true;
488         } else if (version == 2) {
489             version2layout = true;
490         }
491     }
492 
493     //latte directory
494     QString latteDir(archiveTempDir.path() + "/latte");
495 
496     if (QDir(latteDir).exists()) {
497         version2LatteDir = true;
498     }
499 
500     if (version1applets) {
501         return ConfigVersion1;
502     } else if (version2rc && version2LatteDir) {
503         return ConfigVersion2;
504     }
505 
506     return Importer::UnknownFileType;
507 }
508 
importHelper(QString fileName)509 bool Importer::importHelper(QString fileName)
510 {
511     LatteFileVersion version = fileVersion(fileName);
512 
513     if ((version != ConfigVersion1) && (version != ConfigVersion2)) {
514         return false;
515     }
516 
517     KTar archive(fileName, QStringLiteral("application/x-tar"));
518     archive.open(QIODevice::ReadOnly);
519 
520     if (!archive.isOpen()) {
521         return false;
522     }
523 
524     QDir latteDir(layoutUserDir());
525 
526     if (latteDir.exists()) {
527         latteDir.removeRecursively();
528     }
529 
530     archive.directory()->copyTo(Latte::configPath());
531 
532     return true;
533 }
534 
isAutostartEnabled()535 bool Importer::isAutostartEnabled()
536 {
537     QFile autostartFile(Latte::configPath() + "/autostart/org.kde.latte-dock.desktop");
538     return autostartFile.exists();
539 }
540 
enableAutostart()541 void Importer::enableAutostart()
542 {
543     //! deprecated old file
544     QFile oldAutostartFile(Latte::configPath() + "/autostart/latte-dock.desktop");
545 
546     if (oldAutostartFile.exists()) {
547         //! remove deprecated file
548         oldAutostartFile.remove();
549     }
550 
551     QFile autostartFile(Latte::configPath() + "/autostart/org.kde.latte-dock.desktop");
552     QFile metaFile(standardPath("applications/org.kde.latte-dock.desktop", false));
553 
554     if (autostartFile.exists()) {
555         //! if autostart file already exists, do nothing
556         return;
557     }
558 
559     if (metaFile.exists()) {
560         //! check if autostart folder exists and create otherwise
561         QDir autostartDir(Latte::configPath() + "/autostart");
562         if (!autostartDir.exists()) {
563             QDir configDir(Latte::configPath());
564             configDir.mkdir("autostart");
565         }
566 
567         metaFile.copy(autostartFile.fileName());
568     }
569 }
570 
disableAutostart()571 void Importer::disableAutostart()
572 {
573     QFile oldAutostartFile(Latte::configPath() + "/autostart/latte-dock.desktop");
574 
575     if (oldAutostartFile.exists()) {
576         //! remove deprecated file
577         oldAutostartFile.remove();
578     }
579 
580     QFile autostartFile(Latte::configPath() + "/autostart/org.kde.latte-dock.desktop");
581 
582     if (autostartFile.exists()) {
583         autostartFile.remove();
584     }
585 }
586 
hasViewTemplate(const QString & name)587 bool Importer::hasViewTemplate(const QString &name)
588 {
589     return availableViewTemplates().contains(name);
590 }
591 
importLayout(const QString & fileName,const QString & suggestedLayoutName)592 QString Importer::importLayout(const QString &fileName, const QString &suggestedLayoutName)
593 {
594     QString newLayoutName = importLayoutHelper(fileName, suggestedLayoutName);
595 
596     if (!newLayoutName.isEmpty()) {
597         emit newLayoutAdded(layoutUserFilePath(newLayoutName));
598     }
599 
600     return newLayoutName;
601 }
602 
importLayoutHelper(const QString & fileName,const QString & suggestedLayoutName)603 QString Importer::importLayoutHelper(const QString &fileName, const QString &suggestedLayoutName)
604 {
605     LatteFileVersion version = fileVersion(fileName);
606 
607     if (version != LayoutVersion2) {
608         return QString();
609     }
610 
611     QString newLayoutName = !suggestedLayoutName.isEmpty() ? suggestedLayoutName : Layout::AbstractLayout::layoutName(fileName);
612     newLayoutName = uniqueLayoutName(newLayoutName);
613 
614     QString newPath = layoutUserFilePath(newLayoutName);
615 
616     QDir localLayoutsDir(layoutUserDir());
617 
618     if (!localLayoutsDir.exists()) {
619         QDir(Latte::configPath()).mkdir("latte");
620     }
621 
622     QFile(fileName).copy(newPath);
623 
624     QFileInfo newFileInfo(newPath);
625 
626     if (newFileInfo.exists() && !newFileInfo.isWritable()) {
627         QFile(newPath).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
628     }
629 
630     return newLayoutName;
631 }
632 
availableLayouts()633 QStringList Importer::availableLayouts()
634 {
635     QDir layoutDir(layoutUserDir());
636     QStringList filter;
637     filter.append(QString("*.layout.latte"));
638     QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
639 
640     QStringList layoutNames;
641 
642     for(const auto &file : files) {
643         layoutNames.append(Layout::AbstractLayout::layoutName(file));
644     }
645 
646     return layoutNames;
647 }
648 
availableViewTemplates()649 QStringList Importer::availableViewTemplates()
650 {
651     QStringList templates;
652 
653     QDir localDir(layoutUserDir() + "/templates");
654     QStringList filter;
655     filter.append(QString("*.view.latte"));
656     QStringList files = localDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
657 
658     for(const auto &file : files) {
659         templates.append(Templates::Manager::templateName(file));
660     }
661 
662     QDir systemDir(systemShellDataPath()+"/contents/templates");
663     QStringList sfiles = systemDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
664 
665     for(const auto &file : sfiles) {
666         QString name = Templates::Manager::templateName(file);
667         if (!templates.contains(name)) {
668             templates.append(name);
669         }
670     }
671 
672     return templates;
673 }
674 
availableLayoutTemplates()675 QStringList Importer::availableLayoutTemplates()
676 {
677     QStringList templates;
678 
679     QDir localDir(layoutUserDir() + "/templates");
680     QStringList filter;
681     filter.append(QString("*.layout.latte"));
682     QStringList files = localDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
683 
684     for(const auto &file : files) {
685         templates.append(Templates::Manager::templateName(file));
686     }
687 
688     QDir systemDir(systemShellDataPath()+"/contents/templates");
689     QStringList sfiles = systemDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
690 
691     for(const auto &file : sfiles) {
692         QString name = Templates::Manager::templateName(file);
693         if (!templates.contains(name)) {
694             templates.append(name);
695         }
696     }
697 
698     return templates;
699 }
700 
nameOfConfigFile(const QString & fileName)701 QString Importer::nameOfConfigFile(const QString &fileName)
702 {
703     int lastSlash = fileName.lastIndexOf("/");
704     QString tempLayoutFile = fileName;
705     QString layoutName = tempLayoutFile.remove(0, lastSlash + 1);
706 
707     int ext = layoutName.lastIndexOf(".latterc");
708     layoutName = layoutName.remove(ext, 8);
709 
710     return layoutName;
711 }
712 
layoutExists(QString layoutName)713 bool Importer::layoutExists(QString layoutName)
714 {
715     return QFile::exists(layoutUserFilePath(layoutName));
716 }
717 
718 
layoutUserDir()719 QString Importer::layoutUserDir()
720 {
721     return QString(Latte::configPath() + "/latte");
722 }
723 
layoutUserFilePath(QString layoutName)724 QString Importer::layoutUserFilePath(QString layoutName)
725 {
726     return QString(layoutUserDir() + "/" + layoutName + ".layout.latte");
727 }
728 
systemShellDataPath()729 QString Importer::systemShellDataPath()
730 {
731     QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
732     QString rootpath = paths.count() > 0 ? paths[paths.count()-1] : "/usr/share";
733     return  rootpath + "/plasma/shells/org.kde.latte.shell";
734 }
735 
layoutTemplateSystemFilePath(const QString & name)736 QString Importer::layoutTemplateSystemFilePath(const QString &name)
737 {
738     return systemShellDataPath() + "/contents/templates/" + name + ".layout.latte";
739 }
740 
uniqueLayoutName(QString name)741 QString Importer::uniqueLayoutName(QString name)
742 {
743     int pos_ = name.lastIndexOf(QRegExp(QString(" - [0-9]+")));
744 
745     if (layoutExists(name) && pos_ > 0) {
746         name = name.left(pos_);
747     }
748 
749     int i = 2;
750 
751     QString namePart = name;
752 
753     while (layoutExists(name)) {
754         name = namePart + " - " + QString::number(i);
755         i++;
756     }
757 
758     return name;
759 }
760 
checkRepairMultipleLayoutsLinkedFile()761 QStringList Importer::checkRepairMultipleLayoutsLinkedFile()
762 {
763     QString linkedFilePath = layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME);
764     KSharedConfigPtr filePtr = KSharedConfig::openConfig(linkedFilePath);
765     KConfigGroup linkedContainments = KConfigGroup(filePtr, "Containments");
766 
767     //! layoutName and its Containments
768     QHash<QString, QStringList> linkedLayoutContainmentGroups;
769 
770     for(const auto &cId : linkedContainments.groupList()) {
771         QString layoutName = linkedContainments.group(cId).readEntry("layoutId", QString());
772 
773         if (!layoutName.isEmpty()) {
774             qDebug() << layoutName;
775             linkedLayoutContainmentGroups[layoutName].append(cId);
776             linkedContainments.group(cId).writeEntry("layoutId", QString());
777         }
778     }
779 
780     QStringList updatedLayouts;
781 
782     for(const auto &layoutName : linkedLayoutContainmentGroups.uniqueKeys()) {
783         if (layoutName != Layout::MULTIPLELAYOUTSHIDDENNAME && layoutExists(layoutName)) {
784             updatedLayouts << layoutName;
785             KSharedConfigPtr layoutFilePtr = KSharedConfig::openConfig(layoutUserFilePath(layoutName));
786             KConfigGroup origLayoutContainments = KConfigGroup(layoutFilePtr, "Containments");
787 
788             //Clear old containments
789             origLayoutContainments.deleteGroup();
790 
791             //Update containments
792             for(const auto &cId : linkedLayoutContainmentGroups[layoutName]) {
793                 KConfigGroup newContainment = origLayoutContainments.group(cId);
794                 linkedContainments.group(cId).copyTo(&newContainment);
795                 linkedContainments.group(cId).deleteGroup();
796             }
797 
798             origLayoutContainments.sync();
799         }
800     }
801 
802     //! clear all remaining ghost containments
803     for(const auto &cId : linkedContainments.groupList()) {
804         linkedContainments.group(cId).deleteGroup();
805     }
806 
807     linkedContainments.sync();
808 
809     return updatedLayouts;
810 }
811 
812 }
813 }
814