Akonadi

tagsync.cpp
1 /*
2  Copyright (c) 2014 Christian Mollekopf <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 namespace Akonadi
20 {
21 class Item;
22 }
23 
24 #include "tagsync.h"
25 #include "akonadicore_debug.h"
26 #include "jobs/itemfetchjob.h"
27 #include "itemfetchscope.h"
28 #include "jobs/itemmodifyjob.h"
29 #include "jobs/tagfetchjob.h"
30 #include "jobs/tagcreatejob.h"
31 #include "jobs/tagmodifyjob.h"
32 #include "tagfetchscope.h"
33 
34 using namespace Akonadi;
35 
36 bool operator==(const Item &left, const Item &right)
37 {
38  if (left.isValid() && right.isValid() && (left.id() == right.id())) {
39  return true;
40  }
41  if (!left.remoteId().isEmpty() && !right.remoteId().isEmpty() && (left.remoteId() == right.remoteId())) {
42  return true;
43  }
44  if (!left.gid().isEmpty() && !right.gid().isEmpty() && (left.gid() == right.gid())) {
45  return true;
46  }
47  return false;
48 }
49 
50 TagSync::TagSync(QObject *parent)
51  : Job(parent),
52  mDeliveryDone(false),
53  mTagMembersDeliveryDone(false),
54  mLocalTagsFetched(false)
55 {
56 
57 }
58 
59 TagSync::~TagSync()
60 {
61 
62 }
63 
64 void TagSync::setFullTagList(const Akonadi::Tag::List &tags)
65 {
66  mRemoteTags = tags;
67  mDeliveryDone = true;
68  diffTags();
69 }
70 
71 void TagSync::setTagMembers(const QHash<QString, Akonadi::Item::List> &ridMemberMap)
72 {
73  mRidMemberMap = ridMemberMap;
74  mTagMembersDeliveryDone = true;
75  diffTags();
76 }
77 
78 void TagSync::doStart()
79 {
80  // qCDebug(AKONADICORE_LOG);
81  //This should include all tags, including the ones that don't have a remote id
82  Akonadi::TagFetchJob *fetch = new Akonadi::TagFetchJob(this);
83  fetch->fetchScope().setFetchRemoteId(true);
84  connect(fetch, &KJob::result, this, &TagSync::onLocalTagFetchDone);
85 }
86 
87 void TagSync::onLocalTagFetchDone(KJob *job)
88 {
89  // qCDebug(AKONADICORE_LOG);
90  TagFetchJob *fetch = static_cast<TagFetchJob *>(job);
91  mLocalTags = fetch->tags();
92  mLocalTagsFetched = true;
93  diffTags();
94 }
95 
96 void TagSync::diffTags()
97 {
98  if (!mDeliveryDone || !mTagMembersDeliveryDone || !mLocalTagsFetched) {
99  qCDebug(AKONADICORE_LOG) << "waiting for delivery: " << mDeliveryDone << mLocalTagsFetched;
100  return;
101  }
102  // qCDebug(AKONADICORE_LOG) << "diffing";
106  Q_FOREACH (const Akonadi::Tag &localTag, mLocalTags) {
107  tagByRid.insert(localTag.remoteId(), localTag);
108  tagByGid.insert(localTag.gid(), localTag);
109  if (!localTag.remoteId().isEmpty()) {
110  tagById.insert(localTag.id(), localTag);
111  }
112  }
113  Q_FOREACH (const Akonadi::Tag &remoteTag, mRemoteTags) {
114  if (tagByRid.contains(remoteTag.remoteId())) {
115  //Tag still exists, check members
116  Tag tag = tagByRid.value(remoteTag.remoteId());
117  ItemFetchJob *itemFetch = new ItemFetchJob(tag, this);
118  itemFetch->setProperty("tag", QVariant::fromValue(tag));
119  itemFetch->setProperty("merge", false);
120  itemFetch->fetchScope().setFetchGid(true);
121  connect(itemFetch, &KJob::result, this, &TagSync::onTagItemsFetchDone);
122  connect(itemFetch, &KJob::result, this, &TagSync::onJobDone);
123  tagById.remove(tagByRid.value(remoteTag.remoteId()).id());
124  } else if (tagByGid.contains(remoteTag.gid())) {
125  //Tag exists but has no rid
126  //Merge members and set rid
127  Tag tag = tagByGid.value(remoteTag.gid());
128  tag.setRemoteId(remoteTag.remoteId());
129  ItemFetchJob *itemFetch = new ItemFetchJob(tag, this);
130  itemFetch->setProperty("tag", QVariant::fromValue(tag));
131  itemFetch->setProperty("merge", true);
132  itemFetch->fetchScope().setFetchGid(true);
133  connect(itemFetch, &KJob::result, this, &TagSync::onTagItemsFetchDone);
134  connect(itemFetch, &KJob::result, this, &TagSync::onJobDone);
135  tagById.remove(tagByGid.value(remoteTag.gid()).id());
136  } else {
137  //New tag, create
138  TagCreateJob *createJob = new TagCreateJob(remoteTag, this);
139  createJob->setMergeIfExisting(true);
140  connect(createJob, &KJob::result, this, &TagSync::onCreateTagDone);
141  connect(createJob, &KJob::result, this, &TagSync::onJobDone);
142  }
143  }
144  Q_FOREACH (const Tag &tag, tagById) {
145  //Removed remotely, unset rid
146  Tag copy(tag);
147  copy.setRemoteId(QByteArray(""));
148  TagModifyJob *modJob = new TagModifyJob(copy, this);
149  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
150  }
151  checkDone();
152 }
153 
154 void TagSync::onCreateTagDone(KJob *job)
155 {
156  if (job->error()) {
157  qCWarning(AKONADICORE_LOG) << "ItemFetch failed: " << job->errorString();
158  return;
159  }
160 
161  Akonadi::Tag tag = static_cast<Akonadi::TagCreateJob *>(job)->tag();
162  const Item::List remoteMembers = mRidMemberMap.value(QString::fromLatin1(tag.remoteId()));
163  for (Item item : remoteMembers) {
164  item.setTag(tag);
165  ItemModifyJob *modJob = new ItemModifyJob(item, this);
166  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
167  qCDebug(AKONADICORE_LOG) << "setting tag " << item.remoteId();
168  }
169 }
170 
171 static bool containsByGidOrRid(const Item::List &items, const Item &key)
172 {
173  for (const Item &item : items) {
174  if ((!item.gid().isEmpty() && !key.gid().isEmpty()) && (item.gid() == key.gid())) {
175  return true;
176  } else if (item.remoteId() == key.remoteId()) {
177  return true;
178  }
179  }
180  return false;
181 }
182 
183 void TagSync::onTagItemsFetchDone(KJob *job)
184 {
185  if (job->error()) {
186  qCWarning(AKONADICORE_LOG) << "ItemFetch failed: " << job->errorString();
187  return;
188  }
189 
190  const Akonadi::Item::List items = static_cast<Akonadi::ItemFetchJob *>(job)->items();
191  const Akonadi::Tag tag = job->property("tag").value<Akonadi::Tag>();
192  const bool merge = job->property("merge").toBool();
193  const Item::List remoteMembers = mRidMemberMap.value(QString::fromLatin1(tag.remoteId()));
194 
195  //add = remote - local
196  Item::List toAdd;
197  for (const Item &remote : remoteMembers) {
198  if (!containsByGidOrRid(items, remote)) {
199  toAdd << remote;
200  }
201  }
202 
203  //remove = local - remote
204  Item::List toRemove;
205  for (const Item &local : items) {
206  //Skip items that have no remote id yet
207  //Trying to them will only result in a conflict
208  if (local.remoteId().isEmpty()) {
209  continue;
210  }
211  if (!containsByGidOrRid(remoteMembers, local)) {
212  toRemove << local;
213  }
214  }
215 
216  if (!merge) {
217  for (Item item : qAsConst(toRemove)) {
218  item.clearTag(tag);
219  ItemModifyJob *modJob = new ItemModifyJob(item, this);
220  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
221  qCDebug(AKONADICORE_LOG) << "removing tag " << item.remoteId();
222  }
223  }
224  for (Item item : qAsConst(toAdd)) {
225  item.setTag(tag);
226  ItemModifyJob *modJob = new ItemModifyJob(item, this);
227  connect(modJob, &KJob::result, this, &TagSync::onJobDone);
228  qCDebug(AKONADICORE_LOG) << "setting tag " << item.remoteId();
229  }
230 }
231 
232 void TagSync::onJobDone(KJob *)
233 {
234  checkDone();
235 }
236 
237 void TagSync::slotResult(KJob *job)
238 {
239  if (job->error()) {
240  qCWarning(AKONADICORE_LOG) << "Error during TagSync: " << job->errorString() << job->metaObject()->className();
241  // pretend there were no errors
243  } else {
244  Akonadi::Job::slotResult(job);
245  }
246 }
247 
248 void TagSync::checkDone()
249 {
250  if (hasSubjobs()) {
251  return;
252  }
253  qCDebug(AKONADICORE_LOG) << "done";
254  emitResult();
255 }
256 
QHash::iterator insert(const Key &key, const T &value)
virtual QString errorString() const
bool isEmpty() const const
virtual const QMetaObject * metaObject() const const
Id id() const
Returns the unique identifier of the tag.
Definition: tag.cpp:152
T value() const const
Base class for all actions in the Akonadi storage.
Definition: job.h:93
bool removeSubjob(KJob *job) override
Removes the given subjob of this job.
Definition: job.cpp:375
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
ItemFetchScope & fetchScope()
Returns the item fetch scope.
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
QVariant property(const char *name) const const
Tag::List tags() const
Returns the fetched tags after the job has been completed.
int remove(const Key &key)
const T value(const Key &key) const const
const char * className() const const
QVariant fromValue(const T &value)
void setMergeIfExisting(bool merge)
Merges the tag by GID if it is already existing, and returns the merged version.
Helper integration between Akonadi and Qt.
Job that creates a new tag in the Akonadi storage.
Definition: tagcreatejob.h:36
void setFetchRemoteId(bool fetchRemoteId)
Sets whether to fetch tag remote ID.
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:96
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:84
Job that modifies a tag in the Akonadi storage.
Definition: tagmodifyjob.h:36
An Akonadi Tag.
Definition: tag.h:39
void setFetchGid(bool retrieveGID)
Enables retrieval of the item GID.
bool toBool() const const
QString fromLatin1(const char *str, int size)
bool setProperty(const char *name, const QVariant &value)
bool contains(const Key &key) const const
void result(KJob *job)
TagFetchScope & fetchScope()
Returns the tag fetch scope.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVariant merge(const QVariant &lhs, const QVariant &rhs)
Job that fetches tags from the Akonadi storage.
Definition: tagfetchjob.h:43
int error() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 22:46:12 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.