1 /*
2     SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "storage.h"
7 
8 // local
9 #include <coretypes.h>
10 #include "importer.h"
11 #include "manager.h"
12 #include "../lattecorona.h"
13 #include "../screenpool.h"
14 #include "../data/errordata.h"
15 #include "../layout/abstractlayout.h"
16 #include "../view/view.h"
17 
18 // Qt
19 #include <QDebug>
20 #include <QDir>
21 #include <QFile>
22 #include <QFileInfo>
23 #include <QLatin1String>
24 
25 // KDE
26 #include <KConfigGroup>
27 #include <KPluginMetaData>
28 #include <KSharedConfig>
29 #include <KPackage/Package>
30 #include <KPackage/PackageLoader>
31 
32 // Plasma
33 #include <Plasma>
34 #include <Plasma/Applet>
35 #include <Plasma/Containment>
36 
37 namespace Latte {
38 namespace Layouts {
39 
40 const int Storage::IDNULL = -1;
41 const int Storage::IDBASE = 0;
42 
Storage()43 Storage::Storage()
44 {
45     qDebug() << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LAYOUTS::STORAGE, TEMP DIR ::: " << m_storageTmpDir.path();
46 
47     //! Known Errors / Warnings
48     s_knownErrors << Data::Generic(Data::Error::APPLETSWITHSAMEID, i18n("Different Applets With Same Id"));
49     s_knownErrors << Data::Generic(Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT, i18n("Orphaned Parent Applet Of Subcontainment"));
50     s_knownErrors<< Data::Generic(Data::Warning::APPLETANDCONTAINMENTWITHSAMEID, i18n("Different Applet And Containment With Same Id"));
51     s_knownErrors << Data::Generic(Data::Warning::ORPHANEDSUBCONTAINMENT, i18n("Orphaned Subcontainment"));
52 
53 
54     //! Known SubContainment Families
55     SubContaimentIdentityData data;
56     //! Systray Family
57     m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="SystrayContainmentId"};
58     //! Group applet Family
59     m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="ContainmentId"};
60 }
61 
~Storage()62 Storage::~Storage()
63 {
64 }
65 
self()66 Storage *Storage::self()
67 {
68     static Storage store;
69     return &store;
70 }
71 
isWritable(const Layout::GenericLayout * layout) const72 bool Storage::isWritable(const Layout::GenericLayout *layout) const
73 {
74     QFileInfo layoutFileInfo(layout->file());
75 
76     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
77         return false;
78     } else {
79         return true;
80     }
81 }
82 
isLatteContainment(const Plasma::Containment * containment) const83 bool Storage::isLatteContainment(const Plasma::Containment *containment) const
84 {
85     if (!containment) {
86         return false;
87     }
88 
89     if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.latte.containment")) {
90         return true;
91     }
92 
93     return false;
94 }
95 
isLatteContainment(const KConfigGroup & group) const96 bool Storage::isLatteContainment(const KConfigGroup &group) const
97 {
98     QString pluginId = group.readEntry("plugin", "");
99     return pluginId == QLatin1String("org.kde.latte.containment");
100 }
101 
isSubContainment(const Layout::GenericLayout * layout,const Plasma::Applet * applet) const102 bool Storage::isSubContainment(const Layout::GenericLayout *layout, const Plasma::Applet *applet) const
103 {
104     if (!layout || !applet) {
105         return false;
106     }
107 
108     for (const auto containment : *layout->containments()) {
109         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
110         if (parentApplet && parentApplet == applet) {
111             return true;
112         }
113     }
114 
115     return false;
116 }
117 
isSubContainment(const KConfigGroup & appletGroup) const118 bool Storage::isSubContainment(const KConfigGroup &appletGroup) const
119 {
120     return isValid(subContainmentId(appletGroup));
121 }
122 
isValid(const int & id)123 bool Storage::isValid(const int &id)
124 {
125     return id >= IDBASE;
126 }
127 
subContainmentId(const KConfigGroup & appletGroup) const128 int Storage::subContainmentId(const KConfigGroup &appletGroup) const
129 {
130     //! cycle through subcontainments identities
131     for (auto subidentity : m_subIdentities) {
132         KConfigGroup appletConfigGroup = appletGroup;
133 
134         if (!subidentity.cfgGroup.isEmpty()) {
135             //! if identity provides specific configuration group
136             if (appletConfigGroup.hasGroup(subidentity.cfgGroup)) {
137                 appletConfigGroup = appletGroup.group(subidentity.cfgGroup);
138             }
139         }
140 
141         if (!subidentity.cfgProperty.isEmpty()) {
142             //! if identity provides specific property for configuration group
143             if (appletConfigGroup.hasKey(subidentity.cfgProperty)) {
144                 return appletConfigGroup.readEntry(subidentity.cfgProperty, IDNULL);
145             }
146         }
147     }
148 
149     return IDNULL;
150 }
151 
subIdentityIndex(const KConfigGroup & appletGroup) const152 int Storage::subIdentityIndex(const KConfigGroup &appletGroup) const
153 {
154     if (!isSubContainment(appletGroup)) {
155         return IDNULL;
156     }
157 
158     //! cycle through subcontainments identities
159     for (int i=0; i<m_subIdentities.count(); ++i) {
160         KConfigGroup appletConfigGroup = appletGroup;
161 
162         if (!m_subIdentities[i].cfgGroup.isEmpty()) {
163             //! if identity provides specific configuration group
164             if (appletConfigGroup.hasGroup(m_subIdentities[i].cfgGroup)) {
165                 appletConfigGroup = appletGroup.group(m_subIdentities[i].cfgGroup);
166             }
167         }
168 
169         if (!m_subIdentities[i].cfgProperty.isEmpty()) {
170             //! if identity provides specific property for configuration group
171             if (appletConfigGroup.hasKey(m_subIdentities[i].cfgProperty)) {
172                 int subId = appletConfigGroup.readEntry(m_subIdentities[i].cfgProperty, IDNULL);
173                 return isValid(subId) ? i : IDNULL;
174             }
175         }
176     }
177 
178     return IDNULL;
179 }
180 
subContainmentOf(const Layout::GenericLayout * layout,const Plasma::Applet * applet)181 Plasma::Containment *Storage::subContainmentOf(const Layout::GenericLayout *layout, const Plasma::Applet *applet)
182 {
183     if (!layout || !applet) {
184         return nullptr;
185     }
186 
187     if (isSubContainment(layout, applet)) {
188         for (const auto containment : *layout->containments()) {
189             Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
190             if (parentApplet && parentApplet == applet) {
191                 return containment;
192             }
193         }
194     }
195 
196     return nullptr;
197 }
198 
lock(const Layout::GenericLayout * layout)199 void Storage::lock(const Layout::GenericLayout *layout)
200 {
201     QFileInfo layoutFileInfo(layout->file());
202 
203     if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) {
204         QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
205     }
206 }
207 
unlock(const Layout::GenericLayout * layout)208 void Storage::unlock(const Layout::GenericLayout *layout)
209 {
210     QFileInfo layoutFileInfo(layout->file());
211 
212     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
213         QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
214     }
215 }
216 
217 
importToCorona(const Layout::GenericLayout * layout)218 void Storage::importToCorona(const Layout::GenericLayout *layout)
219 {
220     if (!layout->corona()) {
221         return;
222     }
223 
224     //! Setting mutable for create a containment
225     layout->corona()->setImmutability(Plasma::Types::Mutable);
226 
227     QString temp1FilePath = m_storageTmpDir.path() +  "/" + layout->name() + ".multiple.views";
228     //! we need to copy first the layout file because the kde cache
229     //! may not have yet been updated (KSharedConfigPtr)
230     //! this way we make sure at the latest changes stored in the layout file
231     //! will be also available when changing to Multiple Layouts
232     QString tempLayoutFilePath = m_storageTmpDir.path() +  "/" + layout->name() + ".multiple.tmplayout";
233 
234     //! WE NEED A WAY TO COPY A CONTAINMENT!!!!
235     QFile tempLayoutFile(tempLayoutFilePath);
236     QFile copyFile(temp1FilePath);
237     QFile layoutOriginalFile(layout->file());
238 
239     if (tempLayoutFile.exists()) {
240         tempLayoutFile.remove();
241     }
242 
243     if (copyFile.exists())
244         copyFile.remove();
245 
246     layoutOriginalFile.copy(tempLayoutFilePath);
247 
248     KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath);
249     KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath);
250     KConfigGroup copyGroup = KConfigGroup(newFile, "Containments");
251     KConfigGroup current_containments = KConfigGroup(filePtr, "Containments");
252 
253     current_containments.copyTo(&copyGroup);
254 
255     newFile->reparseConfiguration();
256 
257     //! update ids to unique ones
258     QString temp2File = newUniqueIdsFile(temp1FilePath, layout);
259 
260     //! Finally import the configuration
261     importLayoutFile(layout, temp2File);
262 }
263 
264 
availableId(QStringList all,QStringList assigned,int base)265 QString Storage::availableId(QStringList all, QStringList assigned, int base)
266 {
267     bool found = false;
268 
269     int i = base;
270 
271     while (!found && i < 32000) {
272         QString iStr = QString::number(i);
273 
274         if (!all.contains(iStr) && !assigned.contains(iStr)) {
275             return iStr;
276         }
277 
278         i++;
279     }
280 
281     return QString("");
282 }
283 
appletGroupIsValid(const KConfigGroup & appletGroup)284 bool Storage::appletGroupIsValid(const KConfigGroup &appletGroup)
285 {
286     return !( appletGroup.keyList().count() == 0
287               && appletGroup.groupList().count() == 1
288               && appletGroup.groupList().at(0) == QLatin1String("Configuration")
289               && appletGroup.group("Configuration").keyList().count() == 1
290               && appletGroup.group("Configuration").hasKey("PreloadWeight") );
291 }
292 
containmentsIds(const QString & filepath)293 QStringList Storage::containmentsIds(const QString &filepath)
294 {
295     QStringList ids;
296 
297     KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
298     KConfigGroup containments = KConfigGroup(filePtr, "Containments");
299 
300     for(const auto &cId : containments.groupList()) {
301         ids << cId;
302     }
303 
304     return ids;
305 }
306 
appletsIds(const QString & filepath)307 QStringList Storage::appletsIds(const QString &filepath)
308 {
309     QStringList ids;
310 
311     KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
312     KConfigGroup containments = KConfigGroup(filePtr, "Containments");
313 
314     for(const auto &cId : containments.groupList()) {
315         for(const auto &aId : containments.group(cId).group("Applets").groupList()) {
316             ids << aId;
317         }
318     }
319 
320     return ids;
321 }
322 
newUniqueIdsFile(QString originFile,const Layout::GenericLayout * destinationLayout)323 QString Storage::newUniqueIdsFile(QString originFile, const Layout::GenericLayout *destinationLayout)
324 {
325     if (!destinationLayout) {
326         return QString();
327     }
328 
329     QString currentdestinationname = destinationLayout->name();
330     QString currentdestinationfile = "";
331 
332     if (!destinationLayout->isActive()) {
333         currentdestinationfile = destinationLayout->file();
334     }
335 
336     QString tempFile = m_storageTmpDir.path() + "/" + currentdestinationname + ".views.newids";
337 
338     QFile copyFile(tempFile);
339 
340     if (copyFile.exists()) {
341         copyFile.remove();
342     }
343 
344     //! BEGIN updating the ids in the temp file
345     QStringList allIds;
346 
347     if (destinationLayout->isActive()) {
348         allIds << destinationLayout->corona()->containmentsIds();
349         allIds << destinationLayout->corona()->appletsIds();
350     } else {
351         allIds << containmentsIds(currentdestinationfile);
352         allIds << appletsIds(currentdestinationfile);
353     }
354 
355     QStringList toInvestigateContainmentIds;
356     QStringList toInvestigateAppletIds;
357     QStringList toInvestigateSubContIds;
358 
359     //! first is the subcontainment id
360     QHash<QString, QString> subParentContainmentIds;
361     QHash<QString, QString> subAppletIds;
362 
363     //qDebug() << "Ids:" << allIds;
364 
365     //qDebug() << "to copy containments: " << toCopyContainmentIds;
366     //qDebug() << "to copy applets: " << toCopyAppletIds;
367 
368     QStringList assignedIds;
369     QHash<QString, QString> assigned;
370 
371     KSharedConfigPtr filePtr = KSharedConfig::openConfig(originFile);
372     KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments");
373 
374     //! Record the containment and applet ids
375     for (const auto &cId : investigate_conts.groupList()) {
376         toInvestigateContainmentIds << cId;
377         auto appletsEntries = investigate_conts.group(cId).group("Applets");
378         toInvestigateAppletIds << appletsEntries.groupList();
379 
380         //! investigate for subcontainments
381         for (const auto &appletId : appletsEntries.groupList()) {
382             int subId = subContainmentId(appletsEntries.group(appletId));
383 
384             //! It is a subcontainment !!!
385             if (isValid(subId)) {
386                 QString tSubIdStr = QString::number(subId);
387                 toInvestigateSubContIds << tSubIdStr;
388                 subParentContainmentIds[tSubIdStr] = cId;
389                 subAppletIds[tSubIdStr] = appletId;
390                 qDebug() << "subcontainment was found in the containment...";
391             }
392         }
393     }
394 
395     //! Reassign containment and applet ids to unique ones
396     for (const auto &contId : toInvestigateContainmentIds) {
397         QString newId;
398 
399         if (contId.toInt()>=12 && !allIds.contains(contId) && !assignedIds.contains(contId)) {
400             newId = contId;
401         } else {
402             newId = availableId(allIds, assignedIds, 12);
403         }
404 
405         assignedIds << newId;
406         assigned[contId] = newId;
407     }
408 
409     for (const auto &appId : toInvestigateAppletIds) {
410         QString newId;
411 
412         if (appId.toInt()>=40 && !allIds.contains(appId) && !assignedIds.contains(appId)) {
413             newId = appId;
414         } else {
415             newId = availableId(allIds, assignedIds, 40);
416         }
417 
418         assignedIds << newId;
419         assigned[appId] = newId;
420     }
421 
422     qDebug() << "ALL CORONA IDS ::: " << allIds;
423     qDebug() << "FULL ASSIGNMENTS ::: " << assigned;
424 
425     for (const auto &cId : toInvestigateContainmentIds) {
426         QString value = assigned[cId];
427 
428         if (assigned.contains(value)) {
429             QString value2 = assigned[value];
430 
431             if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) {
432                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed ..";
433                 assigned[cId] = cId;
434                 assigned[value] = value;
435             }
436         }
437     }
438 
439     for (const auto &aId : toInvestigateAppletIds) {
440         QString value = assigned[aId];
441 
442         if (assigned.contains(value)) {
443             QString value2 = assigned[value];
444 
445             if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) {
446                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed ..";
447                 assigned[aId] = aId;
448                 assigned[value] = value;
449             }
450         }
451     }
452 
453     qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned;
454 
455     //! update applet ids in their containment order and in MultipleLayouts update also the layoutId
456     for (const auto &cId : investigate_conts.groupList()) {
457         //! Update options that contain applet ids
458         //! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets)
459         QStringList options;
460         options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets";
461 
462         for (const auto &settingStr : options) {
463             QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString());
464 
465             if (!order1.isEmpty()) {
466                 QStringList order1Ids = order1.split(";");
467                 QStringList fixedOrder1Ids;
468 
469                 for (int i = 0; i < order1Ids.count(); ++i) {
470                     fixedOrder1Ids.append(assigned[order1Ids[i]]);
471                 }
472 
473                 QString fixedOrder1 = fixedOrder1Ids.join(";");
474                 investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1);
475             }
476         }
477 
478         if (destinationLayout->isActive() && destinationLayout->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
479             //! will be added in main corona multiple layouts file
480             investigate_conts.group(cId).writeEntry("layoutId", destinationLayout->name());
481         } else {
482             //! will be added in inactive layout
483             investigate_conts.group(cId).writeEntry("layoutId", QString());
484         }
485     }
486 
487     //! must update also the sub id in its applet
488     for (const auto &subId : toInvestigateSubContIds) {
489         KConfigGroup subParentContainment = investigate_conts.group(subParentContainmentIds[subId]);
490         KConfigGroup subAppletConfig = subParentContainment.group("Applets").group(subAppletIds[subId]);
491 
492         int entityIndex = subIdentityIndex(subAppletConfig);
493 
494         if (entityIndex >= 0) {
495             if (!m_subIdentities[entityIndex].cfgGroup.isEmpty()) {
496                 subAppletConfig = subAppletConfig.group(m_subIdentities[entityIndex].cfgGroup);
497             }
498 
499             if (!m_subIdentities[entityIndex].cfgProperty.isEmpty()) {
500                 subAppletConfig.writeEntry(m_subIdentities[entityIndex].cfgProperty, assigned[subId]);
501                 subParentContainment.sync();
502             }
503         }
504     }
505 
506     investigate_conts.sync();
507 
508     //! Copy To Temp 2 File And Update Correctly The Ids
509     KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile);
510     KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments");
511 
512     for (const auto &contId : investigate_conts.groupList()) {
513         QString pluginId = investigate_conts.group(contId).readEntry("plugin", "");
514 
515         if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments
516             KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]);
517             investigate_conts.group(contId).copyTo(&newContainmentGroup);
518 
519             newContainmentGroup.group("Applets").deleteGroup();
520 
521             for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) {
522                 KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId);
523                 KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]);
524                 appletGroup.copyTo(&newAppletGroup);
525             }
526         }
527     }
528 
529     file2Ptr->reparseConfiguration();
530 
531     return tempFile;
532 }
533 
syncToLayoutFile(const Layout::GenericLayout * layout,bool removeLayoutId)534 void Storage::syncToLayoutFile(const Layout::GenericLayout *layout, bool removeLayoutId)
535 {
536     if (!layout->corona() || !isWritable(layout)) {
537         return;
538     }
539 
540     KSharedConfigPtr filePtr = KSharedConfig::openConfig(layout->file());
541 
542     KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments");
543     oldContainments.deleteGroup();
544 
545     qDebug() << " LAYOUT :: " << layout->name() << " is syncing its original file.";
546 
547     for (const auto containment : *layout->containments()) {
548         if (removeLayoutId) {
549             containment->config().writeEntry("layoutId", "");
550         }
551 
552         KConfigGroup newGroup = oldContainments.group(QString::number(containment->id()));
553         containment->config().copyTo(&newGroup);
554 
555         if (!removeLayoutId) {
556             newGroup.writeEntry("layoutId", "");
557         }
558 
559         newGroup.sync();
560     }
561 
562     filePtr->reparseConfiguration();
563 }
564 
importLayoutFile(const Layout::GenericLayout * layout,QString file)565 QList<Plasma::Containment *> Storage::importLayoutFile(const Layout::GenericLayout *layout, QString file)
566 {
567     KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
568     auto newContainments = layout->corona()->importLayout(KConfigGroup(filePtr, ""));
569 
570     qDebug() << " imported containments ::: " << newContainments.length();
571 
572     QList<Plasma::Containment *> importedViews;
573 
574     for (const auto containment : newContainments) {
575         if (isLatteContainment(containment)) {
576             qDebug() << "new latte containment id: " << containment->id();
577             importedViews << containment;
578         }
579     }
580 
581     return importedViews;
582 }
583 
importContainments(const QString & originFile,const QString & destinationFile)584 void Storage::importContainments(const QString &originFile, const QString &destinationFile)
585 {
586     if (originFile.isEmpty() || destinationFile.isEmpty()) {
587         return;
588     }
589 
590     KSharedConfigPtr originPtr = KSharedConfig::openConfig(originFile);
591     KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(destinationFile);
592 
593     KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
594     KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
595 
596     for (const auto originContId : originContainments.groupList()) {
597         KConfigGroup destinationContainment(&destinationContainments, originContId);
598         originContainments.group(originContId).copyTo(&destinationContainment);
599     }
600 
601     destinationContainments.sync();
602 }
603 
newView(const Layout::GenericLayout * destinationLayout,const Data::View & nextViewData)604 Data::View Storage::newView(const Layout::GenericLayout *destinationLayout, const Data::View &nextViewData)
605 {
606     if (!destinationLayout || nextViewData.originFile().isEmpty()) {
607         return Data::View();
608     }
609 
610     qDebug() << "new view for layout";
611 
612     if (destinationLayout->isActive()) {
613         //! Setting mutable for create a containment
614         destinationLayout->corona()->setImmutability(Plasma::Types::Mutable);
615     }
616 
617     QString templateFile = nextViewData.originFile();
618     //! copy view template path in temp file
619     QString templateTmpAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(templateFile).fileName() + ".newids";
620 
621     if (QFile(templateTmpAbsolutePath).exists()) {
622         QFile(templateTmpAbsolutePath).remove();
623     }
624 
625     QFile(templateFile).copy(templateTmpAbsolutePath);
626 
627     //! update ids to unique ones
628     QString temp2File = newUniqueIdsFile(templateTmpAbsolutePath, destinationLayout);
629 
630     //! update view containment data in case next data are provided
631     if (nextViewData.state() != Data::View::IsInvalid) {
632 
633         KSharedConfigPtr lFile = KSharedConfig::openConfig(temp2File);
634         KConfigGroup containments = KConfigGroup(lFile, "Containments");
635 
636         for (const auto cId : containments.groupList()) {
637             if (Layouts::Storage::self()->isLatteContainment(containments.group(cId))) {
638                 //! first view we will find, we update its value
639                 updateView(containments.group(cId), nextViewData);
640                 break;
641             }
642         }
643 
644         lFile->reparseConfiguration();
645     }
646 
647     Data::ViewsTable updatedNextViews = views(temp2File);
648 
649     if (updatedNextViews.rowCount() <= 0) {
650         return Data::View();
651     }
652 
653     if (destinationLayout->isActive()) {
654         //! import views for active layout
655         QList<Plasma::Containment *> importedViews = importLayoutFile(destinationLayout, temp2File);
656 
657         Plasma::Containment *newContainment = (importedViews.size() == 1 ? importedViews[0] : nullptr);
658 
659         if (!newContainment || !newContainment->kPackage().isValid()) {
660             qWarning() << "the requested containment plugin can not be located or loaded from:" << templateFile;
661             return Data::View();
662         }
663     } else {
664         //! import views for inactive layout
665         importContainments(temp2File, destinationLayout->file());
666     }
667 
668     return updatedNextViews[0];
669 }
670 
clearExportedLayoutSettings(KConfigGroup & layoutSettingsGroup)671 void Storage::clearExportedLayoutSettings(KConfigGroup &layoutSettingsGroup)
672 {
673     layoutSettingsGroup.writeEntry("preferredForShortcutsTouched", false);
674     layoutSettingsGroup.writeEntry("lastUsedActivity", QString());
675     layoutSettingsGroup.writeEntry("activities", QStringList());
676     layoutSettingsGroup.sync();
677 }
678 
exportTemplate(const QString & originFile,const QString & destinationFile,const Data::AppletsTable & approvedApplets)679 bool Storage::exportTemplate(const QString &originFile, const QString &destinationFile,const Data::AppletsTable &approvedApplets)
680 {
681     if (originFile.isEmpty() || !QFile(originFile).exists() || destinationFile.isEmpty()) {
682         return false;
683     }
684 
685     if (QFile(destinationFile).exists()) {
686         QFile::remove(destinationFile);
687     }
688 
689     QFile(originFile).copy(destinationFile);
690 
691     KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
692     destFilePtr->reparseConfiguration();
693 
694     KConfigGroup containments = KConfigGroup(destFilePtr, "Containments");
695 
696     QStringList rejectedSubContainments;
697 
698     //! clear applets that are not approved
699     for (const auto &cId : containments.groupList()) {
700         //! clear properties
701         containments.group(cId).writeEntry("layoutId", QString());
702         if (isLatteContainment(containments.group(cId))) {
703             containments.group(cId).writeEntry("isPreferredForShortcuts", false);
704         }
705 
706         //! clear applets
707         auto applets = containments.group(cId).group("Applets");
708         for (const auto &aId: applets.groupList()) {
709             QString pluginId = applets.group(aId).readEntry("plugin", "");
710 
711             if (!approvedApplets.containsId(pluginId)) {
712                 if (!isSubContainment(applets.group(aId))) {
713                     //!remove all configuration for that applet
714                     for (const auto &configId: applets.group(aId).groupList()) {
715                         applets.group(aId).group(configId).deleteGroup();
716                     }
717                 } else {
718                     //! register which subcontaiments should return to default properties
719                     rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
720                 }
721             }
722         }
723     }
724 
725     //! clear rejected SubContainments
726     for (const auto &cId : containments.groupList()) {
727         if (rejectedSubContainments.contains(cId)) {
728             containments.group(cId).group("General").deleteGroup();
729         }
730     };
731 
732     KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
733     clearExportedLayoutSettings(layoutSettingsGrp);
734     destFilePtr->reparseConfiguration();
735 
736     return true;
737 }
738 
exportTemplate(const Layout::GenericLayout * layout,Plasma::Containment * containment,const QString & destinationFile,const Data::AppletsTable & approvedApplets)739 bool Storage::exportTemplate(const Layout::GenericLayout *layout, Plasma::Containment *containment, const QString &destinationFile, const Data::AppletsTable &approvedApplets)
740 {
741     if (!layout || !containment || destinationFile.isEmpty()) {
742         return false;
743     }
744 
745     if (QFile(destinationFile).exists()) {
746         QFile::remove(destinationFile);
747     }
748 
749     KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
750     destFilePtr->reparseConfiguration();
751 
752     KConfigGroup copied_conts = KConfigGroup(destFilePtr, "Containments");
753     KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id()));
754 
755     containment->config().copyTo(&copied_c1);
756 
757     //!investigate if there are subcontainments in the containment to copy also
758 
759     //! subId, subAppletId
760     QHash<uint, QString> subInfo;
761     auto applets = containment->config().group("Applets");
762 
763     for (const auto &applet : applets.groupList()) {
764         int tSubId = subContainmentId(applets.group(applet));
765 
766         //! It is a subcontainment !!!
767         if (isValid(tSubId)) {
768             subInfo[tSubId] = applet;
769             qDebug() << "subcontainment with id "<< tSubId << " was found in the containment... ::: " << containment->id();
770         }
771     }
772 
773     if (subInfo.count() > 0) {
774         for(const auto subId : subInfo.keys()) {
775             Plasma::Containment *subcontainment{nullptr};
776 
777             for (const auto containment : layout->corona()->containments()) {
778                 if (containment->id() == subId) {
779                     subcontainment = containment;
780                     break;
781                 }
782             }
783 
784             if (subcontainment) {
785                 KConfigGroup copied_sub = KConfigGroup(&copied_conts, QString::number(subcontainment->id()));
786                 subcontainment->config().copyTo(&copied_sub);
787             }
788         }
789     }
790     //! end of subcontainments specific code
791 
792     QStringList rejectedSubContainments;
793 
794     //! clear applets that are not approved
795     for (const auto &cId : copied_conts.groupList()) {
796         //! clear properties
797         copied_conts.group(cId).writeEntry("layoutId", QString());
798         if (isLatteContainment(copied_conts.group(cId))) {
799             copied_conts.group(cId).writeEntry("isPreferredForShortcuts", false);
800         }
801 
802         //! clear applets
803         auto applets = copied_conts.group(cId).group("Applets");
804         for (const auto &aId: applets.groupList()) {
805             QString pluginId = applets.group(aId).readEntry("plugin", "");
806 
807             if (!approvedApplets.containsId(pluginId)) {
808                 if (!isSubContainment(applets.group(aId))) {
809                     //!remove all configuration for that applet
810                     for (const auto &configId: applets.group(aId).groupList()) {
811                         applets.group(aId).group(configId).deleteGroup();
812                     }
813                 } else {
814                     //! register which subcontaiments should return to default properties
815                     rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
816                 }
817             }
818         }
819     }
820 
821     //! clear rejected SubContainments
822     for (const auto &cId : copied_conts.groupList()) {
823         if (rejectedSubContainments.contains(cId)) {
824             copied_conts.group(cId).group("General").deleteGroup();
825         }
826     };
827 
828     KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
829     clearExportedLayoutSettings(layoutSettingsGrp);
830     destFilePtr->reparseConfiguration();
831 
832     return true;
833 }
834 
hasDifferentAppletsWithSameId(const Layout::GenericLayout * layout,Data::Error & error)835 bool Storage::hasDifferentAppletsWithSameId(const Layout::GenericLayout *layout, Data::Error &error)
836 {
837     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
838         return false;
839     }
840 
841     error.id = s_knownErrors[Data::Error::APPLETSWITHSAMEID].id;
842     error.name = s_knownErrors[Data::Error::APPLETSWITHSAMEID].name;
843 
844     if (layout->isActive()) { // active layout
845         QStringList registeredapplets;
846         QStringList conflictedapplets;
847 
848         //! split ids to normal registered and conflicted
849         for (const auto containment : *layout->containments()) {
850             QString cid = QString::number(containment->id());
851 
852             for (const auto applet : containment->applets()) {
853                 QString aid = QString::number(applet->id());
854 
855                 if (!registeredapplets.contains(aid)) {
856                     registeredapplets << aid;
857                 } else if (!conflictedapplets.contains(aid)) {
858                     conflictedapplets << aid;
859                 }
860             }
861         }
862 
863         //! create error data
864         for (const auto containment : *layout->containments()) {
865             QString cid = QString::number(containment->id());
866 
867             for (const auto applet : containment->applets()) {
868                 QString aid = QString::number(applet->id());
869 
870                 if (!conflictedapplets.contains(aid)) {
871                    continue;
872                 }
873 
874                 Data::ErrorInformation errorinfo;
875                 errorinfo.id = QString::number(error.information.rowCount());
876                 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
877                 errorinfo.containment.storageId = cid;
878                 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
879                 errorinfo.applet.storageId = aid;
880 
881                 error.information << errorinfo;
882             }
883         }
884     } else { // inactive layout
885         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
886         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
887 
888         QStringList registeredapplets;
889         QStringList conflictedapplets;
890 
891         //! split ids to normal registered and conflicted
892         for (const auto &cid : containmentsEntries.groupList()) {
893             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
894                 if (!registeredapplets.contains(aid)) {
895                     registeredapplets << aid;
896                 } else if (!conflictedapplets.contains(aid)) {
897                     conflictedapplets << aid;
898                 }
899             }
900         }
901 
902         //! create error data
903         for (const auto &cid : containmentsEntries.groupList()) {
904             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
905                 if (!conflictedapplets.contains(aid)) {
906                    continue;
907                 }
908 
909                 Data::ErrorInformation errorinfo;
910                 errorinfo.id = QString::number(error.information.rowCount());
911                 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
912                 errorinfo.containment.storageId = cid;
913                 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
914                 errorinfo.applet.storageId = aid;
915 
916                 error.information << errorinfo;
917             }
918         }
919     }
920 
921     return !error.information.isEmpty();
922 }
923 
hasAppletsAndContainmentsWithSameId(const Layout::GenericLayout * layout,Data::Warning & warning)924 bool Storage::hasAppletsAndContainmentsWithSameId(const Layout::GenericLayout *layout, Data::Warning &warning)
925 {
926     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
927         return false;
928     }
929 
930     warning.id = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].id;
931     warning.name = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].name;
932 
933     if (layout->isActive()) { // active layout
934         QStringList registeredcontainments;
935         QStringList conflicted;
936 
937         //! discover normal containment ids
938         for (const auto containment : *layout->containments()) {
939             QString cid = QString::number(containment->id());
940 
941             if (registeredcontainments.contains(cid)) {
942                 continue;
943             }
944 
945             registeredcontainments << cid;
946         }
947 
948         //! discover conflicted ids between containments and applets
949         for (const auto containment : *layout->containments()) {
950             QString cid = QString::number(containment->id());
951 
952             for (const auto applet : containment->applets()) {
953                 QString aid = QString::number(applet->id());
954 
955                 if (!registeredcontainments.contains(aid)) {
956                     continue;
957                 } else if (!conflicted.contains(aid)) {
958                     conflicted << aid;
959                 }
960             }
961         }
962 
963         //! create warning data
964         for (const auto containment : *layout->containments()) {
965             QString cid = QString::number(containment->id());
966 
967             if (conflicted.contains(cid)) {
968                 Data::WarningInformation warninginfo;
969                 warninginfo.id = QString::number(warning.information.rowCount());
970                 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
971                 warninginfo.containment.storageId = cid;
972 
973                 warning.information << warninginfo;
974             }
975 
976             for (const auto applet : containment->applets()) {
977                 QString aid = QString::number(applet->id());
978 
979                 if (!conflicted.contains(aid)) {
980                    continue;
981                 }
982 
983                 Data::WarningInformation warninginfo;
984                 warninginfo.id = QString::number(warning.information.rowCount());
985                 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
986                 warninginfo.containment.storageId = cid;
987                 warninginfo.applet = metadata(applet->pluginMetaData().pluginId());
988                 warninginfo.applet.storageId = aid;
989 
990                 warning.information << warninginfo;
991             }
992         }
993     } else { // inactive layout
994         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
995         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
996 
997         QStringList registeredcontainments;
998         QStringList conflicted;
999 
1000         //! discover normal containment ids
1001         for (const auto &cid : containmentsEntries.groupList()) {
1002             if (registeredcontainments.contains(cid)) {
1003                 continue;
1004             }
1005 
1006             registeredcontainments << cid;
1007         }
1008 
1009         //! discover conflicted ids between containments and applets
1010         for (const auto &cid : containmentsEntries.groupList()) {
1011             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1012                 if (!registeredcontainments.contains(aid)) {
1013                     continue;
1014                 } else if (!conflicted.contains(aid)) {
1015                     conflicted << aid;
1016                 }
1017             }
1018         }
1019 
1020         //! create warning data
1021         for (const auto &cid : containmentsEntries.groupList()) {
1022             if (conflicted.contains(cid)) {
1023                 Data::WarningInformation warninginfo;
1024                 warninginfo.id = QString::number(warning.information.rowCount());
1025                 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1026                 warninginfo.containment.storageId = cid;
1027 
1028                 warning.information << warninginfo;
1029             }
1030 
1031             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1032                 if (!conflicted.contains(aid)) {
1033                    continue;
1034                 }
1035 
1036                 Data::WarningInformation warninginfo;
1037                 warninginfo.id = QString::number(warning.information.rowCount());
1038                 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1039                 warninginfo.containment.storageId = cid;
1040                 warninginfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1041                 warninginfo.applet.storageId = aid;
1042 
1043                 warning.information << warninginfo;
1044             }
1045         }
1046     }
1047 
1048     return !warning.information.isEmpty();
1049 }
1050 
hasOrphanedParentAppletOfSubContainment(const Layout::GenericLayout * layout,Data::Error & error)1051 bool Storage::hasOrphanedParentAppletOfSubContainment(const Layout::GenericLayout *layout, Data::Error &error)
1052 {
1053     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1054         return false;
1055     }
1056 
1057     error.id = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].id;
1058     error.name = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].name;
1059 
1060     Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1061 
1062     if (layout->isActive()) { // active layout
1063 
1064         //! create error data
1065         for (const auto containment : *layout->containments()) {
1066             QString cid = QString::number(containment->id());
1067 
1068             for (const auto applet : containment->applets()) {
1069                 QString aid = QString::number(applet->id());
1070 
1071                 int subid = subContainmentId(applet->config());
1072 
1073                 if (subid == IDNULL || hasContainment(layout, subid)) {
1074                     continue;
1075                 }
1076 
1077                 Data::ErrorInformation errorinfo;
1078                 errorinfo.id = QString::number(error.information.rowCount());
1079                 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
1080                 errorinfo.containment.storageId = cid;
1081                 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
1082                 errorinfo.applet.storageId = aid;
1083                 errorinfo.applet.subcontainmentId = subid;
1084 
1085                 error.information << errorinfo;
1086             }
1087         }
1088     } else {
1089         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1090         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1091 
1092         //! create error data
1093         for (const auto &cid : containmentsEntries.groupList()) {
1094             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1095                 int subid = subContainmentId(containmentsEntries.group(cid).group("Applets").group(aid));
1096 
1097                 if (subid == IDNULL || hasContainment(layout, subid)) {
1098                     continue;
1099                 }
1100 
1101                 Data::ErrorInformation errorinfo;
1102                 errorinfo.id = QString::number(error.information.rowCount());
1103                 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1104                 errorinfo.containment.storageId = cid;
1105                 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1106                 errorinfo.applet.storageId = aid;
1107                 errorinfo.applet.subcontainmentId = subid;
1108 
1109                 error.information << errorinfo;
1110             }
1111         }
1112     }
1113 
1114     Data::Warning warning1;
1115     if (!error.information.isEmpty() && hasOrphanedSubContainments(layout, warning1)) {
1116         error.information << warning1.information;
1117     }
1118 
1119     return !error.information.isEmpty();
1120 }
1121 
hasOrphanedSubContainments(const Layout::GenericLayout * layout,Data::Warning & warning)1122 bool Storage::hasOrphanedSubContainments(const Layout::GenericLayout *layout, Data::Warning &warning)
1123 {
1124     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1125         return false;
1126     }
1127 
1128     warning.id = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].id;
1129     warning.name = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].name;
1130 
1131     Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1132 
1133     if (layout->isActive()) { // active layout
1134         //! create warning data
1135         for (const auto containment : *layout->containments()) {
1136             QString cid = QString::number(containment->id());
1137 
1138             Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1139             Plasma::Containment *parentContainment = parentApplet ? qobject_cast<Plasma::Containment *>(parentApplet->parent()) : nullptr;
1140 
1141             if (isLatteContainment(containment) || (parentApplet && parentContainment && layout->contains(parentContainment))) {
1142                 //! is latte containment or is subcontainment that belongs to latte containment
1143                 continue;
1144             }
1145 
1146             Data::WarningInformation warninginfo;
1147             warninginfo.id = QString::number(warning.information.rowCount());
1148             warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
1149             warninginfo.containment.storageId = cid;
1150             warning.information << warninginfo;
1151         }
1152     } else { // inactive layout
1153         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1154         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1155 
1156         //! create warning data
1157         for (const auto &cid : containmentsEntries.groupList()) {
1158             if (views.hasContainmentId(cid)) {
1159                 continue;
1160             }
1161 
1162             Data::WarningInformation warninginfo;
1163             warninginfo.id = QString::number(warning.information.rowCount());
1164             warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1165             warninginfo.containment.storageId = cid;
1166             warning.information << warninginfo;
1167         }
1168     }
1169 
1170     return !warning.information.isEmpty();
1171 }
1172 
errors(const Layout::GenericLayout * layout)1173 Data::ErrorsList Storage::errors(const Layout::GenericLayout *layout)
1174 {
1175     Data::ErrorsList errs;
1176 
1177     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1178         return errs;
1179     }
1180 
1181     Data::Error error1;
1182 
1183     if (hasDifferentAppletsWithSameId(layout, error1)) {
1184         errs << error1;
1185     }
1186 
1187     Data::Error error2;
1188 
1189     if (hasOrphanedParentAppletOfSubContainment(layout, error2)) {
1190         errs << error2;
1191     }
1192 
1193     return errs;
1194 }
1195 
warnings(const Layout::GenericLayout * layout)1196 Data::WarningsList Storage::warnings(const Layout::GenericLayout *layout)
1197 {
1198     Data::WarningsList warns;
1199 
1200     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1201         return warns;
1202     }
1203 
1204     Data::Warning warning1;
1205 
1206     if (hasAppletsAndContainmentsWithSameId(layout, warning1)) {
1207         warns << warning1;
1208     }
1209 
1210     Data::Error error1;
1211     Data::Warning warning2;
1212 
1213     if (!hasOrphanedParentAppletOfSubContainment(layout, error1) /*this is needed because this error has higher priority*/
1214             && hasOrphanedSubContainments(layout, warning2)) {
1215         warns << warning2;
1216     }
1217 
1218     return warns;
1219 }
1220 
1221 //! AppletsData Information
metadata(const QString & pluginId)1222 Data::Applet Storage::metadata(const QString &pluginId)
1223 {
1224     Data::Applet data;
1225     data.id = pluginId;
1226 
1227     KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"));
1228     pkg.setDefaultPackageRoot(QStringLiteral("plasma/plasmoids"));
1229     pkg.setPath(pluginId);
1230 
1231     if (pkg.isValid()) {
1232         data.name = pkg.metadata().name();
1233         data.description = pkg.metadata().description();
1234 
1235         QString iconName = pkg.metadata().iconName();
1236         if (!iconName.startsWith("/") && iconName.contains("/")) {
1237             data.icon = QFileInfo(pkg.metadata().fileName()).absolutePath() + "/" + iconName;
1238         } else {
1239             data.icon = iconName;
1240         }
1241     }
1242 
1243     if (data.name.isEmpty()) {
1244         //! this is also a way to identify if a package is installed or not in current system
1245         data.name = data.id;
1246     }
1247 
1248     return data;
1249 }
1250 
plugins(const Layout::GenericLayout * layout,const int containmentid)1251 Data::AppletsTable Storage::plugins(const Layout::GenericLayout *layout, const int containmentid)
1252 {
1253     Data::AppletsTable knownapplets;
1254     Data::AppletsTable unknownapplets;
1255 
1256     if (!layout) {
1257         return knownapplets;
1258     }
1259 
1260     //! empty means all containments are valid
1261     QList<int> validcontainmentids;
1262 
1263     if (isValid(containmentid)) {
1264         validcontainmentids << containmentid;
1265 
1266         //! searching for specific containment and subcontainments and ignore all other containments
1267         for(auto containment : *layout->containments()) {
1268             if (((int)containment->id()) != containmentid) {
1269                 //! ignore irrelevant containments
1270                 continue;
1271             }
1272 
1273             for (auto applet : containment->applets()) {
1274                 if (isSubContainment(layout, applet)) {
1275                     validcontainmentids << subContainmentId(applet->config());
1276                 }
1277             }
1278         }
1279     }
1280 
1281     //! cycle through valid contaiments in order to retrieve their metadata
1282     for(auto containment : *layout->containments()) {
1283         if (validcontainmentids.count()>0 && !validcontainmentids.contains(containment->id())) {
1284             //! searching only for valid containments
1285             continue;
1286         }
1287 
1288         for (auto applet : containment->applets()) {
1289             QString pluginId = applet->pluginMetaData().pluginId();
1290             if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1291                 Data::Applet appletdata = metadata(pluginId);
1292 
1293                 if (appletdata.isInstalled()) {
1294                     knownapplets.insertBasedOnName(appletdata);
1295                 } else if (appletdata.isValid()) {
1296                     unknownapplets.insertBasedOnName(appletdata);
1297                 }
1298             }
1299         }
1300     }
1301 
1302     knownapplets << unknownapplets;
1303 
1304     return knownapplets;
1305 }
1306 
plugins(const QString & layoutfile,const int containmentid)1307 Data::AppletsTable Storage::plugins(const QString &layoutfile, const int containmentid)
1308 {
1309     Data::AppletsTable knownapplets;
1310     Data::AppletsTable unknownapplets;
1311 
1312     if (layoutfile.isEmpty()) {
1313         return knownapplets;
1314     }
1315 
1316     KSharedConfigPtr lFile = KSharedConfig::openConfig(layoutfile);
1317     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1318 
1319     //! empty means all containments are valid
1320     QList<int> validcontainmentids;
1321 
1322     if (isValid(containmentid)) {
1323         validcontainmentids << containmentid;
1324 
1325         //! searching for specific containment and subcontainments and ignore all other containments
1326         for (const auto &cId : containmentGroups.groupList()) {
1327             if (cId.toInt() != containmentid) {
1328                 //! ignore irrelevant containments
1329                 continue;
1330             }
1331 
1332             auto appletGroups = containmentGroups.group(cId).group("Applets");
1333 
1334             for (const auto &appletId : appletGroups.groupList()) {
1335                 KConfigGroup appletCfg = appletGroups.group(appletId);
1336                 if (isSubContainment(appletCfg)) {
1337                     validcontainmentids << subContainmentId(appletCfg);
1338                 }
1339             }
1340         }
1341     }
1342 
1343     //! cycle through valid contaiments in order to retrieve their metadata
1344     for (const auto &cId : containmentGroups.groupList()) {
1345         if (validcontainmentids.count()>0 && !validcontainmentids.contains(cId.toInt())) {
1346             //! searching only for valid containments
1347             continue;
1348         }
1349 
1350         auto appletGroups = containmentGroups.group(cId).group("Applets");
1351 
1352         for (const auto &appletId : appletGroups.groupList()) {
1353             KConfigGroup appletCfg = appletGroups.group(appletId);
1354             QString pluginId = appletCfg.readEntry("plugin", "");
1355 
1356             if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1357                 Data::Applet appletdata = metadata(pluginId);
1358 
1359                 if (appletdata.isInstalled()) {
1360                     knownapplets.insertBasedOnName(appletdata);
1361                 } else if (appletdata.isValid()) {
1362                     unknownapplets.insertBasedOnName(appletdata);
1363                 }
1364             }
1365         }
1366     }
1367 
1368     knownapplets << unknownapplets;
1369 
1370     return knownapplets;
1371 }
1372 
1373 //! Views Data
1374 
syncContainmentConfig(Plasma::Containment * containment)1375 void Storage::syncContainmentConfig(Plasma::Containment *containment)
1376 {
1377     if (!containment) {
1378         return;
1379     }
1380 
1381     for(auto applet: containment->applets()) {
1382         KConfigGroup appletGeneralConfig = applet->config().group("General");
1383 
1384         if (appletGeneralConfig.exists()) {
1385             appletGeneralConfig.sync();
1386         }
1387 
1388         applet->config().sync();
1389     }
1390 
1391     containment->config().sync();
1392 }
1393 
containsView(const QString & filepath,const int & viewId)1394 bool Storage::containsView(const QString &filepath, const int &viewId)
1395 {
1396     KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1397     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1398     KConfigGroup viewGroup = containmentGroups.group(QString::number(viewId));
1399     return viewGroup.exists() && isLatteContainment(viewGroup);
1400 }
1401 
hasContainment(const Layout::GenericLayout * layout,const int & id)1402 bool Storage::hasContainment(const Layout::GenericLayout *layout, const int &id)
1403 {
1404     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1405         return false;
1406     }
1407 
1408     if (layout->isActive()) { // active layout
1409         for(const auto containment : *layout->containments()) {
1410             if ((int)containment->id() == id) {
1411                 return true;
1412             }
1413         }
1414     } else { // inactive layout
1415         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1416         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1417 
1418         //! create warning data
1419         for (const auto &cid : containmentsEntries.groupList()) {
1420             if (cid.toInt() == id) {
1421                 return true;
1422             }
1423         }
1424     }
1425 
1426     return false;
1427 }
1428 
1429 
subcontainments(const Layout::GenericLayout * layout,const Plasma::Containment * lattecontainment) const1430 Data::GenericTable<Data::Generic> Storage::subcontainments(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment) const
1431 {
1432     Data::GenericTable<Data::Generic> subs;
1433 
1434     if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1435         return subs;
1436     }
1437 
1438     for (const auto containment : (*layout->containments())) {
1439         if (containment == lattecontainment) {
1440             continue;
1441         }
1442 
1443         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1444 
1445         //! add subcontainments for that lattecontainment
1446         if (parentApplet && parentApplet->containment() && parentApplet->containment() == lattecontainment) {
1447             Data::Generic subdata;
1448             subdata.id = QString::number(containment->id());
1449             subs << subdata;
1450         }
1451     }
1452 
1453     return subs;
1454 }
1455 
subcontainments(const KConfigGroup & containmentGroup)1456 Data::GenericTable<Data::Generic> Storage::subcontainments(const KConfigGroup &containmentGroup)
1457 {
1458     Data::GenericTable<Data::Generic> subs;
1459 
1460     if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1461         return subs;
1462     }
1463 
1464     auto applets = containmentGroup.group("Applets");
1465 
1466     for (const auto &applet : applets.groupList()) {
1467         if (isSubContainment(applets.group(applet))) {
1468             Data::Generic subdata;
1469             subdata.id = QString::number(subContainmentId(applets.group(applet)));
1470             subs << subdata;
1471         }
1472     }
1473 
1474     return subs;
1475 }
1476 
view(const Layout::GenericLayout * layout,const Plasma::Containment * lattecontainment)1477 Data::View Storage::view(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment)
1478 {
1479     Data::View vdata;
1480 
1481     if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1482         return vdata;
1483     }
1484 
1485     vdata = view(lattecontainment->config());
1486 
1487     vdata.screen = lattecontainment->screen();
1488     if (!isValid(vdata.screen)) {
1489         vdata.screen = lattecontainment->lastScreen();
1490     }
1491 
1492     vdata.subcontainments = subcontainments(layout, lattecontainment);
1493 
1494     return vdata;
1495 }
1496 
view(const KConfigGroup & containmentGroup)1497 Data::View Storage::view(const KConfigGroup &containmentGroup)
1498 {
1499     Data::View vdata;
1500 
1501     if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1502         return vdata;
1503     }
1504 
1505     vdata.id = containmentGroup.name();
1506     vdata.name = containmentGroup.readEntry("name", QString());
1507     vdata.isActive = false;
1508     vdata.onPrimary = containmentGroup.readEntry("onPrimary", true);
1509     vdata.screen = containmentGroup.readEntry("lastScreen", IDNULL);
1510     vdata.screenEdgeMargin = containmentGroup.group("General").readEntry("screenEdgeMargin", (int)-1);
1511 
1512     int location = containmentGroup.readEntry("location", (int)Plasma::Types::BottomEdge);
1513     vdata.edge = (Plasma::Types::Location)location;
1514 
1515     vdata.maxLength = containmentGroup.group("General").readEntry("maxLength", (float)100.0);
1516 
1517     int alignment = containmentGroup.group("General").readEntry("alignment", (int)Latte::Types::Center) ;
1518     vdata.alignment = (Latte::Types::Alignment)alignment;
1519 
1520     vdata.subcontainments = subcontainments(containmentGroup);
1521     vdata.setState(Data::View::IsCreated);
1522 
1523     return vdata;
1524 }
1525 
updateView(KConfigGroup viewGroup,const Data::View & viewData)1526 void Storage::updateView(KConfigGroup viewGroup, const Data::View &viewData)
1527 {
1528     if (!Layouts::Storage::self()->isLatteContainment(viewGroup)) {
1529         return;
1530     }
1531 
1532     viewGroup.writeEntry("name", viewData.name);
1533     viewGroup.writeEntry("onPrimary", viewData.onPrimary);
1534     viewGroup.writeEntry("lastScreen", viewData.screen);
1535     viewGroup.group("General").writeEntry("screenEdgeMargin", viewData.screenEdgeMargin);
1536     viewGroup.writeEntry("location", (int)viewData.edge);
1537     viewGroup.writeEntry("maxLength", viewData.maxLength);
1538     viewGroup.group("General").writeEntry("alignment", (int)viewData.alignment);
1539     viewGroup.sync();
1540 }
1541 
updateView(const Layout::GenericLayout * layout,const Data::View & viewData)1542 void Storage::updateView(const Layout::GenericLayout *layout, const Data::View &viewData)
1543 {
1544     if (!layout) {
1545         return;
1546     }
1547 
1548     auto view = layout->viewForContainment(viewData.id.toUInt());
1549 
1550     if (view) {
1551         qDebug() << "Storage::updateView should not be called because view is active and present...";
1552         return;
1553     }
1554 
1555     if (layout->isActive()) {
1556         //! active view but is not present in active screens;
1557         auto containment = layout->containmentForId(viewData.id.toUInt());
1558         if (containment) {
1559             //! update containment
1560             containment->setLocation(viewData.edge);
1561             updateView(containment->config(), viewData);
1562         }
1563     } else {
1564         //! inactive view and in layout storage
1565         KSharedConfigPtr lFile = KSharedConfig::openConfig(layout->file());
1566         KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1567         KConfigGroup viewContainment = containmentGroups.group(viewData.id);
1568 
1569         if (viewContainment.exists() && Layouts::Storage::self()->isLatteContainment(viewContainment)) {
1570             updateView(viewContainment, viewData);
1571         }
1572     }
1573 }
1574 
removeView(const QString & filepath,const Data::View & viewData)1575 void Storage::removeView(const QString &filepath, const Data::View &viewData)
1576 {
1577     if (!viewData.isValid()) {
1578         return;
1579     }
1580 
1581     removeContainment(filepath, viewData.id);
1582 
1583     for (int i=0; i<viewData.subcontainments.rowCount(); ++i) {
1584         removeContainment(filepath, viewData.subcontainments[i].id);
1585     }
1586 }
1587 
removeContainment(const QString & filepath,const QString & containmentId)1588 void Storage::removeContainment(const QString &filepath, const QString &containmentId)
1589 {
1590     if (containmentId.isEmpty()) {
1591         return;
1592     }
1593 
1594     KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1595     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1596 
1597     if (!containmentGroups.group(containmentId).exists()) {
1598         return;
1599     }
1600 
1601     containmentGroups.group(containmentId).deleteGroup();
1602     lFile->reparseConfiguration();
1603 }
1604 
storedView(const Layout::GenericLayout * layout,const int & containmentId)1605 QString Storage::storedView(const Layout::GenericLayout *layout, const int &containmentId)
1606 {
1607     //! make sure that layout and containmentId are valid
1608     if (!layout) {
1609         return QString();
1610     }
1611 
1612     if (layout->isActive()) {
1613         auto containment = layout->containmentForId((uint)containmentId);
1614         if (!containment || !isLatteContainment(containment)) {
1615             return QString();
1616         }
1617     } else {
1618         if (!containsView(layout->file(), containmentId)) {
1619             return QString();
1620         }
1621     }
1622 
1623     //! at this point we are sure that both layout and containmentId are acceptable
1624     QString nextTmpStoredViewAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(layout->name()).fileName() + "." + QString::number(containmentId) + ".stored.tmp";
1625 
1626     QFile tempStoredViewFile(nextTmpStoredViewAbsolutePath);
1627 
1628     if (tempStoredViewFile.exists()) {
1629         tempStoredViewFile.remove();
1630     }
1631 
1632     KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(nextTmpStoredViewAbsolutePath);
1633     KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
1634 
1635     if (layout->isActive()) {
1636         //! update and copy containments
1637         auto containment = layout->containmentForId((uint)containmentId);
1638         syncContainmentConfig(containment);
1639 
1640         KConfigGroup destinationViewContainment(&destinationContainments, QString::number(containment->id()));
1641         containment->config().copyTo(&destinationViewContainment);
1642 
1643         QList<Plasma::Containment *> subconts = layout->subContainmentsOf(containment->id());
1644 
1645         for(const auto subcont : subconts) {
1646             syncContainmentConfig(subcont);
1647             KConfigGroup destinationsubcontainment(&destinationContainments, QString::number(subcont->id()));
1648             subcont->config().copyTo(&destinationsubcontainment);
1649         }
1650 
1651         //! update with latest view data if active view is present
1652         auto view = layout->viewForContainment(containment);
1653 
1654         if (view) {
1655             Data::View currentviewdata = view->data();
1656             updateView(destinationViewContainment, currentviewdata);
1657         }
1658     } else {
1659         QString containmentid = QString::number(containmentId);
1660         KConfigGroup destinationViewContainment(&destinationContainments, containmentid);
1661 
1662         KSharedConfigPtr originPtr = KSharedConfig::openConfig(layout->file());
1663         KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
1664 
1665         originContainments.group(containmentid).copyTo(&destinationViewContainment);
1666 
1667         Data::GenericTable<Data::Generic> subconts = subcontainments(originContainments.group(containmentid));
1668 
1669         for(int i=0; i<subconts.rowCount(); ++i) {
1670             QString subid = subconts[i].id;
1671             KConfigGroup destinationsubcontainment(&destinationContainments, subid);
1672             originContainments.group(subid).copyTo(&destinationsubcontainment);
1673         }
1674     }
1675 
1676     destinationPtr->reparseConfiguration();
1677     return nextTmpStoredViewAbsolutePath;
1678 }
1679 
views(const Layout::GenericLayout * layout)1680 Data::ViewsTable Storage::views(const Layout::GenericLayout *layout)
1681 {
1682     Data::ViewsTable vtable;
1683 
1684     if (!layout) {
1685         return vtable;
1686     } else if (!layout->isActive()) {
1687         return views(layout->file());
1688     }
1689 
1690     for (const auto containment : (*layout->containments())) {
1691         if (!isLatteContainment(containment)) {
1692             continue;
1693         }
1694 
1695         Latte::View *vw = layout->viewForContainment(containment);
1696 
1697         if (vw) {
1698             vtable << vw->data();
1699         } else {
1700             vtable << view(layout, containment);
1701         }
1702     }
1703 
1704     return vtable;
1705 }
1706 
views(const QString & file)1707 Data::ViewsTable Storage::views(const QString &file)
1708 {
1709     Data::ViewsTable vtable;
1710 
1711     KSharedConfigPtr lFile = KSharedConfig::openConfig(file);
1712     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1713 
1714     for (const auto &cId : containmentGroups.groupList()) {
1715         if (Layouts::Storage::self()->isLatteContainment(containmentGroups.group(cId))) {
1716             vtable << view(containmentGroups.group(cId));
1717         }
1718     }
1719 
1720     return vtable;
1721 }
1722 
1723 }
1724 }
1725