• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
trashrestorejob.cpp
1 /*
2  * Copyright (c) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
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 
20 #include "trashrestorejob.h"
21 
22 #include "collection.h"
23 #include "entitydeletedattribute.h"
24 #include "item.h"
25 #include "job_p.h"
26 #include "trashsettings.h"
27 
28 #include <KLocalizedString>
29 
30 #include <akonadi/itemdeletejob.h>
31 #include <akonadi/collectiondeletejob.h>
32 #include <akonadi/itemmovejob.h>
33 #include <akonadi/collectionmovejob.h>
34 #include <akonadi/itemmodifyjob.h>
35 #include <akonadi/collectionmodifyjob.h>
36 #include <akonadi/collectionfetchjob.h>
37 #include <akonadi/itemfetchjob.h>
38 #include <akonadi/collectionfetchscope.h>
39 #include <akonadi/itemfetchscope.h>
40 
41 #include <QHash>
42 
43 using namespace Akonadi;
44 
45 class TrashRestoreJob::TrashRestoreJobPrivate : public JobPrivate
46 {
47 public:
48  TrashRestoreJobPrivate(TrashRestoreJob *parent)
49  : JobPrivate(parent)
50  {
51  }
52 
53  void selectResult(KJob *job);
54 
55  //Called when the target collection was fetched,
56  //will issue the move and the removal of the attributes if collection is valid
57  void targetCollectionFetched(KJob *job);
58 
59  void removeAttribute(const Akonadi::Item::List &list);
60  void removeAttribute(const Akonadi::Collection::List &list);
61 
62  //Called after initial fetch of items, issues fetch of target collection or removes attributes for in place restore
63  void itemsReceived(const Akonadi::Item::List &items);
64  void collectionsReceived(const Akonadi::Collection::List &collections);
65 
66  Q_DECLARE_PUBLIC(TrashRestoreJob)
67 
68  Item::List mItems;
69  Collection mCollection;
70  Collection mTargetCollection;
71  QHash<Collection, Item::List> restoreCollections; //groups items to target restore collections
72 
73 };
74 
75 void TrashRestoreJob::TrashRestoreJobPrivate::selectResult(KJob *job)
76 {
77  Q_Q(TrashRestoreJob);
78  if (job->error()) {
79  kWarning() << job->errorString();
80  return; // KCompositeJob takes care of errors
81  }
82 
83  if (!q->hasSubjobs() || (q->subjobs().contains(static_cast<KJob *>(q->sender())) && q->subjobs().size() == 1)) {
84  //kWarning() << "trash restore finished";
85  q->emitResult();
86  }
87 }
88 
89 void TrashRestoreJob::TrashRestoreJobPrivate::targetCollectionFetched(KJob *job)
90 {
91  Q_Q(TrashRestoreJob);
92 
93  CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob *> (job);
94  Q_ASSERT(fetchJob);
95  const Collection::List &list = fetchJob->collections();
96 
97  if (list.isEmpty() || !list.first().isValid() || list.first().hasAttribute<Akonadi::EntityDeletedAttribute>()) { //target collection is invalid/not existing
98 
99  const QString res = fetchJob->property("Resource").toString();
100  if (res.isEmpty()) { //There is no fallback
101  q->setError(Job::Unknown);
102  q->setErrorText(i18n("Could not find restore collection and restore resource is not available"));
103  q->emitResult();
104  //FAIL
105  kWarning() << "restore collection not available";
106  return;
107  }
108 
109  //Try again with the root collection of the resource as fallback
110  CollectionFetchJob *resRootFetch = new CollectionFetchJob(Collection::root(), CollectionFetchJob::FirstLevel, q);
111  resRootFetch->fetchScope().setResource(res);
112  const QVariant &var = fetchJob->property("Items");
113  if (var.isValid()) {
114  resRootFetch->setProperty("Items", var.toInt());
115  }
116  q->connect(resRootFetch, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)));
117  q->connect(resRootFetch, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
118  return;
119  }
120  Q_ASSERT(list.size() == 1);
121  //SUCCESS
122  //We know where to move the entity, so remove the attributes and move them to the right location
123  if (!mItems.isEmpty()) {
124  const QVariant &var = fetchJob->property("Items");
125  Q_ASSERT(var.isValid());
126  const Item::List &items = restoreCollections[Collection(var.toInt())];
127 
128  //store removed attribute if destination collection is valid or the item doesn't have a restore collection
129  //TODO only remove the attribute if the move job was successful (although it is unlikely that it fails since we already fetched the collection)
130  removeAttribute(items);
131  if (items.first().parentCollection() != list.first()) {
132  ItemMoveJob *job = new ItemMoveJob(items, list.first(), q);
133  q->connect(job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
134  }
135  } else {
136  Q_ASSERT(mCollection.isValid());
137  //TODO only remove the attribute if the move job was successful
138  removeAttribute(Collection::List() << mCollection);
139  CollectionFetchJob *collectionFetchJob = new CollectionFetchJob(mCollection, CollectionFetchJob::Recursive, q);
140  q->connect(collectionFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
141  q->connect(collectionFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(removeAttribute(Akonadi::Collection::List)));
142 
143  if (mCollection.parentCollection() != list.first()) {
144  CollectionMoveJob *job = new CollectionMoveJob(mCollection, list.first(), q);
145  q->connect(job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
146  }
147  }
148 
149 }
150 
151 void TrashRestoreJob::TrashRestoreJobPrivate::itemsReceived(const Akonadi::Item::List &items)
152 {
153  Q_Q(TrashRestoreJob);
154  if (items.isEmpty()) {
155  q->setError(Job::Unknown);
156  q->setErrorText(i18n("Invalid items passed"));
157  q->emitResult();
158  return;
159  }
160  mItems = items;
161 
162  //Sort by restore collection
163  foreach (const Item &item, mItems) {
164  if (!item.hasAttribute<Akonadi::EntityDeletedAttribute>()) {
165  continue;
166  }
167  //If the restore collection is invalid we restore the item in place, so we don't need to know its restore resource => we can put those cases in the same list
168  restoreCollections[item.attribute<Akonadi::EntityDeletedAttribute>()->restoreCollection()].append(item);
169  }
170 
171  foreach (const Collection &col, restoreCollections.keys()) { //krazy:exclude=foreach
172  const Item &first = restoreCollections.value(col).first();
173  //Move the items to the correct collection if available
174  Collection targetCollection = col;
175  const QString restoreResource = first.attribute<Akonadi::EntityDeletedAttribute>()->restoreResource();
176 
177  //Restore in place if no restore collection is set
178  if (!targetCollection.isValid()) {
179  removeAttribute(restoreCollections.value(col));
180  return;
181  }
182 
183  //Explicit target overrides the resource
184  if (mTargetCollection.isValid()) {
185  targetCollection = mTargetCollection;
186  }
187 
188  //Try to fetch the target resource to see if it is available
189  CollectionFetchJob *fetchJob = new CollectionFetchJob(targetCollection, Akonadi::CollectionFetchJob::Base, q);
190  if (!mTargetCollection.isValid()) { //explicit targets don't have a fallback
191  fetchJob->setProperty("Resource", restoreResource);
192  }
193  fetchJob->setProperty("Items", col.id()); //to find the items in restore collections again
194  q->connect(fetchJob, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)));
195  }
196 }
197 
198 void TrashRestoreJob::TrashRestoreJobPrivate::collectionsReceived(const Akonadi::Collection::List &collections)
199 {
200  Q_Q(TrashRestoreJob);
201  if (collections.isEmpty()) {
202  q->setError(Job::Unknown);
203  q->setErrorText(i18n("Invalid collection passed"));
204  q->emitResult();
205  return;
206  }
207  Q_ASSERT(collections.size() == 1);
208  mCollection = collections.first();
209 
210  if (!mCollection.hasAttribute<Akonadi::EntityDeletedAttribute>()) {
211  return;
212  }
213 
214  const QString restoreResource = mCollection.attribute<Akonadi::EntityDeletedAttribute>()->restoreResource();
215  Collection targetCollection = mCollection.attribute<EntityDeletedAttribute>()->restoreCollection();
216 
217  //Restore in place if no restore collection/resource is set
218  if (!targetCollection.isValid()) {
219  removeAttribute(Collection::List() << mCollection);
220  CollectionFetchJob *collectionFetchJob = new CollectionFetchJob(mCollection, CollectionFetchJob::Recursive, q);
221  q->connect(collectionFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
222  q->connect(collectionFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(removeAttribute(Akonadi::Collection::List)));
223  return;
224  }
225 
226  //Explicit target overrides the resource/configured restore collection
227  if (mTargetCollection.isValid()) {
228  targetCollection = mTargetCollection;
229  }
230 
231  //Fetch the target collection to check if it's valid
232  CollectionFetchJob *fetchJob = new CollectionFetchJob(targetCollection, CollectionFetchJob::Base, q);
233  if (!mTargetCollection.isValid()) { //explicit targets don't have a fallback
234  fetchJob->setProperty("Resource", restoreResource);
235  }
236  q->connect(fetchJob, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)));
237 }
238 
239 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute(const Akonadi::Collection::List &list)
240 {
241  Q_Q(TrashRestoreJob);
242  QListIterator<Collection> i(list);
243  while (i.hasNext()) {
244  Collection col = i.next();
245  col.removeAttribute<EntityDeletedAttribute>();
246 
247  CollectionModifyJob *job = new CollectionModifyJob(col, q);
248  q->connect(job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
249 
250  ItemFetchJob *itemFetchJob = new ItemFetchJob(col, q);
251  itemFetchJob->fetchScope().fetchAttribute<EntityDeletedAttribute> (true);
252  q->connect(itemFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
253  q->connect(itemFetchJob, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(removeAttribute(Akonadi::Item::List)));
254  }
255 }
256 
257 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute(const Akonadi::Item::List &list)
258 {
259  Q_Q(TrashRestoreJob);
260  Item::List items = list;
261  QMutableListIterator<Item> i(items);
262  while (i.hasNext()) {
263  Item &item = i.next();
264  item.removeAttribute<EntityDeletedAttribute>();
265  ItemModifyJob *job = new ItemModifyJob(item, q);
266  job->setIgnorePayload(true);
267  q->connect(job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)));
268  }
269  //For some reason it is not possible to apply this change to multiple items at once
270  //ItemModifyJob *job = new ItemModifyJob(items, q);
271  //q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
272 }
273 
274 TrashRestoreJob::TrashRestoreJob(const Item &item, QObject *parent)
275  : Job(new TrashRestoreJobPrivate(this), parent)
276 {
277  Q_D(TrashRestoreJob);
278  d->mItems << item;
279 }
280 
281 TrashRestoreJob::TrashRestoreJob(const Item::List &items, QObject *parent)
282  : Job(new TrashRestoreJobPrivate(this), parent)
283 {
284  Q_D(TrashRestoreJob);
285  d->mItems = items;
286 }
287 
288 TrashRestoreJob::TrashRestoreJob(const Collection &collection, QObject *parent)
289  : Job(new TrashRestoreJobPrivate(this), parent)
290 {
291  Q_D(TrashRestoreJob);
292  d->mCollection = collection;
293 }
294 
295 TrashRestoreJob::~TrashRestoreJob()
296 {
297 }
298 
299 void TrashRestoreJob::setTargetCollection(const Akonadi::Collection collection)
300 {
301  Q_D(TrashRestoreJob);
302  d->mTargetCollection = collection;
303 }
304 
305 Item::List TrashRestoreJob::items() const
306 {
307  Q_D(const TrashRestoreJob);
308  return d->mItems;
309 }
310 
311 void TrashRestoreJob::doStart()
312 {
313  Q_D(TrashRestoreJob);
314 
315  //We always have to fetch the entities to ensure that the EntityDeletedAttribute is available
316  if (!d->mItems.isEmpty()) {
317  ItemFetchJob *job = new ItemFetchJob(d->mItems, this);
318  job->fetchScope().setCacheOnly(true);
319  job->fetchScope().fetchAttribute<EntityDeletedAttribute> (true);
320  connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(itemsReceived(Akonadi::Item::List)));
321  } else if (d->mCollection.isValid()) {
322  CollectionFetchJob *job = new CollectionFetchJob(d->mCollection, CollectionFetchJob::Base, this);
323  connect(job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)));
324  } else {
325  kWarning() << "No valid collection or empty itemlist";
326  setError(Job::Unknown);
327  setErrorText(i18n("No valid collection or empty itemlist"));
328  emitResult();
329  }
330 
331 }
332 
333 #include "moc_trashrestorejob.cpp"
Akonadi::CollectionModifyJob
Job that modifies a collection in the Akonadi storage.
Definition: collectionmodifyjob.h:82
Akonadi::ItemFetchScope::fetchAttribute
void fetchAttribute(const QByteArray &type, bool fetch=true)
Sets whether the attribute of the given type should be fetched.
Definition: itemfetchscope.cpp:80
Akonadi::CollectionFetchJob::collections
Collection::List collections() const
Returns the list of fetched collection.
Definition: collectionfetchjob.cpp:169
Akonadi::Job::Unknown
Unknown error.
Definition: job.h:108
Akonadi::CollectionMoveJob
Job that moves a collection in the Akonadi storage to a new parent collection.
Definition: collectionmovejob.h:50
Akonadi::CollectionFetchJob::FirstLevel
Only list direct sub-collections of the base collection.
Definition: collectionfetchjob.h:63
Akonadi::CollectionFetchJob::fetchScope
CollectionFetchScope & fetchScope()
Returns the collection fetch scope.
Definition: collectionfetchjob.cpp:439
Akonadi::TrashRestoreJob::setTargetCollection
void setTargetCollection(const Collection collection)
Sets the target collection, where the item is moved to.
Definition: trashrestorejob.cpp:299
Akonadi::CollectionFetchScope::setResource
void setResource(const QString &resource)
Sets a resource filter, that is only collections owned by the specified resource are retrieved...
Definition: collectionfetchscope.cpp:118
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
Akonadi::CollectionFetchJob
Job that fetches collections from the Akonadi storage.
Definition: collectionfetchjob.h:53
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
QList::size
int size() const
Akonadi::CollectionFetchJob::Base
Only fetch the base collection.
Definition: collectionfetchjob.h:62
Akonadi::Entity::attribute
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
Definition: entity.cpp:167
Akonadi::ItemFetchJob::fetchScope
ItemFetchScope & fetchScope()
Returns the item fetch scope.
Definition: itemfetchjob.cpp:261
Akonadi::TrashRestoreJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: trashrestorejob.cpp:311
Akonadi::Entity::removeAttribute
void removeAttribute(const QByteArray &name)
Removes and deletes the attribute of the given type name.
Definition: entity.cpp:142
Akonadi::Entity::parentCollection
Collection parentCollection() const
Returns the parent collection of this object.
Definition: entity.cpp:185
QVariant::toInt
int toInt(bool *ok) const
QHash
QObject
Akonadi::TrashRestoreJob::TrashRestoreJob
TrashRestoreJob(const Item &item, QObject *parent=0)
All items need to be from the same resource.
Definition: trashrestorejob.cpp:274
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
Akonadi::Collection::root
static Collection root()
Returns the root collection.
Definition: collection.cpp:192
QList::first
T & first()
QString
QList
Akonadi::Job::List
QList< Job * > List
Describes a list of jobs.
Definition: job.h:97
Akonadi::TrashRestoreJob
Job that restores entites from trash.
Definition: trashrestorejob.h:56
Akonadi::Entity::id
Id id() const
Returns the unique identifier of the entity.
Definition: entity.cpp:72
QHash::keys
QList< Key > keys() const
Akonadi::EntityDeletedAttribute
An Attribute that marks that an entity was marked as deleted.
Definition: entitydeletedattribute.h:49
Akonadi::ItemMoveJob
Job that moves an item into a different collection in the Akonadi storage.
Definition: itemmovejob.h:48
QMutableListIterator
Akonadi::ItemModifyJob
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:97
Akonadi::ItemFetchJob
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:82
Akonadi::JobPrivate
Definition: job_p.h:31
QVariant::isValid
bool isValid() const
QListIterator
Akonadi::Entity::isValid
bool isValid() const
Returns whether the entity is valid.
Definition: entity.cpp:97
Akonadi::CollectionFetchJob::Recursive
List all sub-collections.
Definition: collectionfetchjob.h:64
Akonadi::ItemFetchScope::setCacheOnly
void setCacheOnly(bool cacheOnly)
Sets whether payload data should be requested from remote sources or just from the local cache...
Definition: itemfetchscope.cpp:109
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:03 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal