Akonadi

tagsync.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6namespace Akonadi
7{
8class 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
21using namespace Akonadi;
22
23bool 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
37TagSync::TagSync(QObject *parent)
38 : Job(parent)
39{
40}
41
42TagSync::~TagSync()
43{
44}
45
46void TagSync::setFullTagList(const Akonadi::Tag::List &tags)
47{
48 mRemoteTags = tags;
49 mDeliveryDone = true;
50 diffTags();
51}
52
53void TagSync::setTagMembers(const QHash<QString, Akonadi::Item::List> &ridMemberMap)
54{
55 mRidMemberMap = ridMemberMap;
56 mTagMembersDeliveryDone = true;
57 diffTags();
58}
59
60void 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
69void 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
78void 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
136void 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
153static 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
160void 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
209void TagSync::onJobDone(KJob * /*unused*/)
210{
211 checkDone();
212}
213
214void 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
225void TagSync::checkDone()
226{
227 if (hasSubjobs()) {
228 return;
229 }
230 qCDebug(AKONADICORE_LOG) << "done";
231 emitResult();
232}
233
234#include "moc_tagsync.cpp"
Job that fetches items from the Akonadi storage.
Job that modifies an existing item in the Akonadi storage.
Base class for all actions in the Akonadi storage.
Definition job.h:81
bool removeSubjob(KJob *job) override
Removes the given subjob of this job.
Definition job.cpp:369
Job that creates a new tag in the Akonadi storage.
Job that fetches tags from the Akonadi storage.
Definition tagfetchjob.h:29
Job that modifies a tag in the Akonadi storage.
An Akonadi Tag.
Definition tag.h:26
bool hasSubjobs() const
virtual QString errorString() const
void emitResult()
int error() const
void result(KJob *job)
Helper integration between Akonadi and Qt.
KIOCORE_EXPORT bool operator==(const UDSEntry &entry, const UDSEntry &other)
KIOCORE_EXPORT CopyJob * copy(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
QStringView merge(QStringView lhs, QStringView rhs)
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
bool remove(const Key &key)
T value(const Key &key) const const
const_iterator cbegin() const const
const_iterator cend() const const
const char * className() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual const QMetaObject * metaObject() const const
QVariant property(const char *name) const const
QString fromLatin1(QByteArrayView str)
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
QVariant fromValue(T &&value)
bool toBool() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:31:58 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.