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

akonadi

trashjob.cpp
00001 /*
00002     Copyright (c) 2011 Christian Mollekopf <chrigi_1@fastmail.fm>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #include "trashjob.h"
00021 
00022 #include "collection.h"
00023 #include "entitydeletedattribute.h"
00024 #include "item.h"
00025 #include "job_p.h"
00026 #include "trashsettings.h"
00027 
00028 #include <KLocale>
00029 
00030 #include <akonadi/itemdeletejob.h>
00031 #include <akonadi/collectiondeletejob.h>
00032 #include <akonadi/itemmovejob.h>
00033 #include <akonadi/collectionmovejob.h>
00034 #include <akonadi/itemmodifyjob.h>
00035 #include <akonadi/collectionmodifyjob.h>
00036 #include <akonadi/itemfetchscope.h>
00037 #include <akonadi/collectionfetchscope.h>
00038 #include <akonadi/itemfetchjob.h>
00039 #include <akonadi/collectionfetchjob.h>
00040 
00041 #include <QHash>
00042 
00043 using namespace Akonadi;
00044 
00045 class TrashJob::TrashJobPrivate : public JobPrivate
00046 {
00047   public:
00048     TrashJobPrivate( TrashJob *parent )
00049         : JobPrivate( parent ),
00050         mKeepTrashInCollection( false ),
00051         mSetRestoreCollection( false ),
00052         mDeleteIfInTrash( false ) {
00053     }
00054 //4.
00055     void selectResult( KJob *job );
00056 //3.
00057     //Helper functions to recursivly set the attribute on deleted collections
00058     void setAttribute( const Akonadi::Collection::List & );
00059     void setAttribute( const Akonadi::Item::List & );
00060     //Set attributes after ensuring that move job was successful
00061     void setAttribute( KJob *job );
00062 
00063 //2.
00064     //called after parent of the trashed item was fetched (needed to see in which resource the item is in)
00065     void parentCollectionReceived( const Akonadi::Collection::List & );
00066 
00067 
00068 //1.
00069     //called after initial fetch of trashed items
00070     void itemsReceived( const Akonadi::Item::List & );
00071     //called after initial fetch of trashed collection
00072     void collectionsReceived( const Akonadi::Collection::List & );
00073 
00074 
00075     Q_DECLARE_PUBLIC( TrashJob )
00076 
00077     Item::List mItems;
00078     Collection mCollection;
00079     Collection mRestoreCollection;
00080     Collection mTrashCollection;
00081     bool mKeepTrashInCollection;
00082     bool mSetRestoreCollection; //only set restore collection when moved to trash collection (not in place)
00083     bool mDeleteIfInTrash;
00084     QHash<Collection, Item::List> mCollectionItems; //list of trashed items sorted according to parent collection
00085     QHash<Entity::Id, Collection> mParentCollections; //fetched parent collcetion of items (containing the resource name)
00086 
00087 };
00088 
00089 void TrashJob::TrashJobPrivate::selectResult( KJob *job )
00090 {
00091   Q_Q( TrashJob );
00092   if ( job->error() ) {
00093     kWarning() << job->objectName();
00094     kWarning() << job->errorString();
00095     return; // KCompositeJob takes care of errors
00096   }
00097 
00098   if ( !q->hasSubjobs() || ( q->subjobs().contains( static_cast<KJob*>( q->sender() ) ) && q->subjobs().size() == 1 ) ) {
00099     q->emitResult();
00100   }
00101 }
00102 
00103 void TrashJob::TrashJobPrivate::setAttribute( const Akonadi::Collection::List &list )
00104 {
00105   Q_Q( TrashJob );
00106   QListIterator<Collection> i( list );
00107   while ( i.hasNext() ) {
00108     const Collection &col = i.next();
00109     EntityDeletedAttribute *eda = new EntityDeletedAttribute();
00110     if ( mSetRestoreCollection ) {
00111       Q_ASSERT( mRestoreCollection.isValid() );
00112       eda->setRestoreCollection( mRestoreCollection );
00113     }
00114 
00115     Collection modCol( col.id() ); //really only modify attribute (forget old remote ids, etc.), otherwise we have an error because of the move
00116     modCol.addAttribute( eda );
00117 
00118     CollectionModifyJob *job = new CollectionModifyJob( modCol, q );
00119     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00120 
00121     ItemFetchJob *itemFetchJob = new ItemFetchJob( col, q );
00122     //TODO not sure if it is guaranteed that itemsReceived is always before result (otherwise the result is emitted before the attributes are set)
00123     q->connect( itemFetchJob, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(setAttribute(Akonadi::Item::List)) );
00124     q->connect( itemFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00125   }
00126 }
00127 
00128 void TrashJob::TrashJobPrivate::setAttribute( const Akonadi::Item::List &list )
00129 {
00130   Q_Q( TrashJob );
00131   Item::List items = list;
00132   QMutableListIterator<Item> i( items );
00133   while ( i.hasNext() ) {
00134     const Item &item = i.next();
00135     EntityDeletedAttribute *eda = new EntityDeletedAttribute();
00136     if ( mSetRestoreCollection ) {
00137       //When deleting a collection, we want to restore the deleted collection's items restored to the deleted collection's parent, not the items parent
00138       if ( mRestoreCollection.isValid() ) {
00139         eda->setRestoreCollection( mRestoreCollection );
00140       } else {
00141         Q_ASSERT( mParentCollections.contains( item.parentCollection().id() ) );
00142         eda->setRestoreCollection( mParentCollections.value( item.parentCollection().id() ) );
00143       }
00144     }
00145 
00146     Item modItem( item.id() ); //really only modify attribute (forget old remote ids, etc.)
00147     modItem.addAttribute( eda );
00148     ItemModifyJob *job = new ItemModifyJob( modItem, q );
00149     job->setIgnorePayload( true );
00150     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00151   }
00152 
00153   //For some reason it is not possible to apply this change to multiple items at once
00154   /*ItemModifyJob *job = new ItemModifyJob(items, q);
00155   q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );*/
00156 }
00157 
00158 void TrashJob::TrashJobPrivate::setAttribute( KJob* job )
00159 {
00160   Q_Q( TrashJob );
00161   if ( job->error() ) {
00162     kWarning() << job->objectName();
00163     kWarning() << job->errorString();
00164     q->setError( Job::Unknown );
00165     q->setErrorText( i18n( "Move to trash collection failed, aborting trash operation" ) );
00166     return;
00167   }
00168 
00169   //For Items
00170   const QVariant var = job->property( "MovedItems" );
00171   if ( var.isValid() ) {
00172     int id = var.toInt();
00173     Q_ASSERT( id >= 0 );
00174     setAttribute( mCollectionItems.value( Collection( id ) ) );
00175     return;
00176   }
00177 
00178   //For a collection
00179   Q_ASSERT( mCollection.isValid() );
00180   setAttribute( Collection::List() << mCollection );
00181   //Set the attribute on all subcollections and items
00182   CollectionFetchJob *colFetchJob = new CollectionFetchJob( mCollection, CollectionFetchJob::Recursive, q );
00183   q->connect( colFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(setAttribute(Akonadi::Collection::List)) );
00184   q->connect( colFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00185 }
00186 
00187 void TrashJob::TrashJobPrivate::parentCollectionReceived( const Akonadi::Collection::List &collections )
00188 {
00189   Q_Q( TrashJob );
00190   Q_ASSERT( collections.size() == 1 );
00191   const Collection &parentCollection = collections.first();
00192 
00193   //store attribute
00194   Q_ASSERT( !parentCollection.resource().isEmpty() );
00195   Collection trashCollection = mTrashCollection;
00196   if ( !mTrashCollection.isValid() ) {
00197     trashCollection = TrashSettings::getTrashCollection( parentCollection.resource() );
00198   }
00199   if ( !mKeepTrashInCollection && trashCollection.isValid() ) { //Only set the restore collection if the item is moved to trash
00200     mSetRestoreCollection = true;
00201   }
00202 
00203   mParentCollections.insert( parentCollection.id(), parentCollection );
00204 
00205   if ( trashCollection.isValid() ) {  //Move the items to the correct collection if available
00206     ItemMoveJob *job = new ItemMoveJob( mCollectionItems.value( parentCollection ), trashCollection, q );
00207     job->setProperty( "MovedItems", parentCollection.id() );
00208     q->connect( job, SIGNAL(result(KJob*)), SLOT(setAttribute(KJob*)) ); //Wait until the move finished to set the attirbute
00209     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00210   } else {
00211     setAttribute( mCollectionItems.value( parentCollection ) );
00212   }
00213 }
00214 
00215 void TrashJob::TrashJobPrivate::itemsReceived( const Akonadi::Item::List &items )
00216 {
00217   Q_Q( TrashJob );
00218   if ( items.isEmpty() ) {
00219     q->setError( Job::Unknown );
00220     q->setErrorText( i18n( "Invalid items passed" ) );
00221     q->emitResult();
00222     return;
00223   }
00224 
00225   Item::List toDelete;
00226 
00227   QListIterator<Item> i( items );
00228   while ( i.hasNext() ) {
00229     const Item &item = i.next();
00230     if ( item.hasAttribute<EntityDeletedAttribute>() ) {
00231       toDelete.append( item );
00232       continue;
00233     }
00234     Q_ASSERT( item.parentCollection().isValid() );
00235     mCollectionItems[item.parentCollection()].append( item ); //Sort by parent col ( = restore collection)
00236   }
00237 
00238   foreach( const Collection &col, mCollectionItems.keys() ) { //krazy:exclude=foreach
00239     CollectionFetchJob *job = new CollectionFetchJob( col, Akonadi::CollectionFetchJob::Base, q );
00240     q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
00241                 SLOT(parentCollectionReceived(Akonadi::Collection::List)) );
00242   }
00243 
00244   if ( mDeleteIfInTrash && !toDelete.isEmpty() ) {
00245     ItemDeleteJob *job = new ItemDeleteJob( toDelete, q );
00246     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00247   } else if ( mCollectionItems.isEmpty() ) { //No job started, so we abort the job
00248     kWarning() << "Nothing to do";
00249     q->emitResult();
00250   }
00251 
00252 }
00253 
00254 void TrashJob::TrashJobPrivate::collectionsReceived( const Akonadi::Collection::List &collections )
00255 {
00256   Q_Q( TrashJob );
00257   if ( collections.isEmpty() ) {
00258     q->setError( Job::Unknown );
00259     q->setErrorText( i18n( "Invalid collection passed" ) );
00260     q->emitResult();
00261     return;
00262   }
00263   Q_ASSERT( collections.size() == 1 );
00264   mCollection = collections.first();
00265 
00266   if ( mCollection.hasAttribute<EntityDeletedAttribute>() ) {//marked as deleted
00267     if ( mDeleteIfInTrash ) {
00268       CollectionDeleteJob *job = new CollectionDeleteJob( mCollection, q );
00269       q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00270     } else {
00271       kWarning() << "Nothing to do";
00272       q->emitResult();
00273     }
00274     return;
00275   }
00276 
00277   Collection trashCollection = mTrashCollection;
00278   if ( !mTrashCollection.isValid() ) {
00279     trashCollection = TrashSettings::getTrashCollection( mCollection.resource() );
00280   }
00281   if ( !mKeepTrashInCollection && trashCollection.isValid() ) { //only set the restore collection if the item is moved to trash
00282     mSetRestoreCollection = true;
00283     Q_ASSERT( mCollection.parentCollection().isValid() );
00284     mRestoreCollection = mCollection.parentCollection();
00285     mRestoreCollection.setResource( mCollection.resource() ); //The parent collection doesn't contain the resource, so we have to set it manually
00286   }
00287 
00288   if ( trashCollection.isValid() ) {
00289     CollectionMoveJob *job = new CollectionMoveJob( mCollection, trashCollection, q );
00290     q->connect( job, SIGNAL(result(KJob*)), SLOT(setAttribute(KJob*)) );
00291     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00292   } else {
00293     setAttribute( Collection::List() << mCollection );
00294   }
00295 
00296 }
00297 
00298 
00299 
00300 
00301 TrashJob::TrashJob( const Item & item, QObject * parent )
00302     : Job( new TrashJobPrivate( this ), parent )
00303 {
00304   Q_D( TrashJob );
00305   d->mItems << item;
00306 }
00307 
00308 TrashJob::TrashJob( const Item::List& items, QObject* parent )
00309     : Job( new TrashJobPrivate( this ), parent )
00310 {
00311   Q_D( TrashJob );
00312   d->mItems = items;
00313 }
00314 
00315 TrashJob::TrashJob( const Collection& collection, QObject* parent )
00316     : Job( new TrashJobPrivate( this ), parent )
00317 {
00318   Q_D( TrashJob );
00319   d->mCollection = collection;
00320 }
00321 
00322 TrashJob::~TrashJob()
00323 {
00324 }
00325 
00326 Item::List TrashJob::items() const
00327 {
00328   Q_D( const TrashJob );
00329   return d->mItems;
00330 }
00331 
00332 void TrashJob::setTrashCollection( const Akonadi::Collection &collection )
00333 {
00334   Q_D( TrashJob );
00335   d->mTrashCollection = collection;
00336 }
00337 
00338 void TrashJob::keepTrashInCollection( bool enable )
00339 {
00340   Q_D( TrashJob );
00341   d->mKeepTrashInCollection = enable;
00342 }
00343 
00344 void TrashJob::deleteIfInTrash( bool enable )
00345 {
00346   Q_D( TrashJob );
00347   d->mDeleteIfInTrash = enable;
00348 }
00349 
00350 void TrashJob::doStart()
00351 {
00352   Q_D( TrashJob );
00353 
00354   //Fetch items first to ensure that the EntityDeletedAttribute is available
00355   if ( !d->mItems.isEmpty() ) {
00356     ItemFetchJob *job = new ItemFetchJob( d->mItems, this );
00357     job->fetchScope().setAncestorRetrieval( Akonadi::ItemFetchScope::Parent ); //so we have access to the resource
00358     //job->fetchScope().setCacheOnly(true);
00359     job->fetchScope().fetchAttribute<EntityDeletedAttribute>( true );
00360     connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(itemsReceived(Akonadi::Item::List)) );
00361 
00362   } else if ( d->mCollection.isValid() ) {
00363     CollectionFetchJob *job = new CollectionFetchJob( d->mCollection, CollectionFetchJob::Base, this );
00364     job->fetchScope().setAncestorRetrieval( Akonadi::CollectionFetchScope::Parent );
00365     connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
00366 
00367   } else {
00368     kWarning() << "No valid collection or empty itemlist";
00369     setError( Job::Unknown );
00370     setErrorText( i18n( "No valid collection or empty itemlist" ) );
00371     emitResult();
00372   }
00373 }
00374 
00375 #include "trashjob.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 22:18:37 by doxygen 1.8.0 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.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • 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