Akonadi

specialcollections.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Constantin Berzan <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "specialcollections.h"
8 #include "akonadicore_debug.h"
9 #include "specialcollectionattribute.h"
10 #include "specialcollections_p.h"
11 
12 #include "agentinstance.h"
13 #include "agentmanager.h"
14 #include "collectionfetchjob.h"
15 #include "collectionfetchscope.h"
16 #include "collectionmodifyjob.h"
17 #include "monitor.h"
18 
19 #include <KCoreConfigSkeleton>
20 
21 #include <QHash>
22 
23 using namespace Akonadi;
24 
25 SpecialCollectionsPrivate::SpecialCollectionsPrivate(KCoreConfigSkeleton *settings, SpecialCollections *qq)
26  : q(qq)
27  , mSettings(settings)
28  , mBatchMode(false)
29 {
30  mMonitor = new Monitor(q);
31  mMonitor->setObjectName(QStringLiteral("SpecialCollectionsMonitor"));
32  mMonitor->fetchCollectionStatistics(true);
33 
34  /// In order to know if items are added or deleted
35  /// from one of our specialcollection folders,
36  /// we have to watch all mail item add/move/delete notifications
37  /// and check for the parent to see if it is one we care about
38  QObject::connect(mMonitor, &Monitor::collectionRemoved, q, [this](const Akonadi::Collection &col) {
39  collectionRemoved(col);
40  });
42  collectionStatisticsChanged(id, statistics);
43  });
44 }
45 
46 SpecialCollectionsPrivate::~SpecialCollectionsPrivate()
47 {
48 }
49 
50 QString SpecialCollectionsPrivate::defaultResourceId() const
51 {
52  if (mDefaultResourceId.isEmpty()) {
53  mSettings->load();
54  const KConfigSkeletonItem *item = mSettings->findItem(QStringLiteral("DefaultResourceId"));
55  Q_ASSERT(item);
56 
57  mDefaultResourceId = item->property().toString();
58  }
59  return mDefaultResourceId;
60 }
61 
62 void SpecialCollectionsPrivate::emitChanged(const QString &resourceId)
63 {
64  if (mBatchMode) {
65  mToEmitChangedFor.insert(resourceId);
66  } else {
67  qCDebug(AKONADICORE_LOG) << "Emitting changed for" << resourceId;
68  const AgentInstance agentInstance = AgentManager::self()->instance(resourceId);
69  Q_EMIT q->collectionsChanged(agentInstance);
70  // first compare with local value then with config value (which also updates the local value)
71  if (resourceId == mDefaultResourceId || resourceId == defaultResourceId()) {
72  qCDebug(AKONADICORE_LOG) << "Emitting defaultFoldersChanged.";
73  Q_EMIT q->defaultCollectionsChanged();
74  }
75  }
76 }
77 
78 void SpecialCollectionsPrivate::collectionRemoved(const Collection &collection)
79 {
80  qCDebug(AKONADICORE_LOG) << "Collection" << collection.id() << "resource" << collection.resource();
81  if (mFoldersForResource.contains(collection.resource())) {
82  // Retrieve the list of special folders for the resource the collection belongs to
83  QHash<QByteArray, Collection> &folders = mFoldersForResource[collection.resource()];
84  {
86  while (it.hasNext()) {
87  it.next();
88  if (it.value() == collection) {
89  // The collection to be removed is a special folder
90  it.remove();
91  emitChanged(collection.resource());
92  }
93  }
94  }
95 
96  if (folders.isEmpty()) {
97  // This resource has no more folders, so remove it completely.
98  mFoldersForResource.remove(collection.resource());
99  }
100  }
101 }
102 
103 void SpecialCollectionsPrivate::collectionStatisticsChanged(Akonadi::Collection::Id collectionId, const Akonadi::CollectionStatistics &statistics)
104 {
105  // need to get the name of the collection in order to be able to check if we are storing it,
106  // but we have the id from the monitor, so fetch the name.
108  fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::None);
109  fetchJob->setProperty("statistics", QVariant::fromValue(statistics));
110 
111  q->connect(fetchJob, &CollectionFetchJob::result, q, [this](KJob *job) {
112  collectionFetchJobFinished(job);
113  });
114 }
115 
116 void SpecialCollectionsPrivate::collectionFetchJobFinished(KJob *job)
117 {
118  if (job->error()) {
119  qCWarning(AKONADICORE_LOG) << "Error fetching collection to get name from id for statistics updating in specialcollections!";
120  return;
121  }
122 
123  const Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
124 
125  Q_ASSERT(!fetchJob->collections().empty());
126  const Akonadi::Collection collection = fetchJob->collections().at(0);
127  const auto statistics = fetchJob->property("statistics").value<Akonadi::CollectionStatistics>();
128 
129  mFoldersForResource[collection.resource()][collection.name().toUtf8()].setStatistics(statistics);
130 }
131 
132 void SpecialCollectionsPrivate::beginBatchRegister()
133 {
134  Q_ASSERT(!mBatchMode);
135  mBatchMode = true;
136  Q_ASSERT(mToEmitChangedFor.isEmpty());
137 }
138 
139 void SpecialCollectionsPrivate::endBatchRegister()
140 {
141  Q_ASSERT(mBatchMode);
142  mBatchMode = false;
143 
144  for (const QString &resourceId : std::as_const(mToEmitChangedFor)) {
145  emitChanged(resourceId);
146  }
147 
148  mToEmitChangedFor.clear();
149 }
150 
151 void SpecialCollectionsPrivate::forgetFoldersForResource(const QString &resourceId)
152 {
153  if (mFoldersForResource.contains(resourceId)) {
154  const auto folders = mFoldersForResource[resourceId];
155  for (const auto &collection : folders) {
156  mMonitor->setCollectionMonitored(collection, false);
157  }
158 
159  mFoldersForResource.remove(resourceId);
160  emitChanged(resourceId);
161  }
162 }
163 
164 AgentInstance SpecialCollectionsPrivate::defaultResource() const
165 {
166  const QString identifier = defaultResourceId();
167  return AgentManager::self()->instance(identifier);
168 }
169 
171  : QObject(parent)
172  , d(new SpecialCollectionsPrivate(settings, this))
173 {
174 }
175 
177 
178 bool SpecialCollections::hasCollection(const QByteArray &type, const AgentInstance &instance) const
179 {
180  return d->mFoldersForResource.value(instance.identifier()).contains(type);
181 }
182 
184 {
185  return d->mFoldersForResource.value(instance.identifier()).value(type);
186 }
187 
189 {
191  Collection attributeCollection(collection);
192  auto attribute = attributeCollection.attribute<SpecialCollectionAttribute>(Collection::AddIfMissing);
193  attribute->setCollectionType(type);
194  new CollectionModifyJob(attributeCollection);
195  }
196 }
197 
199 {
201  Collection attributeCollection(collection);
202  attributeCollection.removeAttribute<SpecialCollectionAttribute>();
203  new CollectionModifyJob(attributeCollection);
204  }
205 }
206 
208 {
209  if (!collection.isValid()) {
210  qCWarning(AKONADICORE_LOG) << "Invalid collection.";
211  return false;
212  }
213 
214  const QString &resourceId = collection.resource();
215  if (resourceId.isEmpty()) {
216  qCWarning(AKONADICORE_LOG) << "Collection has empty resourceId.";
217  return false;
218  }
219 
221 
222  d->mMonitor->setCollectionMonitored(collection, false);
223  // Remove from list of collection
224  d->collectionRemoved(collection);
225  return true;
226 }
227 
229 {
230  if (!collection.isValid()) {
231  qCWarning(AKONADICORE_LOG) << "Invalid collection.";
232  return false;
233  }
234 
235  const QString &resourceId = collection.resource();
236  if (resourceId.isEmpty()) {
237  qCWarning(AKONADICORE_LOG) << "Collection has empty resourceId.";
238  return false;
239  }
240 
242 
243  const Collection oldCollection = d->mFoldersForResource.value(resourceId).value(type);
244  if (oldCollection != collection) {
245  if (oldCollection.isValid()) {
246  d->mMonitor->setCollectionMonitored(oldCollection, false);
247  }
248  d->mMonitor->setCollectionMonitored(collection, true);
249  d->mFoldersForResource[resourceId].insert(type, collection);
250  d->emitChanged(resourceId);
251  }
252 
253  return true;
254 }
255 
257 {
258  return hasCollection(type, d->defaultResource());
259 }
260 
262 {
263  return collection(type, d->defaultResource());
264 }
265 
266 #include "moc_specialcollections.cpp"
QString identifier() const
Returns the unique identifier of the agent instance.
Provides statistics information of a Collection.
bool hasDefaultCollection(const QByteArray &type) const
Returns whether the default resource has a special collection of the given type.
bool registerCollection(const QByteArray &type, const Akonadi::Collection &collection)
Registers the given collection as a special collection with the given type.
void collectionRemoved(const Akonadi::Collection &collection)
This signal is emitted if a monitored collection has been removed from the Akonadi storage.
virtual QVariant property() const=0
Job that modifies a collection in the Akonadi storage.
QVariant fromValue(const T &value)
void result(KJob *job)
T value() const const
void removeAttribute(const QByteArray &name)
Removes and deletes the attribute of the given type name.
Definition: collection.cpp:156
Represents a collection of PIM items.
Definition: collection.h:61
QByteArray collectionType() const
Returns the special collections type of the collection.
Monitors an item or collection for changes.
Definition: monitor.h:71
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
SpecialCollections(KCoreConfigSkeleton *config, QObject *parent=nullptr)
Creates a new special collections object.
Job that fetches collections from the Akonadi storage.
Attribute * attribute(const QByteArray &name)
Returns the attribute of the given type name if available, 0 otherwise.
Definition: collection.cpp:176
Collection::List collections() const
Returns the list of fetched collection.
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
QAction * statistics(const QObject *recvr, const char *slot, QObject *parent)
~SpecialCollections() override
Destroys the special collections object.
const T & at(int i) const const
void setCollectionType(const QByteArray &type)
Sets the special collections type of the collection.
bool isEmpty() const const
QByteArray toUtf8() const const
bool hasAttribute(const QByteArray &name) const
Returns true if the collection has an attribute of the given type name, false otherwise.
Definition: collection.cpp:161
An Attribute that stores the special collection type of a collection.
@ Base
Only fetch the base collection.
Akonadi::Collection collection(const QByteArray &type, const AgentInstance &instance) const
Returns the special collection of the given type in the given agent instance, or an invalid collectio...
An interface to special collections.
int remove(const Key &key)
QString & insert(int position, QChar ch)
bool isEmpty() const const
bool hasCollection(const QByteArray &type, const AgentInstance &instance) const
Returns whether the given agent instance has a special collection of the given type.
void collectionStatisticsChanged(Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistics)
This signal is emitted if the statistics information of a monitored collection has changed.
static AgentManager * self()
Returns the global instance of the agent manager.
@ AddIfMissing
Creates the attribute if it is missing.
Definition: collection.h:281
static void unsetSpecialCollection(const Akonadi::Collection &collection)
unsets the special collection attribute which marks collection as being a special collection.
@ None
No ancestor retrieval at all (the default)
int error() const
bool unregisterCollection(const Collection &collection)
Unregisters the given collection as a special collection.
A representation of an agent instance.
Akonadi::Collection defaultCollection(const QByteArray &type) const
Returns the special collection of given type in the default resource, or an invalid collection if suc...
qint64 Id
Describes the unique id type.
Definition: collection.h:79
static void setSpecialCollectionType(const QByteArray &type, const Akonadi::Collection &collection)
Sets the special collection attribute which marks collection as being a special collection of type ty...
bool empty() const const
QString toString() const const
QVariant property(const char *name) const const
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 03:52:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.