20 #include "collectionsync_p.h"
21 #include "collection.h"
23 #include "collectioncreatejob.h"
24 #include "collectiondeletejob.h"
25 #include "collectionfetchjob.h"
26 #include "collectionmodifyjob.h"
27 #include "collectionfetchscope.h"
28 #include "collectionmovejob.h"
30 #include "cachepolicy.h"
33 #include <KLocalizedString>
34 #include <QtCore/QVariant>
36 using namespace Akonadi;
52 qDeleteAll(childNodes);
53 qDeleteAll(pendingRemoteNodes);
66 Q_DECLARE_METATYPE(LocalNode *)
67 static const
char LOCAL_NODE[] = "LocalNode";
82 Q_DECLARE_METATYPE(RemoteNode *)
83 static const
char REMOTE_NODE[] = "RemoteNode";
85 static const
char CONTENTMIMETYPES[] = "CONTENTMIMETYPES";
98 , currentTransaction(0)
99 , knownLocalCollections(0)
102 , hierarchicalRIDs(false)
103 , localListDone(false)
104 , deliveryDone(false)
111 qDeleteAll(rootRemoteNodes);
119 localRoot->processed =
true;
120 if (currentTransaction) {
122 localRoot->pendingRemoteNodes.swap(rootRemoteNodes);
127 localUidMap.insert(localRoot->collection.id(), localRoot);
128 if (!hierarchicalRIDs) {
129 localRidMap.insert(
QString(), localRoot);
134 LocalNode *createLocalNode(
const Collection &col)
136 LocalNode *node =
new LocalNode(col);
137 Q_ASSERT(!localUidMap.contains(col.
id()));
138 localUidMap.insert(node->collection.id(), node);
140 localRidMap.insert(node->collection.remoteId(), node);
144 if (localPendingCollections.contains(col.
id())) {
147 Q_ASSERT(localUidMap.contains(childId));
148 LocalNode *childNode = localUidMap.
value(childId);
149 node->childNodes.append(childNode);
150 if (!childNode->collection.remoteId().isEmpty()) {
151 node->childRidMap.insert(childNode->collection.remoteId(), childNode);
159 parentNode->childNodes.append(node);
160 if (!node->collection.remoteId().isEmpty()) {
161 parentNode->childRidMap.insert(node->collection.remoteId(), node);
174 kWarning() <<
"Collection '" << col.
name() <<
"' does not have a remote identifier - skipping";
177 RemoteNode *node =
new RemoteNode(col);
178 rootRemoteNodes.append(node);
186 knownLocalCollections++;
191 void localCollectionFetchResult(KJob *job)
198 if (!localPendingCollections.isEmpty()) {
199 q->setError(Unknown);
200 q->setErrorText(i18n(
"Inconsistent local collection tree detected."));
205 localListDone =
true;
214 LocalNode *findLocalChildNodeByName(LocalNode *localParentNode,
const QString &name)
const
220 if (localParentNode == localRoot) {
224 foreach (LocalNode *childNode, localParentNode->childNodes) {
226 if (childNode->collection.name() == name && childNode->collection.remoteId().isEmpty()) {
237 LocalNode *findMatchingLocalNode(
const Collection &collection)
const
239 if (!hierarchicalRIDs) {
240 if (localRidMap.contains(collection.
remoteId())) {
241 return localRidMap.value(collection.
remoteId());
248 LocalNode *localParent = 0;
250 kWarning() <<
"Remote collection without valid parent found: " << collection;
254 localParent = localRoot;
260 if (localParent->childRidMap.contains(collection.
remoteId())) {
261 return localParent->childRidMap.value(collection.
remoteId());
265 if (LocalNode *recoveredLocalNode = findLocalChildNodeByName(localParent, collection.
name())) {
266 kDebug() <<
"Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
267 return recoveredLocalNode;
279 LocalNode *findBestLocalAncestor(
const Collection &collection,
bool *exactMatch = 0)
281 if (!hierarchicalRIDs) {
291 kWarning() <<
"Remote collection without valid parent found: " << collection;
294 bool parentIsExact =
false;
295 LocalNode *localParent = findBestLocalAncestor(collection.
parentCollection(), &parentIsExact);
296 if (!parentIsExact) {
302 if (localParent->childRidMap.contains(collection.
remoteId())) {
306 return localParent->childRidMap.value(collection.
remoteId());
317 bool checkPendingRemoteNodes()
const
319 if (rootRemoteNodes.size() != knownLocalCollections) {
323 foreach (RemoteNode *remoteNode, rootRemoteNodes) {
325 LocalNode *localNode = findMatchingLocalNode(remoteNode->collection);
327 if (checkLocalCollection(localNode, remoteNode)) {
342 void processPendingRemoteNodes(LocalNode *_localRoot)
345 _localRoot->pendingRemoteNodes.clear();
347 foreach (RemoteNode *remoteNode, pendingRemoteNodes) {
349 LocalNode *localNode = findMatchingLocalNode(remoteNode->collection);
351 Q_ASSERT(!localNode->processed);
352 updateLocalCollection(localNode, remoteNode);
356 localNode = findMatchingLocalNode(remoteNode->collection.parentCollection());
358 pendingCreations[localNode].append(remoteNode);
362 localNode = findBestLocalAncestor(remoteNode->collection);
364 q->setError(Unknown);
365 q->setErrorText(i18n(
"Remote collection without root-terminated ancestor chain provided, resource is broken."));
369 localNode->pendingRemoteNodes.append(remoteNode);
374 it != pendingCreations.
constEnd(); ++it) {
375 createLocalCollections(it.key(), it.value());
382 bool checkLocalCollection(LocalNode *localNode, RemoteNode *remoteNode)
const
384 const Collection &localCollection = localNode->collection;
385 const Collection &remoteCollection = remoteNode->collection;
387 if (!keepLocalChanges.contains(CONTENTMIMETYPES)) {
403 if (localCollection.
name() != remoteCollection.
name()) {
419 if (localAttr && keepLocalChanges.contains(attr->
type())) {
434 void updateLocalCollection(LocalNode *localNode, RemoteNode *remoteNode)
437 Q_ASSERT(!upd.remoteId().isEmpty());
438 Q_ASSERT(currentTransaction);
439 upd.setId(localNode->collection.id());
440 if (keepLocalChanges.contains(CONTENTMIMETYPES)) {
441 upd.setContentMimeTypes(localNode->collection.contentMimeTypes());
443 foreach (
Attribute *remoteAttr, upd.attributes()) {
444 if (keepLocalChanges.contains(remoteAttr->
type()) && localNode->collection.hasAttribute(remoteAttr->
type())) {
446 Attribute *localAttr = localNode->collection.attribute(remoteAttr->
type());
447 upd.removeAttribute(localAttr->
type());
448 upd.addAttribute(localAttr->
clone());
456 c.setParentCollection(localNode->collection.parentCollection());
459 connect(mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)));
463 if (!hierarchicalRIDs) {
464 LocalNode *oldParent = localUidMap.value(localNode->collection.parentCollection().id());
465 LocalNode *newParent = findMatchingLocalNode(remoteNode->collection.parentCollection());
468 if (newParent && oldParent != newParent) {
471 connect(move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)));
475 localNode->processed =
true;
479 void updateLocalCollectionResult(KJob *job)
485 if (qobject_cast<CollectionModifyJob *>(job)) {
497 foreach (RemoteNode *remoteNode, remoteNodes) {
505 connect(create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)));
509 void createLocalCollectionResult(KJob *job)
517 LocalNode *localNode = createLocalNode(newLocal);
518 localNode->processed =
true;
520 LocalNode *localParent = job->property(LOCAL_NODE).value<LocalNode *>();
521 Q_ASSERT(localParent->childNodes.contains(localNode));
522 RemoteNode *remoteNode = job->property(REMOTE_NODE).value<RemoteNode *>();
526 processPendingRemoteNodes(localParent);
527 if (!hierarchicalRIDs) {
528 processPendingRemoteNodes(localRoot);
537 bool hasProcessedChildren(LocalNode *localNode)
const
539 if (localNode->processed) {
542 foreach (LocalNode *child, localNode->childNodes) {
543 if (hasProcessedChildren(child)) {
554 Collection::List findUnprocessedLocalCollections(LocalNode *localNode)
const
557 if (!localNode->processed) {
558 if (hasProcessedChildren(localNode)) {
559 kWarning() <<
"Found unprocessed local node with processed children, excluding from deletion";
560 kWarning() << localNode->collection;
563 if (localNode->collection.remoteId().isEmpty()) {
564 kWarning() <<
"Found unprocessed local node without remoteId, excluding from deletion";
565 kWarning() << localNode->collection;
568 rv.
append(localNode->collection);
572 foreach (LocalNode *child, localNode->childNodes) {
573 rv.
append(findUnprocessedLocalCollections(child));
581 void deleteUnprocessedLocalNodes()
587 deleteLocalCollections(cols);
596 q->setTotalAmount(KJob::Bytes, q->totalAmount(KJob::Bytes) + cols.
size());
601 Q_ASSERT(currentTransaction);
603 connect(job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)));
609 currentTransaction->setIgnoreJobFailure(job);
613 void deleteLocalCollectionsResult(KJob *)
624 void checkUpdateNecessity()
626 bool updateNeeded = checkPendingRemoteNodes();
634 Q_ASSERT(!currentTransaction);
636 currentTransaction->setAutomaticCommittingEnabled(
false);
637 q->connect(currentTransaction, SIGNAL(result(KJob*)), SLOT(transactionSequenceResult(KJob*)));
644 void transactionSequenceResult(KJob *job)
658 kDebug() << Q_FUNC_INFO <<
"localListDone: " << localListDone <<
" deliveryDone: " << deliveryDone;
659 if (!localListDone || !deliveryDone) {
665 if (!currentTransaction) {
666 checkUpdateNecessity();
671 processPendingRemoteNodes(localRoot);
673 if (!incremental && deliveryDone) {
674 deleteUnprocessedLocalNodes();
677 if (!hierarchicalRIDs) {
678 deleteLocalCollections(removedRemoteCollections);
681 foreach (
const Collection &c, removedRemoteCollections) {
682 LocalNode *node = findMatchingLocalNode(c);
684 localCols.
append(node->collection);
687 deleteLocalCollections(localCols);
689 removedRemoteCollections.clear();
700 rv.
append(localNode->pendingRemoteNodes);
701 foreach (LocalNode *child, localNode->childNodes) {
702 rv.
append(findPendingRemoteNodes(child));
713 q->setProcessedAmount(KJob::Bytes, progress);
716 if (!deliveryDone || pendingJobs > 0 || !localListDone) {
723 q->setError(Unknown);
724 q->setErrorText(i18n(
"Found unresolved orphan collections"));
725 foreach (RemoteNode *orphan, orphans) {
726 kDebug() <<
"found orphan collection:" << orphan->collection;
732 kDebug() << Q_FUNC_INFO <<
"q->commit()";
733 Q_ASSERT(currentTransaction);
734 currentTransaction->commit();
744 LocalNode *localRoot;
760 int knownLocalCollections;
764 bool hierarchicalRIDs;
775 , d(new Private(this))
777 d->resourceId = resourceId;
778 setTotalAmount(KJob::Bytes, 0);
788 setTotalAmount(KJob::Bytes, totalAmount(KJob::Bytes) + remoteCollections.
count());
789 foreach (
const Collection &c, remoteCollections) {
790 d->createRemoteNode(c);
794 d->deliveryDone =
true;
801 setTotalAmount(KJob::Bytes, totalAmount(KJob::Bytes) + changedCollections.
count());
802 d->incremental =
true;
803 foreach (
const Collection &c, changedCollections) {
804 d->createRemoteNode(c);
806 d->removedRemoteCollections += removedCollections;
809 d->deliveryDone =
true;
817 Job *parent = (d->currentTransaction ?
static_cast<Job *
>(d->currentTransaction) : static_cast<Job *>(
this));
824 connect(job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)));
829 d->streaming = streaming;
834 d->deliveryDone =
true;
840 d->hierarchicalRIDs = hierarchical;
845 if (d->currentTransaction) {
846 d->currentTransaction->rollback();
852 d->keepLocalChanges = parts;
855 #include "moc_collectionsync_p.cpp"
Job that modifies a collection in the Akonadi storage.
void rollback()
Do a rollback operation if needed.
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
Sets how many levels of ancestor collections should be included in the retrieval. ...
void setKeepLocalChanges(const QSet< QByteArray > &parts)
Allows to specify parts of the collection that should not be changed if locally available.
QString name() const
Returns the i18n'ed name of the collection.
AKONADI_DEPRECATED void setIncludeUnsubscribed(bool include)
Sets whether unsubscribed collections should be included in the collection listing.
Job that moves a collection in the Akonadi storage to a new parent collection.
CollectionFetchScope & fetchScope()
Returns the collection fetch scope.
void setResource(const QString &resource)
Sets a resource filter, that is only collections owned by the specified resource are retrieved...
Represents a collection of PIM items.
const T & at(int i) const
Job that fetches collections from the Akonadi storage.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
qint64 Id
Describes the unique id type.
Base class for all actions in the Akonadi storage.
~CollectionSync()
Destroys this job.
Provides interface for custom attributes for Entity.
Only retrieve the immediate parent collection.
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
void setHierarchicalRemoteIds(bool hierarchical)
Indicate whether the resource supplies collections with hierarchical or global remote identifiers...
void setRemoteCollections(const Collection::List &remoteCollections)
Sets the result of a full remote collection listing.
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
int count(const T &value) const
void append(const T &value)
const_iterator constEnd() const
Collection parentCollection() const
Returns the parent collection of this object.
void doStart()
This method must be reimplemented in the concrete jobs.
QString remoteId() const
Returns the remote id of the entity.
void retrievalDone()
Indicate that all collections have been retrieved in streaming mode.
QString remoteRevision() const
Returns the remote revision of the entity.
static Collection root()
Returns the root collection.
Job that deletes a collection in the Akonadi storage.
Id id() const
Returns the unique identifier of the entity.
CollectionSync(const QString &resourceId, QObject *parent=0)
Creates a new collection synchronzier.
QVariant fromValue(const T &value)
const_iterator constBegin() const
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
CachePolicy cachePolicy() const
Returns the cache policy of the collection.
virtual QByteArray serialized() const =0
Returns a QByteArray representation of the attribute which will be storaged.
Job that creates a new collection in the Akonadi storage.
virtual Attribute * clone() const =0
Creates a copy of this attribute.
virtual QByteArray type() const =0
Returns the type of the attribute.
List all sub-collections.
void setStreamingEnabled(bool streaming)
Enables streaming, that is not all collections are delivered at once.
Attribute::List attributes() const
Returns a list of all attributes of the entity.