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

akonadi

trashrestorejob.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 "trashrestorejob.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/collectionfetchjob.h>
00037 #include <akonadi/itemfetchjob.h>
00038 #include <akonadi/collectionfetchscope.h>
00039 #include <akonadi/itemfetchscope.h>
00040 
00041 #include <QHash>
00042 
00043 
00044 using namespace Akonadi;
00045 
00046 class TrashRestoreJob::TrashRestoreJobPrivate : public JobPrivate
00047 {
00048   public:
00049     TrashRestoreJobPrivate( TrashRestoreJob *parent )
00050         : JobPrivate( parent ) {
00051     }
00052 
00053     void selectResult( KJob * );
00054 
00055     //Called when the target collection was fetched,
00056     //will issue the move and the removal of the attributes if collection is valid
00057     void targetCollectionFetched( KJob * );
00058 
00059     void removeAttribute( const Akonadi::Item::List & );
00060     void removeAttribute( const Akonadi::Collection::List & );
00061 
00062     //Called after initial fetch of items, issues fetch of target collection or removes attributes for in place restore
00063     void itemsReceived( const Akonadi::Item::List & );
00064     void collectionsReceived( const Akonadi::Collection::List & );
00065 
00066     Q_DECLARE_PUBLIC( TrashRestoreJob )
00067 
00068     Item::List mItems;
00069     Collection mCollection;
00070     Collection mTargetCollection;
00071     QHash<Collection, Item::List> restoreCollections; //groups items to target restore collections
00072 
00073 };
00074 
00075 void TrashRestoreJob::TrashRestoreJobPrivate::selectResult( KJob *job )
00076 {
00077   Q_Q( TrashRestoreJob );
00078   if ( job->error() ) {
00079     kWarning() << job->errorString();
00080     return; // KCompositeJob takes care of errors
00081   }
00082 
00083   if ( !q->hasSubjobs() || ( q->subjobs().contains( static_cast<KJob*>( q->sender() ) ) && q->subjobs().size() == 1 ) ) {
00084     //kWarning() << "trash restore finished";
00085     q->emitResult();
00086   }
00087 }
00088 
00089 void TrashRestoreJob::TrashRestoreJobPrivate::targetCollectionFetched( KJob *job )
00090 {
00091   Q_Q( TrashRestoreJob );
00092 
00093   CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob*> ( job );
00094   Q_ASSERT( fetchJob );
00095   const Collection::List &list = fetchJob->collections();
00096 
00097   if ( list.isEmpty() || !list.first().isValid() || list.first().hasAttribute<Akonadi::EntityDeletedAttribute>() ) { //target collection is invalid/not existing
00098 
00099     const QString res = fetchJob->property( "Resource" ).toString();
00100     if ( res.isEmpty() ) { //There is no fallback
00101       q->setError( Job::Unknown );
00102       q->setErrorText( i18n( "Could not find restore collection and restore resource is not available" ) );
00103       q->emitResult();
00104       //FAIL
00105       kWarning() << "restore collection not available";
00106       return;
00107     }
00108 
00109     //Try again with the root collection of the resource as fallback
00110     CollectionFetchJob *resRootFetch = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel, q );
00111     resRootFetch->fetchScope().setResource( res );
00112     const QVariant &var = fetchJob->property( "Items" );
00113     if ( var.isValid() ) {
00114       resRootFetch->setProperty( "Items", var.toInt() );
00115     }
00116     q->connect( resRootFetch, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)) );
00117     q->connect( resRootFetch, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00118     return;
00119   }
00120   Q_ASSERT( list.size() == 1 );
00121   //SUCCESS
00122   //We know where to move the entity, so remove the attributes and move them to the right location
00123   if ( !mItems.isEmpty() ) {
00124     const QVariant &var = fetchJob->property( "Items" );
00125     Q_ASSERT( var.isValid() );
00126     const Item::List &items = restoreCollections[Collection( var.toInt() )];
00127 
00128     //store removed attribute if destination collection is valid or the item doesn't have a restore collection
00129     //TODO only remove the attribute if the move job was successful (although it is unlikely that it fails since we already fetched the collection)
00130     removeAttribute( items );
00131     if ( items.first().parentCollection() != list.first() ) {
00132       ItemMoveJob *job = new ItemMoveJob( items, list.first(), q );
00133       q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00134     }
00135   } else {
00136     Q_ASSERT( mCollection.isValid() );
00137     //TODO only remove the attribute if the move job was successful
00138     removeAttribute( Collection::List() << mCollection );
00139     CollectionFetchJob *collectionFetchJob = new CollectionFetchJob( mCollection, CollectionFetchJob::Recursive, q );
00140     q->connect( collectionFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00141     q->connect( collectionFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(removeAttribute(Akonadi::Collection::List)) );
00142 
00143     if ( mCollection.parentCollection() != list.first() ) {
00144       CollectionMoveJob *job = new CollectionMoveJob( mCollection, list.first(), q );
00145       q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00146     }
00147   }
00148 
00149 }
00150 
00151 void TrashRestoreJob::TrashRestoreJobPrivate::itemsReceived( const Akonadi::Item::List &items )
00152 {
00153   Q_Q( TrashRestoreJob );
00154   if ( items.isEmpty() ) {
00155     q->setError( Job::Unknown );
00156     q->setErrorText( i18n( "Invalid items passed" ) );
00157     q->emitResult();
00158     return;
00159   }
00160   mItems = items;
00161 
00162   //Sort by restore collection
00163   foreach( const Item &item, mItems ) {
00164     if ( !item.hasAttribute<Akonadi::EntityDeletedAttribute>() ) {
00165       continue;
00166     }
00167     //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
00168     restoreCollections[item.attribute<Akonadi::EntityDeletedAttribute>()->restoreCollection()].append( item );
00169   }
00170 
00171   foreach( const Collection &col, restoreCollections.keys() ) { //krazy:exclude=foreach
00172     const Item &first = restoreCollections.value( col ).first();
00173     //Move the items to the correct collection if available
00174     Collection targetCollection = col;
00175     const QString restoreResource = first.attribute<Akonadi::EntityDeletedAttribute>()->restoreResource();
00176 
00177     //Restore in place if no restore collection is set
00178     if ( !targetCollection.isValid() ) {
00179       removeAttribute( restoreCollections.value( col ) );
00180       return;
00181     }
00182 
00183     //Explicit target overrides the resource
00184     if ( mTargetCollection.isValid() ) {
00185       targetCollection = mTargetCollection;
00186     }
00187 
00188     //Try to fetch the target resource to see if it is available
00189     CollectionFetchJob *fetchJob = new CollectionFetchJob( targetCollection, Akonadi::CollectionFetchJob::Base, q );
00190     if ( !mTargetCollection.isValid() ) { //explicit targets don't have a fallback
00191       fetchJob->setProperty( "Resource", restoreResource );
00192     }
00193     fetchJob->setProperty( "Items", col.id() );  //to find the items in restore collections again
00194     q->connect( fetchJob, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)) );
00195   }
00196 }
00197 
00198 
00199 void TrashRestoreJob::TrashRestoreJobPrivate::collectionsReceived( const Akonadi::Collection::List &collections )
00200 {
00201   Q_Q( TrashRestoreJob );
00202   if ( collections.isEmpty() ) {
00203     q->setError( Job::Unknown );
00204     q->setErrorText( i18n( "Invalid collection passed" ) );
00205     q->emitResult();
00206     return;
00207   }
00208   Q_ASSERT( collections.size() == 1 );
00209   mCollection = collections.first();
00210 
00211   if ( !mCollection.hasAttribute<Akonadi::EntityDeletedAttribute>() ) {
00212     return;
00213   }
00214 
00215   const QString restoreResource = mCollection.attribute<Akonadi::EntityDeletedAttribute>()->restoreResource();
00216   Collection targetCollection = mCollection.attribute<EntityDeletedAttribute>()->restoreCollection();
00217 
00218   //Restore in place if no restore collection/resource is set
00219   if ( !targetCollection.isValid() ) {
00220     removeAttribute( Collection::List() << mCollection );
00221     CollectionFetchJob *collectionFetchJob = new CollectionFetchJob( mCollection, CollectionFetchJob::Recursive, q );
00222     q->connect( collectionFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00223     q->connect( collectionFetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(removeAttribute(Akonadi::Collection::List)) );
00224     return;
00225   }
00226 
00227   //Explicit target overrides the resource/configured restore collection
00228   if ( mTargetCollection.isValid() ) {
00229     targetCollection = mTargetCollection;
00230   }
00231 
00232   //Fetch the target collection to check if it's valid
00233   CollectionFetchJob *fetchJob = new CollectionFetchJob( targetCollection, CollectionFetchJob::Base, q );
00234   if ( !mTargetCollection.isValid() ) { //explicit targets don't have a fallback
00235     fetchJob->setProperty( "Resource", restoreResource );
00236   }
00237   q->connect( fetchJob, SIGNAL(result(KJob*)), SLOT(targetCollectionFetched(KJob*)) );
00238 }
00239 
00240 
00241 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute( const Akonadi::Collection::List &list )
00242 {
00243   Q_Q( TrashRestoreJob );
00244   QListIterator<Collection> i( list );
00245   while ( i.hasNext() ) {
00246     Collection col = i.next();
00247     col.removeAttribute<EntityDeletedAttribute>();
00248 
00249     CollectionModifyJob *job = new CollectionModifyJob( col, q );
00250     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00251 
00252     ItemFetchJob *itemFetchJob = new ItemFetchJob( col, q );
00253     itemFetchJob->fetchScope().fetchAttribute<EntityDeletedAttribute> ( true );
00254     q->connect( itemFetchJob, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00255     q->connect( itemFetchJob, SIGNAL(itemsReceived(Akonadi::Item::List)), SLOT(removeAttribute(Akonadi::Item::List)) );
00256   }
00257 }
00258 
00259 void TrashRestoreJob::TrashRestoreJobPrivate::removeAttribute( const Akonadi::Item::List &list )
00260 {
00261   Q_Q( TrashRestoreJob );
00262   Item::List items = list;
00263   QMutableListIterator<Item> i( items );
00264   while ( i.hasNext() ) {
00265     Item &item = i.next();
00266     item.removeAttribute<EntityDeletedAttribute>();
00267     ItemModifyJob *job = new ItemModifyJob( item, q );
00268     job->setIgnorePayload( true );
00269     q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00270   }
00271   //For some reason it is not possible to apply this change to multiple items at once
00272   //ItemModifyJob *job = new ItemModifyJob(items, q);
00273   //q->connect( job, SIGNAL(result(KJob*)), SLOT(selectResult(KJob*)) );
00274 }
00275 
00276 
00277 TrashRestoreJob::TrashRestoreJob( const Item & item, QObject * parent )
00278     : Job( new TrashRestoreJobPrivate( this ), parent )
00279 {
00280   Q_D( TrashRestoreJob );
00281   d->mItems << item;
00282 }
00283 
00284 TrashRestoreJob::TrashRestoreJob( const Item::List& items, QObject* parent )
00285     : Job( new TrashRestoreJobPrivate( this ), parent )
00286 {
00287   Q_D( TrashRestoreJob );
00288   d->mItems = items;
00289 }
00290 
00291 TrashRestoreJob::TrashRestoreJob( const Collection& collection, QObject* parent )
00292     : Job( new TrashRestoreJobPrivate( this ), parent )
00293 {
00294   Q_D( TrashRestoreJob );
00295   d->mCollection = collection;
00296 }
00297 
00298 TrashRestoreJob::~TrashRestoreJob()
00299 {
00300 }
00301 
00302 void TrashRestoreJob::setTargetCollection( const Akonadi::Collection collection )
00303 {
00304   Q_D( TrashRestoreJob );
00305   d->mTargetCollection = collection;
00306 }
00307 
00308 
00309 Item::List TrashRestoreJob::items() const
00310 {
00311   Q_D( const TrashRestoreJob );
00312   return d->mItems;
00313 }
00314 
00315 void TrashRestoreJob::doStart()
00316 {
00317   Q_D( TrashRestoreJob );
00318 
00319   //We always have to fetch the entities to ensure that the EntityDeletedAttribute is available
00320   if ( !d->mItems.isEmpty() ) {
00321     ItemFetchJob *job = new ItemFetchJob( d->mItems, this );
00322     job->fetchScope().setCacheOnly( true );
00323     job->fetchScope().fetchAttribute<EntityDeletedAttribute> ( true );
00324     connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(itemsReceived(Akonadi::Item::List)) );
00325   } else if ( d->mCollection.isValid() ) {
00326       CollectionFetchJob *job = new CollectionFetchJob( d->mCollection, CollectionFetchJob::Base, this );
00327       connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
00328   }  else {
00329     kWarning() << "No valid collection or empty itemlist";
00330     setError( Job::Unknown );
00331     setErrorText( i18n( "No valid collection or empty itemlist" ) );
00332     emitResult();
00333   }
00334 
00335 }
00336 
00337 #include "trashrestorejob.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:09:25 by doxygen 1.7.5 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.5 API Reference

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