Akonadi

tagsync.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Christian Mollekopf <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 namespace Akonadi
7 {
8 class Item;
9 }
10 
11 #include "tagsync.h"
12 #include "akonadicore_debug.h"
13 #include "itemfetchscope.h"
14 #include "jobs/itemfetchjob.h"
15 #include "jobs/itemmodifyjob.h"
16 #include "jobs/tagcreatejob.h"
17 #include "jobs/tagfetchjob.h"
18 #include "jobs/tagmodifyjob.h"
19 #include "tagfetchscope.h"
20 
21 using namespace Akonadi;
22 
23 bool operator==(const Item &left, const Item &right)
24 {
25  if (left.isValid() && right.isValid() && (left.id() == right.id())) {
26  return true;
27  }
28  if (!left.remoteId().isEmpty() && !right.remoteId().isEmpty() && (left.remoteId() == right.remoteId())) {
29  return true;
30  }
31  if (!left.gid().isEmpty() && !right.gid().isEmpty() && (left.gid() == right.gid())) {
32  return true;
33  }
34  return false;
35 }
36 
37 TagSync::TagSync(QObject *parent)
38  : Job(parent)
39 {
40 }
41 
42 TagSync::~TagSync()
43 {
44 }
45 
46 void TagSync::setFullTagList(const Akonadi::Tag::List &tags)
47 {
48  mRemoteTags = tags;
49  mDeliveryDone = true;
50  diffTags();
51 }
52 
53 void TagSync::setTagMembers(const QHash<QString, Akonadi::Item::List> &ridMemberMap)
54 {
55  mRidMemberMap = ridMemberMap;
56  mTagMembersDeliveryDone = true;
57  diffTags();
58 }
59 
60 void TagSync::doStart()
61 {
62  // qCDebug(AKONADICORE_LOG);
63  // This should include all tags, including the ones that don't have a remote id
64  auto fetch = new Akonadi::TagFetchJob(this);
65  fetch->fetchScope().setFetchRemoteId(true);
66  connect(fetch, &KJob::result, this, &TagSync::onLocalTagFetchDone);
67 }
68 
69 void TagSync::onLocalTagFetchDone(KJob *job)
70 {
71  // qCDebug(AKONADICORE_LOG);
72  auto fetch = static_cast<TagFetchJob *>(job);
73  mLocalTags = fetch->tags();
74  mLocalTagsFetched = true;
75  diffTags();
76 }
77 
78 void TagSync::diffTags()
79 {
80  if (!mDeliveryDone || !mTagMembersDeliveryDone || !mLocalTagsFetched) {
81  qCDebug(AKONADICORE_LOG) << "waiting for delivery: " << mDeliveryDone << mLocalTagsFetched;
82  return;
83  }
84  // qCDebug(AKONADICORE_LOG) << "diffing";
88  for (const Akonadi::Tag &localTag : std::as_const(mLocalTags)) {
89  tagByRid.insert(localTag.remoteId(), localTag);
90  tagByGid.insert(localTag.gid(), localTag);
91  if (!localTag.remoteId().isEmpty()) {
92  tagById.insert(localTag.id(), localTag);
93  }
94  }
95  for (const Akonadi::Tag &remoteTag : std::as_const(mRemoteTags)) {
96  if (tagByRid.contains(remoteTag.remoteId())) {
97  // Tag still exists, check members
98  Tag tag = tagByRid.value(remoteTag.remoteId());
99  auto itemFetch = new ItemFetchJob(tag, this);
100  itemFetch->setProperty("tag", QVariant::fromValue(tag));
101  itemFetch->setProperty("merge", false);
102  itemFetch->fetchScope().setFetchGid(true);
103  connect(itemFetch, &KJob::result, this, &TagSync::onTagItemsFetchDone);
104  connect(itemFetch, &KJob::result, this, &TagSync::onJobDone);
105  tagById.remove(tagByRid.value(remoteTag.remoteId()).id());
106  } else if (tagByGid.contains(remoteTag.gid())) {
107  // Tag exists but has no rid
108  // Merge members and set rid
109  Tag tag = tagByGid.value(remoteTag.gid());
110  tag.setRemoteId(remoteTag.remoteId());
111  auto itemFetch = new ItemFetchJob(tag, this);
112  itemFetch->setProperty("tag", QVariant::fromValue(tag));
113  itemFetch->setProperty("merge", true);
114  itemFetch->fetchScope().setFetchGid(true);
115  connect(itemFetch, &KJob::result, this, &TagSync::onTagItemsFetchDone);
116  connect(itemFetch, &KJob::result, this, &TagSync::onJobDone);
117  tagById.remove(tagByGid.value(remoteTag.gid()).id());
118  } else {
119  // New tag, create
120  auto createJob = new TagCreateJob(remoteTag, this);
121  createJob->setMergeIfExisting(true);
122  connect(createJob, &KJob::result, this, &TagSync::onCreateTagDone);
123  connect(createJob, &KJob::result, this, &TagSync::onJobDone);
124  }
125  }
126  for (const Tag &tag : std::as_const(tagById)) {
127  // Removed remotely, unset rid
128  Tag copy(tag);
129  copy.setRemoteId(QByteArray(""));
130  auto modJob = new TagModifyJob(copy, this);
131  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
132  }
133  checkDone();
134 }
135 
136 void TagSync::onCreateTagDone(KJob *job)
137 {
138  if (job->error()) {
139  qCWarning(AKONADICORE_LOG) << "ItemFetch failed: " << job->errorString();
140  return;
141  }
142 
143  Akonadi::Tag tag = static_cast<Akonadi::TagCreateJob *>(job)->tag();
144  const Item::List remoteMembers = mRidMemberMap.value(QString::fromLatin1(tag.remoteId()));
145  for (Item item : remoteMembers) {
146  item.setTag(tag);
147  auto modJob = new ItemModifyJob(item, this);
148  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
149  qCDebug(AKONADICORE_LOG) << "setting tag " << item.remoteId();
150  }
151 }
152 
153 static bool containsByGidOrRid(const Item::List &items, const Item &key)
154 {
155  return std::any_of(items.cbegin(), items.cend(), [&key](const Item &item) {
156  return ((!item.gid().isEmpty() && !key.gid().isEmpty()) && (item.gid() == key.gid())) || (item.remoteId() == key.remoteId());
157  });
158 }
159 
160 void TagSync::onTagItemsFetchDone(KJob *job)
161 {
162  if (job->error()) {
163  qCWarning(AKONADICORE_LOG) << "ItemFetch failed: " << job->errorString();
164  return;
165  }
166 
167  const Akonadi::Item::List items = static_cast<Akonadi::ItemFetchJob *>(job)->items();
168  const auto tag = job->property("tag").value<Akonadi::Tag>();
169  const bool merge = job->property("merge").toBool();
170  const Item::List remoteMembers = mRidMemberMap.value(QString::fromLatin1(tag.remoteId()));
171 
172  // add = remote - local
173  Item::List toAdd;
174  for (const Item &remote : remoteMembers) {
175  if (!containsByGidOrRid(items, remote)) {
176  toAdd << remote;
177  }
178  }
179 
180  // remove = local - remote
181  Item::List toRemove;
182  for (const Item &local : items) {
183  // Skip items that have no remote id yet
184  // Trying to them will only result in a conflict
185  if (local.remoteId().isEmpty()) {
186  continue;
187  }
188  if (!containsByGidOrRid(remoteMembers, local)) {
189  toRemove << local;
190  }
191  }
192 
193  if (!merge) {
194  for (Item item : std::as_const(toRemove)) {
195  item.clearTag(tag);
196  auto modJob = new ItemModifyJob(item, this);
197  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
198  qCDebug(AKONADICORE_LOG) << "removing tag " << item.remoteId();
199  }
200  }
201  for (Item item : std::as_const(toAdd)) {
202  item.setTag(tag);
203  auto modJob = new ItemModifyJob(item, this);
204  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
205  qCDebug(AKONADICORE_LOG) << "setting tag " << item.remoteId();
206  }
207 }
208 
209 void TagSync::onJobDone(KJob * /*unused*/)
210 {
211  checkDone();
212 }
213 
214 void TagSync::slotResult(KJob *job)
215 {
216  if (job->error()) {
217  qCWarning(AKONADICORE_LOG) << "Error during TagSync: " << job->errorString() << job->metaObject()->className();
218  // pretend there were no errors
220  } else {
221  Akonadi::Job::slotResult(job);
222  }
223 }
224 
225 void TagSync::checkDone()
226 {
227  if (hasSubjobs()) {
228  return;
229  }
230  qCDebug(AKONADICORE_LOG) << "done";
231  emitResult();
232 }
QTextStream & right(QTextStream &stream)
const T value(const Key &key) const const
QVariant fromValue(const T &value)
void result(KJob *job)
An Akonadi Tag.
Definition: tag.h:25
T value() const const
QTextStream & left(QTextStream &stream)
Tag::List tags() const
Returns the fetched tags after the job has been completed.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVector< Item > List
Describes a list of items.
Definition: item.h:115
QHash::iterator insert(const Key &key, const T &value)
T value(int i) const const
bool removeSubjob(KJob *job) override
Removes the given subjob of this job.
Definition: job.cpp:369
Job that fetches tags from the Akonadi storage.
Definition: tagfetchjob.h:28
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:81
Base class for all actions in the Akonadi storage.
Definition: job.h:80
QVariant merge(const QVariant &lhs, const QVariant &rhs)
bool toBool() const const
virtual const QMetaObject * metaObject() const const
const char * className() const const
int remove(const Key &key)
QString fromLatin1(const char *str, int size)
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:69
virtual QString errorString() const
int error() const
bool contains(const Key &key) const const
const QList< QKeySequence > & copy()
Represents a PIM item stored in Akonadi storage.
Definition: item.h:104
QVariant property(const char *name) const const
Helper integration between Akonadi and Qt.
Job that creates a new tag in the Akonadi storage.
Definition: tagcreatejob.h:21
Job that modifies a tag in the Akonadi storage.
Definition: tagmodifyjob.h:21
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:00:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.