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

akonadi

  • 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 <KLocale>
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 
44 using namespace Akonadi;
45 
46 class TrashRestoreJob::TrashRestoreJobPrivate : public JobPrivate
47 {
48  public:
49  TrashRestoreJobPrivate( TrashRestoreJob *parent )
50  : JobPrivate( parent ) {
51  }
52 
53  void selectResult( KJob * );
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 * );
58 
59  void removeAttribute( const Akonadi::Item::List & );
60  void removeAttribute( const Akonadi::Collection::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 & );
64  void collectionsReceived( const Akonadi::Collection::List & );
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 
199 void TrashRestoreJob::TrashRestoreJobPrivate::collectionsReceived( const Akonadi::Collection::List &collections )
200 {
201  Q_Q( TrashRestoreJob );
202  if ( collections.isEmpty() ) {
203  q->setError( Job::Unknown );
204  q->setErrorText( i18n( "Invalid collection passed" ) );
205  q->emitResult();
206  return;
207  }
208  Q_ASSERT( collections.size() == 1 );
209  mCollection = collections.first();
210 
211  if ( !mCollection.hasAttribute<Akonadi::EntityDeletedAttribute>() ) {
212  return;
213  }
214 
215  const QString restoreResource = mCollection.attribute<Akonadi::EntityDeletedAttribute>()->restoreResource();
216  Collection targetCollection = mCollection.attribute<EntityDeletedAttribute>()->restoreCollection();
217 
218  //Restore in place if no restore collection/resource is set
219  if ( !targetCollection.isValid() ) {
220  removeAttribute( Collection::List() << mCollection );
221  CollectionFetchJob *collectionFetchJob = new CollectionFetchJob( mCollection, CollectionFetchJob::Recursive, q );
222  q->connect( collectionFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
223  q->connect( collectionFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(removeAttribute(Akonadi::Collection::List)) );
224  return;
225  }
226 
227  //Explicit target overrides the resource/configured restore collection
228  if ( mTargetCollection.isValid() ) {
229  targetCollection = mTargetCollection;
230  }
231 
232  //Fetch the target collection to check if it's valid
233  CollectionFetchJob *fetchJob = new CollectionFetchJob( targetCollection, CollectionFetchJob::Base, q );
234  if ( !mTargetCollection.isValid() ) { //explicit targets don't have a fallback
235  fetchJob->setProperty( "Resource", restoreResource );
236  }
237  q->connect( fetchJob, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)) );
238 }
239 
240 
241 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute( const Akonadi::Collection::List &list )
242 {
243  Q_Q( TrashRestoreJob );
244  QListIterator<Collection> i( list );
245  while ( i.hasNext() ) {
246  Collection col = i.next();
247  col.removeAttribute<EntityDeletedAttribute>();
248 
249  CollectionModifyJob *job = new CollectionModifyJob( col, q );
250  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
251 
252  ItemFetchJob *itemFetchJob = new ItemFetchJob( col, q );
253  itemFetchJob->fetchScope().fetchAttribute<EntityDeletedAttribute> ( true );
254  q->connect( itemFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
255  q->connect( itemFetchJob, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(removeAttribute(Akonadi::Item::List)) );
256  }
257 }
258 
259 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute( const Akonadi::Item::List &list )
260 {
261  Q_Q( TrashRestoreJob );
262  Item::List items = list;
263  QMutableListIterator<Item> i( items );
264  while ( i.hasNext() ) {
265  Item &item = i.next();
266  item.removeAttribute<EntityDeletedAttribute>();
267  ItemModifyJob *job = new ItemModifyJob( item, q );
268  job->setIgnorePayload( true );
269  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
270  }
271  //For some reason it is not possible to apply this change to multiple items at once
272  //ItemModifyJob *job = new ItemModifyJob(items, q);
273  //q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
274 }
275 
276 
277 TrashRestoreJob::TrashRestoreJob( const Item & item, QObject * parent )
278  : Job( new TrashRestoreJobPrivate( this ), parent )
279 {
280  Q_D( TrashRestoreJob );
281  d->mItems << item;
282 }
283 
284 TrashRestoreJob::TrashRestoreJob( const Item::List& items, QObject* parent )
285  : Job( new TrashRestoreJobPrivate( this ), parent )
286 {
287  Q_D( TrashRestoreJob );
288  d->mItems = items;
289 }
290 
291 TrashRestoreJob::TrashRestoreJob( const Collection& collection, QObject* parent )
292  : Job( new TrashRestoreJobPrivate( this ), parent )
293 {
294  Q_D( TrashRestoreJob );
295  d->mCollection = collection;
296 }
297 
298 TrashRestoreJob::~TrashRestoreJob()
299 {
300 }
301 
302 void TrashRestoreJob::setTargetCollection( const Akonadi::Collection collection )
303 {
304  Q_D( TrashRestoreJob );
305  d->mTargetCollection = collection;
306 }
307 
308 
309 Item::List TrashRestoreJob::items() const
310 {
311  Q_D( const TrashRestoreJob );
312  return d->mItems;
313 }
314 
315 void TrashRestoreJob::doStart()
316 {
317  Q_D( TrashRestoreJob );
318 
319  //We always have to fetch the entities to ensure that the EntityDeletedAttribute is available
320  if ( !d->mItems.isEmpty() ) {
321  ItemFetchJob *job = new ItemFetchJob( d->mItems, this );
322  job->fetchScope().setCacheOnly( true );
323  job->fetchScope().fetchAttribute<EntityDeletedAttribute> ( true );
324  connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(itemsReceived(Akonadi::Item::List)) );
325  } else if ( d->mCollection.isValid() ) {
326  CollectionFetchJob *job = new CollectionFetchJob( d->mCollection, CollectionFetchJob::Base, this );
327  connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
328  } else {
329  kWarning() << "No valid collection or empty itemlist";
330  setError( Job::Unknown );
331  setErrorText( i18n( "No valid collection or empty itemlist" ) );
332  emitResult();
333  }
334 
335 }
336 
337 #include "moc_trashrestorejob.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:43 by doxygen 1.8.3.1 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-4.10.5 API Reference

Skip menu "kdepimlibs-4.10.5 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
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