1 /*
2 SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "kolabresource.h"
8
9 #include "kolabresource_debug.h"
10 #include "kolabresource_trace.h"
11 #include "sessionpool.h"
12 #include "sessionuiproxy.h"
13 #include "settingspasswordrequester.h"
14 #include "setupserver.h"
15
16 #include <Akonadi/Calendar/BlockAlarmsAttribute>
17
18 #include <Akonadi/AttributeFactory>
19 #include <Akonadi/CollectionColorAttribute>
20
21 #include <changecollectiontask.h>
22 #include <collectionannotationsattribute.h>
23 #include <resourcestateinterface.h>
24 #include <retrieveitemstask.h>
25
26 #include <KLocalizedString>
27 #include <KWindowSystem>
28 #include <QIcon>
29
30 #include "kolabaddtagtask.h"
31 #include "kolabchangeitemsrelationstask.h"
32 #include "kolabchangeitemstagstask.h"
33 #include "kolabchangetagtask.h"
34 #include "kolabhelpers.h"
35 #include "kolabremovetagtask.h"
36 #include "kolabresourcestate.h"
37 #include "kolabretrievecollectionstask.h"
38 #include "kolabretrievetagstask.h"
39 #include "kolabsettings.h"
40
KolabResource(const QString & id)41 KolabResource::KolabResource(const QString &id)
42 : ImapResourceBase(id)
43 {
44 m_pool->setPasswordRequester(new SettingsPasswordRequester(this, m_pool));
45 m_pool->setSessionUiProxy(SessionUiProxy::Ptr(new SessionUiProxy));
46 m_pool->setClientId(clientId());
47
48 Akonadi::AttributeFactory::registerAttribute<Akonadi::CollectionColorAttribute>();
49 // Ensure we have up-to date metadata before attempting to sync folder
50 setScheduleAttributeSyncBeforeItemSync(true);
51 setKeepLocalCollectionChanges(QSet<QByteArray>() << "ENTITYDISPLAY" << Akonadi::BlockAlarmsAttribute().type());
52
53 settings(); // make sure the D-Bus settings interface is up
54 }
55
~KolabResource()56 KolabResource::~KolabResource()
57 {
58 }
59
settings() const60 Settings *KolabResource::settings() const
61 {
62 if (!m_settings) {
63 m_settings = new KolabSettings;
64 }
65
66 return m_settings;
67 }
68
delayedInit()69 void KolabResource::delayedInit()
70 {
71 ImapResourceBase::delayedInit();
72 settings()->setRetrieveMetadataOnFolderListing(false);
73 Q_ASSERT(!settings()->retrieveMetadataOnFolderListing());
74 }
75
defaultName() const76 QString KolabResource::defaultName() const
77 {
78 return i18n("Kolab Resource");
79 }
80
clientId() const81 QByteArray KolabResource::clientId() const
82 {
83 return QByteArrayLiteral("Kontact Kolab Resource 5/KOLAB");
84 }
85
createConfigureDialog(WId windowId)86 QDialog *KolabResource::createConfigureDialog(WId windowId)
87 {
88 auto dlg = new SetupServer(this, windowId);
89 dlg->setAttribute(Qt::WA_NativeWindow, true);
90 KWindowSystem::setMainWindow(dlg->windowHandle(), windowId);
91 dlg->setWindowTitle(i18nc("@title:window", "Kolab Account Settings"));
92 dlg->setWindowIcon(QIcon::fromTheme(QStringLiteral("kolab")));
93 connect(dlg, &QDialog::finished, this, &KolabResource::onConfigurationDone);
94 return dlg;
95 }
96
onConfigurationDone(int result)97 void KolabResource::onConfigurationDone(int result)
98 {
99 auto dlg = qobject_cast<SetupServer *>(sender());
100 if (result) {
101 if (dlg->shouldClearCache()) {
102 clearCache();
103 }
104 settings()->save();
105 }
106 dlg->deleteLater();
107 }
108
createResourceState(const TaskArguments & args)109 ResourceStateInterface::Ptr KolabResource::createResourceState(const TaskArguments &args)
110 {
111 return ResourceStateInterface::Ptr(new KolabResourceState(this, args));
112 }
113
retrieveCollections()114 void KolabResource::retrieveCollections()
115 {
116 qCDebug(KOLABRESOURCE_TRACE);
117 Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving folders"));
118
119 startTask(new KolabRetrieveCollectionsTask(createResourceState(TaskArguments()), this));
120 synchronizeTags();
121 synchronizeRelations();
122 }
123
itemAdded(const Akonadi::Item & item,const Akonadi::Collection & collection)124 void KolabResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection)
125 {
126 qCDebug(KOLABRESOURCE_TRACE) << item.id() << collection.id();
127 bool ok = true;
128 const Akonadi::Item imapItem = KolabHelpers::translateToImap(item, ok);
129 if (!ok) {
130 qCWarning(KOLABRESOURCE_LOG) << "Failed to convert item";
131 cancelTask();
132 return;
133 }
134 ImapResourceBase::itemAdded(imapItem, collection);
135 }
136
itemChanged(const Akonadi::Item & item,const QSet<QByteArray> & parts)137 void KolabResource::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &parts)
138 {
139 qCDebug(KOLABRESOURCE_TRACE) << item.id() << parts;
140 bool ok = true;
141 const Akonadi::Item imapItem = KolabHelpers::translateToImap(item, ok);
142 if (!ok) {
143 qCWarning(KOLABRESOURCE_LOG) << "Failed to convert item";
144 cancelTask();
145 return;
146 }
147 ImapResourceBase::itemChanged(imapItem, parts);
148 }
149
itemsMoved(const Akonadi::Item::List & items,const Akonadi::Collection & source,const Akonadi::Collection & destination)150 void KolabResource::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination)
151 {
152 qCDebug(KOLABRESOURCE_TRACE) << items.size() << source.id() << destination.id();
153 bool ok = true;
154 const Akonadi::Item::List imapItems = KolabHelpers::translateToImap(items, ok);
155 if (!ok) {
156 qCWarning(KOLABRESOURCE_LOG) << "Failed to convert item";
157 cancelTask();
158 return;
159 }
160 ImapResourceBase::itemsMoved(imapItems, source, destination);
161 }
162
updateAnnotations(const Akonadi::Collection & collection)163 static Akonadi::Collection updateAnnotations(const Akonadi::Collection &collection)
164 {
165 qCDebug(KOLABRESOURCE_TRACE) << collection.id();
166 // Set the annotations on new folders
167 const QByteArray kolabType = KolabHelpers::kolabTypeForMimeType(collection.contentMimeTypes());
168 Akonadi::Collection col = collection;
169 auto attr = col.attribute<Akonadi::CollectionAnnotationsAttribute>(Akonadi::Collection::AddIfMissing);
170 QMap<QByteArray, QByteArray> annotations = attr->annotations();
171
172 bool changed = false;
173 auto colorAttribute = col.attribute<Akonadi::CollectionColorAttribute>();
174 if (colorAttribute) {
175 const QColor color = colorAttribute->color();
176 if (color.isValid()) {
177 KolabHelpers::setFolderColor(annotations, color);
178 changed = true;
179 }
180 }
181
182 if (!kolabType.isEmpty()) {
183 KolabHelpers::setFolderTypeAnnotation(annotations, kolabType);
184 changed = true;
185 }
186
187 if (changed) {
188 attr->setAnnotations(annotations);
189 return col;
190 }
191 return collection;
192 }
193
collectionAdded(const Akonadi::Collection & collection,const Akonadi::Collection & parent)194 void KolabResource::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent)
195 {
196 qCDebug(KOLABRESOURCE_TRACE) << collection.id() << parent.id();
197 // Set the annotations on new folders
198 const Akonadi::Collection col = updateAnnotations(collection);
199 // TODO we need to save the collections as well if the annotations have changed
200 // or we simply don't have the annotations locally, which perhaps is also not required?
201 ImapResourceBase::collectionAdded(col, parent);
202 }
203
collectionChanged(const Akonadi::Collection & collection,const QSet<QByteArray> & parts)204 void KolabResource::collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &parts)
205 {
206 qCDebug(KOLABRESOURCE_TRACE) << collection.id() << parts;
207 QSet<QByteArray> p = parts;
208 // Update annotations if necessary
209 // FIXME col ?????
210 const Akonadi::Collection col = updateAnnotations(collection);
211 if (parts.contains(Akonadi::CollectionColorAttribute().type())) {
212 p << Akonadi::CollectionAnnotationsAttribute().type();
213 }
214
215 // TODO we need to save the collections as well if the annotations have changed
216 Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Updating folder '%1'", collection.name()));
217 auto task = new ChangeCollectionTask(createResourceState(TaskArguments(collection, p)), this);
218 task->syncEnabledState(true);
219 startTask(task);
220 }
221
tagAdded(const Akonadi::Tag & tag)222 void KolabResource::tagAdded(const Akonadi::Tag &tag)
223 {
224 qCDebug(KOLABRESOURCE_TRACE) << tag.id();
225 auto task = new KolabAddTagTask(createResourceState(TaskArguments(tag)), this);
226 startTask(task);
227 }
228
tagChanged(const Akonadi::Tag & tag)229 void KolabResource::tagChanged(const Akonadi::Tag &tag)
230 {
231 qCDebug(KOLABRESOURCE_TRACE) << tag.id();
232 auto task = new KolabChangeTagTask(createResourceState(TaskArguments(tag)), QSharedPointer<TagConverter>(new TagConverter), this);
233 startTask(task);
234 }
235
tagRemoved(const Akonadi::Tag & tag)236 void KolabResource::tagRemoved(const Akonadi::Tag &tag)
237 {
238 qCDebug(KOLABRESOURCE_TRACE) << tag.id();
239 auto task = new KolabRemoveTagTask(createResourceState(TaskArguments(tag)), this);
240 startTask(task);
241 }
242
itemsTagsChanged(const Akonadi::Item::List & items,const QSet<Akonadi::Tag> & addedTags,const QSet<Akonadi::Tag> & removedTags)243 void KolabResource::itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags)
244 {
245 qCDebug(KOLABRESOURCE_TRACE) << items.size() << addedTags.size() << removedTags.size();
246 auto task =
247 new KolabChangeItemsTagsTask(createResourceState(TaskArguments(items, addedTags, removedTags)), QSharedPointer<TagConverter>(new TagConverter), this);
248 startTask(task);
249 }
250
retrieveTags()251 void KolabResource::retrieveTags()
252 {
253 qCDebug(KOLABRESOURCE_TRACE);
254 auto task = new KolabRetrieveTagTask(createResourceState(TaskArguments()), KolabRetrieveTagTask::RetrieveTags, this);
255 startTask(task);
256 }
257
retrieveRelations()258 void KolabResource::retrieveRelations()
259 {
260 qCDebug(KOLABRESOURCE_TRACE);
261 auto task = new KolabRetrieveTagTask(createResourceState(TaskArguments()), KolabRetrieveTagTask::RetrieveRelations, this);
262 startTask(task);
263 }
264
itemsRelationsChanged(const Akonadi::Item::List & items,const Akonadi::Relation::List & addedRelations,const Akonadi::Relation::List & removedRelations)265 void KolabResource::itemsRelationsChanged(const Akonadi::Item::List &items,
266 const Akonadi::Relation::List &addedRelations,
267 const Akonadi::Relation::List &removedRelations)
268 {
269 qCDebug(KOLABRESOURCE_TRACE) << items.size() << addedRelations.size() << removedRelations.size();
270 auto task = new KolabChangeItemsRelationsTask(createResourceState(TaskArguments(items, addedRelations, removedRelations)));
271 startTask(task);
272 }
273
274 AKONADI_RESOURCE_MAIN(KolabResource)
275