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

akonadi

  • akonadi
trashjob.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 "trashjob.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/itemfetchscope.h>
37 #include <akonadi/collectionfetchscope.h>
38 #include <akonadi/itemfetchjob.h>
39 #include <akonadi/collectionfetchjob.h>
40 
41 #include <QHash>
42 
43 using namespace Akonadi;
44 
45 class TrashJob::TrashJobPrivate : public JobPrivate
46 {
47  public:
48  TrashJobPrivate( TrashJob *parent )
49  : JobPrivate( parent ),
50  mKeepTrashInCollection( false ),
51  mSetRestoreCollection( false ),
52  mDeleteIfInTrash( false ) {
53  }
54 //4.
55  void selectResult( KJob *job );
56 //3.
57  //Helper functions to recursivly set the attribute on deleted collections
58  void setAttribute( const Akonadi::Collection::List & );
59  void setAttribute( const Akonadi::Item::List & );
60  //Set attributes after ensuring that move job was successful
61  void setAttribute( KJob *job );
62 
63 //2.
64  //called after parent of the trashed item was fetched (needed to see in which resource the item is in)
65  void parentCollectionReceived( const Akonadi::Collection::List & );
66 
67 
68 //1.
69  //called after initial fetch of trashed items
70  void itemsReceived( const Akonadi::Item::List & );
71  //called after initial fetch of trashed collection
72  void collectionsReceived( const Akonadi::Collection::List & );
73 
74 
75  Q_DECLARE_PUBLIC( TrashJob )
76 
77  Item::List mItems;
78  Collection mCollection;
79  Collection mRestoreCollection;
80  Collection mTrashCollection;
81  bool mKeepTrashInCollection;
82  bool mSetRestoreCollection; //only set restore collection when moved to trash collection (not in place)
83  bool mDeleteIfInTrash;
84  QHash<Collection, Item::List> mCollectionItems; //list of trashed items sorted according to parent collection
85  QHash<Entity::Id, Collection> mParentCollections; //fetched parent collcetion of items (containing the resource name)
86 
87 };
88 
89 void TrashJob::TrashJobPrivate::selectResult( KJob *job )
90 {
91  Q_Q( TrashJob );
92  if ( job->error() ) {
93  kWarning() << job->objectName();
94  kWarning() << job->errorString();
95  return; // KCompositeJob takes care of errors
96  }
97 
98  if ( !q->hasSubjobs() || ( q->subjobs().contains( static_cast<KJob*>( q->sender() ) ) && q->subjobs().size() == 1 ) ) {
99  q->emitResult();
100  }
101 }
102 
103 void TrashJob::TrashJobPrivate::setAttribute( const Akonadi::Collection::List &list )
104 {
105  Q_Q( TrashJob );
106  QListIterator<Collection> i( list );
107  while ( i.hasNext() ) {
108  const Collection &col = i.next();
109  EntityDeletedAttribute *eda = new EntityDeletedAttribute();
110  if ( mSetRestoreCollection ) {
111  Q_ASSERT( mRestoreCollection.isValid() );
112  eda->setRestoreCollection( mRestoreCollection );
113  }
114 
115  Collection modCol( col.id() ); //really only modify attribute (forget old remote ids, etc.), otherwise we have an error because of the move
116  modCol.addAttribute( eda );
117 
118  CollectionModifyJob *job = new CollectionModifyJob( modCol, q );
119  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
120 
121  ItemFetchJob *itemFetchJob = new ItemFetchJob( col, q );
122  //TODO not sure if it is guaranteed that itemsReceived is always before result (otherwise the result is emitted before the attributes are set)
123  q->connect( itemFetchJob, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(setAttribute(Akonadi::Item::List)) );
124  q->connect( itemFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
125  }
126 }
127 
128 void TrashJob::TrashJobPrivate::setAttribute( const Akonadi::Item::List &list )
129 {
130  Q_Q( TrashJob );
131  Item::List items = list;
132  QMutableListIterator<Item> i( items );
133  while ( i.hasNext() ) {
134  const Item &item = i.next();
135  EntityDeletedAttribute *eda = new EntityDeletedAttribute();
136  if ( mSetRestoreCollection ) {
137  //When deleting a collection, we want to restore the deleted collection's items restored to the deleted collection's parent, not the items parent
138  if ( mRestoreCollection.isValid() ) {
139  eda->setRestoreCollection( mRestoreCollection );
140  } else {
141  Q_ASSERT( mParentCollections.contains( item.parentCollection().id() ) );
142  eda->setRestoreCollection( mParentCollections.value( item.parentCollection().id() ) );
143  }
144  }
145 
146  Item modItem( item.id() ); //really only modify attribute (forget old remote ids, etc.)
147  modItem.addAttribute( eda );
148  ItemModifyJob *job = new ItemModifyJob( modItem, q );
149  job->setIgnorePayload( true );
150  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
151  }
152 
153  //For some reason it is not possible to apply this change to multiple items at once
154  /*ItemModifyJob *job = new ItemModifyJob(items, q);
155  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );*/
156 }
157 
158 void TrashJob::TrashJobPrivate::setAttribute( KJob* job )
159 {
160  Q_Q( TrashJob );
161  if ( job->error() ) {
162  kWarning() << job->objectName();
163  kWarning() << job->errorString();
164  q->setError( Job::Unknown );
165  q->setErrorText( i18n( "Move to trash collection failed, aborting trash operation" ) );
166  return;
167  }
168 
169  //For Items
170  const QVariant var = job->property( "MovedItems" );
171  if ( var.isValid() ) {
172  int id = var.toInt();
173  Q_ASSERT( id >= 0 );
174  setAttribute( mCollectionItems.value( Collection( id ) ) );
175  return;
176  }
177 
178  //For a collection
179  Q_ASSERT( mCollection.isValid() );
180  setAttribute( Collection::List() << mCollection );
181  //Set the attribute on all subcollections and items
182  CollectionFetchJob *colFetchJob = new CollectionFetchJob( mCollection, CollectionFetchJob::Recursive, q );
183  q->connect( colFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(setAttribute(Akonadi::Collection::List)) );
184  q->connect( colFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
185 }
186 
187 void TrashJob::TrashJobPrivate::parentCollectionReceived( const Akonadi::Collection::List &collections )
188 {
189  Q_Q( TrashJob );
190  Q_ASSERT( collections.size() == 1 );
191  const Collection &parentCollection = collections.first();
192 
193  //store attribute
194  Q_ASSERT( !parentCollection.resource().isEmpty() );
195  Collection trashCollection = mTrashCollection;
196  if ( !mTrashCollection.isValid() ) {
197  trashCollection = TrashSettings::getTrashCollection( parentCollection.resource() );
198  }
199  if ( !mKeepTrashInCollection && trashCollection.isValid() ) { //Only set the restore collection if the item is moved to trash
200  mSetRestoreCollection = true;
201  }
202 
203  mParentCollections.insert( parentCollection.id(), parentCollection );
204 
205  if ( trashCollection.isValid() ) { //Move the items to the correct collection if available
206  ItemMoveJob *job = new ItemMoveJob( mCollectionItems.value( parentCollection ), trashCollection, q );
207  job->setProperty( "MovedItems", parentCollection.id() );
208  q->connect( job, SIGNAL(result(KJob*)), SLOT(setAttribute(KJob*)) ); //Wait until the move finished to set the attirbute
209  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
210  } else {
211  setAttribute( mCollectionItems.value( parentCollection ) );
212  }
213 }
214 
215 void TrashJob::TrashJobPrivate::itemsReceived( const Akonadi::Item::List &items )
216 {
217  Q_Q( TrashJob );
218  if ( items.isEmpty() ) {
219  q->setError( Job::Unknown );
220  q->setErrorText( i18n( "Invalid items passed" ) );
221  q->emitResult();
222  return;
223  }
224 
225  Item::List toDelete;
226 
227  QListIterator<Item> i( items );
228  while ( i.hasNext() ) {
229  const Item &item = i.next();
230  if ( item.hasAttribute<EntityDeletedAttribute>() ) {
231  toDelete.append( item );
232  continue;
233  }
234  Q_ASSERT( item.parentCollection().isValid() );
235  mCollectionItems[item.parentCollection()].append( item ); //Sort by parent col ( = restore collection)
236  }
237 
238  foreach( const Collection &col, mCollectionItems.keys() ) { //krazy:exclude=foreach
239  CollectionFetchJob *job = new CollectionFetchJob( col, Akonadi::CollectionFetchJob::Base, q );
240  q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
241  SLOT(parentCollectionReceived(Akonadi::Collection::List)) );
242  }
243 
244  if ( mDeleteIfInTrash && !toDelete.isEmpty() ) {
245  ItemDeleteJob *job = new ItemDeleteJob( toDelete, q );
246  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
247  } else if ( mCollectionItems.isEmpty() ) { //No job started, so we abort the job
248  kWarning() << "Nothing to do";
249  q->emitResult();
250  }
251 
252 }
253 
254 void TrashJob::TrashJobPrivate::collectionsReceived( const Akonadi::Collection::List &collections )
255 {
256  Q_Q( TrashJob );
257  if ( collections.isEmpty() ) {
258  q->setError( Job::Unknown );
259  q->setErrorText( i18n( "Invalid collection passed" ) );
260  q->emitResult();
261  return;
262  }
263  Q_ASSERT( collections.size() == 1 );
264  mCollection = collections.first();
265 
266  if ( mCollection.hasAttribute<EntityDeletedAttribute>() ) {//marked as deleted
267  if ( mDeleteIfInTrash ) {
268  CollectionDeleteJob *job = new CollectionDeleteJob( mCollection, q );
269  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
270  } else {
271  kWarning() << "Nothing to do";
272  q->emitResult();
273  }
274  return;
275  }
276 
277  Collection trashCollection = mTrashCollection;
278  if ( !mTrashCollection.isValid() ) {
279  trashCollection = TrashSettings::getTrashCollection( mCollection.resource() );
280  }
281  if ( !mKeepTrashInCollection && trashCollection.isValid() ) { //only set the restore collection if the item is moved to trash
282  mSetRestoreCollection = true;
283  Q_ASSERT( mCollection.parentCollection().isValid() );
284  mRestoreCollection = mCollection.parentCollection();
285  mRestoreCollection.setResource( mCollection.resource() ); //The parent collection doesn't contain the resource, so we have to set it manually
286  }
287 
288  if ( trashCollection.isValid() ) {
289  CollectionMoveJob *job = new CollectionMoveJob( mCollection, trashCollection, q );
290  q->connect( job, SIGNAL(result(KJob*)), SLOT(setAttribute(KJob*)) );
291  q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
292  } else {
293  setAttribute( Collection::List() << mCollection );
294  }
295 
296 }
297 
298 
299 
300 
301 TrashJob::TrashJob( const Item & item, QObject * parent )
302  : Job( new TrashJobPrivate( this ), parent )
303 {
304  Q_D( TrashJob );
305  d->mItems << item;
306 }
307 
308 TrashJob::TrashJob( const Item::List& items, QObject* parent )
309  : Job( new TrashJobPrivate( this ), parent )
310 {
311  Q_D( TrashJob );
312  d->mItems = items;
313 }
314 
315 TrashJob::TrashJob( const Collection& collection, QObject* parent )
316  : Job( new TrashJobPrivate( this ), parent )
317 {
318  Q_D( TrashJob );
319  d->mCollection = collection;
320 }
321 
322 TrashJob::~TrashJob()
323 {
324 }
325 
326 Item::List TrashJob::items() const
327 {
328  Q_D( const TrashJob );
329  return d->mItems;
330 }
331 
332 void TrashJob::setTrashCollection( const Akonadi::Collection &collection )
333 {
334  Q_D( TrashJob );
335  d->mTrashCollection = collection;
336 }
337 
338 void TrashJob::keepTrashInCollection( bool enable )
339 {
340  Q_D( TrashJob );
341  d->mKeepTrashInCollection = enable;
342 }
343 
344 void TrashJob::deleteIfInTrash( bool enable )
345 {
346  Q_D( TrashJob );
347  d->mDeleteIfInTrash = enable;
348 }
349 
350 void TrashJob::doStart()
351 {
352  Q_D( TrashJob );
353 
354  //Fetch items first to ensure that the EntityDeletedAttribute is available
355  if ( !d->mItems.isEmpty() ) {
356  ItemFetchJob *job = new ItemFetchJob( d->mItems, this );
357  job->fetchScope().setAncestorRetrieval( Akonadi::ItemFetchScope::Parent ); //so we have access to the resource
358  //job->fetchScope().setCacheOnly(true);
359  job->fetchScope().fetchAttribute<EntityDeletedAttribute>( true );
360  connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(itemsReceived(Akonadi::Item::List)) );
361 
362  } else if ( d->mCollection.isValid() ) {
363  CollectionFetchJob *job = new CollectionFetchJob( d->mCollection, CollectionFetchJob::Base, this );
364  job->fetchScope().setAncestorRetrieval( Akonadi::CollectionFetchScope::Parent );
365  connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
366 
367  } else {
368  kWarning() << "No valid collection or empty itemlist";
369  setError( Job::Unknown );
370  setErrorText( i18n( "No valid collection or empty itemlist" ) );
371  emitResult();
372  }
373 }
374 
375 #include "moc_trashjob.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