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

akonadi

  • akonadi
resourcebase.cpp
1 /*
2  Copyright (c) 2006 Till Adam <adam@kde.org>
3  Copyright (c) 2007 Volker Krause <vkrause@kde.org>
4 
5  This library is free software; you can redistribute it and/or modify it
6  under the terms of the GNU Library General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or (at your
8  option) any later version.
9 
10  This library is distributed in the hope that it will be useful, but WITHOUT
11  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13  License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to the
17  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  02110-1301, USA.
19 */
20 
21 #include "resourcebase.h"
22 #include "agentbase_p.h"
23 
24 #include "resourceadaptor.h"
25 #include "collectiondeletejob.h"
26 #include "collectionsync_p.h"
27 #include "dbusconnectionpool.h"
28 #include "itemsync.h"
29 #include "kdepimlibs-version.h"
30 #include "resourcescheduler_p.h"
31 #include "tracerinterface.h"
32 #include "xdgbasedirs_p.h"
33 
34 #include "changerecorder.h"
35 #include "collectionfetchjob.h"
36 #include "collectionfetchscope.h"
37 #include "collectionmodifyjob.h"
38 #include "invalidatecachejob_p.h"
39 #include "itemfetchjob.h"
40 #include "itemfetchscope.h"
41 #include "itemmodifyjob.h"
42 #include "itemmodifyjob_p.h"
43 #include "session.h"
44 #include "resourceselectjob_p.h"
45 #include "monitor_p.h"
46 #include "servermanager_p.h"
47 #include "recursivemover_p.h"
48 
49 #include <kaboutdata.h>
50 #include <kcmdlineargs.h>
51 #include <kdebug.h>
52 #include <klocale.h>
53 
54 #include <QtCore/QDebug>
55 #include <QtCore/QDir>
56 #include <QtCore/QHash>
57 #include <QtCore/QSettings>
58 #include <QtCore/QTimer>
59 #include <QApplication>
60 #include <QtDBus/QtDBus>
61 
62 using namespace Akonadi;
63 
64 class Akonadi::ResourceBasePrivate : public AgentBasePrivate
65 {
66  Q_OBJECT
67  Q_CLASSINFO( "D-Bus Interface", "org.kde.dfaure" )
68 
69  public:
70  ResourceBasePrivate( ResourceBase *parent )
71  : AgentBasePrivate( parent ),
72  scheduler( 0 ),
73  mItemSyncer( 0 ),
74  mItemSyncFetchScope( 0 ),
75  mItemTransactionMode( ItemSync::SingleTransaction ),
76  mCollectionSyncer( 0 ),
77  mHierarchicalRid( false ),
78  mUnemittedProgress( 0 ),
79  mAutomaticProgressReporting( true )
80  {
81  Internal::setClientType( Internal::Resource );
82  mStatusMessage = defaultReadyMessage();
83  mProgressEmissionCompressor.setInterval( 1000 );
84  mProgressEmissionCompressor.setSingleShot( true );
85  }
86 
87  ~ResourceBasePrivate()
88  {
89  delete mItemSyncFetchScope;
90  }
91 
92  Q_DECLARE_PUBLIC( ResourceBase )
93 
94  void delayedInit()
95  {
96  const QString serviceId = ServerManager::agentServiceName( ServerManager::Resource, mId );
97  if ( !DBusConnectionPool::threadConnection().registerService( serviceId ) ) {
98  QString reason = DBusConnectionPool::threadConnection().lastError().message();
99  if ( reason.isEmpty() ) {
100  reason = QString::fromLatin1( "this service is probably running already." );
101  }
102  kError() << "Unable to register service" << serviceId << "at D-Bus:" << reason;
103 
104  if ( QThread::currentThread() == QCoreApplication::instance()->thread() )
105  QCoreApplication::instance()->exit(1);
106 
107  } else {
108  AgentBasePrivate::delayedInit();
109  }
110  }
111 
112  virtual void changeProcessed()
113  {
114  if ( m_recursiveMover ) {
115  m_recursiveMover->changeProcessed();
116  QTimer::singleShot( 0, m_recursiveMover, SLOT(replayNext()) );
117  return;
118  }
119 
120  mChangeRecorder->changeProcessed();
121  if ( !mChangeRecorder->isEmpty() )
122  scheduler->scheduleChangeReplay();
123  scheduler->taskDone();
124  }
125 
126  void slotAbortRequested();
127 
128  void slotDeliveryDone( KJob* job );
129  void slotCollectionSyncDone( KJob *job );
130  void slotLocalListDone( KJob *job );
131  void slotSynchronizeCollection( const Collection &col );
132  void slotCollectionListDone( KJob *job );
133  void slotSynchronizeCollectionAttributes( const Collection &col );
134  void slotCollectionListForAttributesDone( KJob *job );
135  void slotCollectionAttributesSyncDone( KJob *job );
136 
137  void slotItemSyncDone( KJob *job );
138 
139  void slotPercent( KJob* job, unsigned long percent );
140  void slotDelayedEmitProgress();
141  void slotDeleteResourceCollection();
142  void slotDeleteResourceCollectionDone( KJob *job );
143  void slotCollectionDeletionDone( KJob *job );
144 
145  void slotInvalidateCache( const Akonadi::Collection &collection );
146 
147  void slotPrepareItemRetrieval( const Akonadi::Item &item );
148  void slotPrepareItemRetrievalResult( KJob* job );
149 
150  void changeCommittedResult( KJob* job );
151 
152  void slotRecursiveMoveReplay( RecursiveMover *mover );
153  void slotRecursiveMoveReplayResult( KJob *job );
154 
155  void slotSessionReconnected()
156  {
157  Q_Q( ResourceBase );
158 
159  new ResourceSelectJob( q->identifier() );
160  }
161 
162  void createItemSyncInstanceIfMissing()
163  {
164  Q_Q( ResourceBase );
165  Q_ASSERT_X( scheduler->currentTask().type == ResourceScheduler::SyncCollection,
166  "createItemSyncInstance", "Calling items retrieval methods although no item retrieval is in progress" );
167  if ( !mItemSyncer ) {
168  mItemSyncer = new ItemSync( q->currentCollection() );
169  mItemSyncer->setTransactionMode( mItemTransactionMode );
170  if ( mItemSyncFetchScope )
171  mItemSyncer->setFetchScope( *mItemSyncFetchScope );
172  mItemSyncer->setProperty( "collection", QVariant::fromValue( q->currentCollection() ) );
173  connect( mItemSyncer, SIGNAL(percent(KJob*,ulong)), q, SLOT(slotPercent(KJob*,ulong)) );
174  connect( mItemSyncer, SIGNAL(result(KJob*)), q, SLOT(slotItemSyncDone(KJob*)) );
175  }
176  Q_ASSERT( mItemSyncer );
177  }
178 
179  public Q_SLOTS:
180  // Dump the contents of the current ChangeReplay
181  Q_SCRIPTABLE QString dumpNotificationListToString() const
182  {
183  return mChangeRecorder->dumpNotificationListToString();
184  }
185 
186  // Dump the state of the scheduler
187  Q_SCRIPTABLE QString dumpToString() const
188  {
189  Q_Q( const ResourceBase );
190  QString retVal;
191  QMetaObject::invokeMethod( const_cast<ResourceBase *>(q), "dumpResourceToString", Qt::DirectConnection, Q_RETURN_ARG(QString, retVal) );
192  return scheduler->dumpToString() + QLatin1Char('\n') + retVal;
193  }
194 
195  Q_SCRIPTABLE void dump()
196  {
197  scheduler->dump();
198  }
199 
200  Q_SCRIPTABLE void clear()
201  {
202  scheduler->clear();
203  }
204 
205  protected Q_SLOTS:
206  // reimplementations from AgentbBasePrivate, containing sanity checks that only apply to resources
207  // such as making sure that RIDs are present as well as translations of cross-resource moves
208  // TODO: we could possibly add recovery code for no-RID notifications by re-enquing those to the change recorder
209  // as the corresponding Add notifications, although that contains a risk of endless fail/retry loops
210 
211  void itemAdded(const Akonadi::Item& item, const Akonadi::Collection& collection)
212  {
213  if ( collection.remoteId().isEmpty() ) {
214  changeProcessed();
215  return;
216  }
217  AgentBasePrivate::itemAdded( item, collection );
218  }
219 
220  void itemChanged(const Akonadi::Item& item, const QSet< QByteArray >& partIdentifiers)
221  {
222  if ( item.remoteId().isEmpty() ) {
223  changeProcessed();
224  return;
225  }
226  AgentBasePrivate::itemChanged( item, partIdentifiers );
227  }
228 
229  // TODO move the move translation code from AgebtBasePrivate here, it's wrong for agents
230  void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &destination)
231  {
232  if ( item.remoteId().isEmpty() || destination.remoteId().isEmpty() || destination == source ) {
233  changeProcessed();
234  return;
235  }
236  AgentBasePrivate::itemMoved( item, source, destination );
237  }
238 
239  void itemRemoved(const Akonadi::Item& item)
240  {
241  if ( item.remoteId().isEmpty() ) {
242  changeProcessed();
243  return;
244  }
245  AgentBasePrivate::itemRemoved( item );
246  }
247 
248  void collectionAdded(const Akonadi::Collection& collection, const Akonadi::Collection& parent)
249  {
250  if ( parent.remoteId().isEmpty() ) {
251  changeProcessed();
252  return;
253  }
254  AgentBasePrivate::collectionAdded( collection, parent );
255  }
256 
257  void collectionChanged(const Akonadi::Collection& collection)
258  {
259  if ( collection.remoteId().isEmpty() ) {
260  changeProcessed();
261  return;
262  }
263  AgentBasePrivate::collectionChanged( collection );
264  }
265 
266  void collectionChanged(const Akonadi::Collection& collection, const QSet< QByteArray >& partIdentifiers)
267  {
268  if ( collection.remoteId().isEmpty() ) {
269  changeProcessed();
270  return;
271  }
272  AgentBasePrivate::collectionChanged( collection, partIdentifiers );
273  }
274 
275  void collectionMoved(const Akonadi::Collection& collection, const Akonadi::Collection& source, const Akonadi::Collection& destination)
276  {
277  // unknown destination or source == destination means we can't do/don't have to do anything
278  if ( destination.remoteId().isEmpty() || source == destination ) {
279  changeProcessed();
280  return;
281  }
282 
283  // inter-resource moves, requires we know which resources the source and destination are in though
284  if ( !source.resource().isEmpty() && !destination.resource().isEmpty() && source.resource() != destination.resource() ) {
285  if ( source.resource() == q_ptr->identifier() ) { // moved away from us
286  AgentBasePrivate::collectionRemoved( collection );
287  } else if ( destination.resource() == q_ptr->identifier() ) { // moved to us
288  scheduler->taskDone(); // stop change replay for now
289  RecursiveMover *mover = new RecursiveMover( this );
290  mover->setCollection( collection, destination );
291  scheduler->scheduleMoveReplay( collection, mover );
292  }
293  return;
294  }
295 
296  // intra-resource move, requires the moved collection to have a valid id though
297  if ( collection.remoteId().isEmpty() ) {
298  changeProcessed();
299  return;
300  }
301 
302  // intra-resource move, ie. something we can handle internally
303  AgentBasePrivate::collectionMoved( collection, source, destination );
304  }
305 
306  void collectionRemoved(const Akonadi::Collection& collection)
307  {
308  if ( collection.remoteId().isEmpty() ) {
309  changeProcessed();
310  return;
311  }
312  AgentBasePrivate::collectionRemoved( collection );
313  }
314 
315  public:
316  // synchronize states
317  Collection currentCollection;
318 
319  ResourceScheduler *scheduler;
320  ItemSync *mItemSyncer;
321  ItemFetchScope *mItemSyncFetchScope;
322  ItemSync::TransactionMode mItemTransactionMode;
323  CollectionSync *mCollectionSyncer;
324  bool mHierarchicalRid;
325  QTimer mProgressEmissionCompressor;
326  int mUnemittedProgress;
327  QMap<Akonadi::Collection::Id, QVariantMap> mUnemittedAdvancedStatus;
328  bool mAutomaticProgressReporting;
329  QPointer<RecursiveMover> m_recursiveMover;
330 };
331 
332 ResourceBase::ResourceBase( const QString & id )
333  : AgentBase( new ResourceBasePrivate( this ), id )
334 {
335  Q_D( ResourceBase );
336 
337  new Akonadi__ResourceAdaptor( this );
338 
339  d->scheduler = new ResourceScheduler( this );
340 
341  d->mChangeRecorder->setChangeRecordingEnabled( true );
342  d->mChangeRecorder->setCollectionMoveTranslationEnabled( false ); // we deal with this ourselves
343  connect( d->mChangeRecorder, SIGNAL(changesAdded()),
344  d->scheduler, SLOT(scheduleChangeReplay()) );
345 
346  d->mChangeRecorder->setResourceMonitored( d->mId.toLatin1() );
347  d->mChangeRecorder->fetchCollection( true );
348 
349  connect( d->scheduler, SIGNAL(executeFullSync()),
350  SLOT(retrieveCollections()) );
351  connect( d->scheduler, SIGNAL(executeCollectionTreeSync()),
352  SLOT(retrieveCollections()) );
353  connect( d->scheduler, SIGNAL(executeCollectionSync(Akonadi::Collection)),
354  SLOT(slotSynchronizeCollection(Akonadi::Collection)) );
355  connect( d->scheduler, SIGNAL(executeCollectionAttributesSync(Akonadi::Collection)),
356  SLOT(slotSynchronizeCollectionAttributes(Akonadi::Collection)) );
357  connect( d->scheduler, SIGNAL(executeItemFetch(Akonadi::Item,QSet<QByteArray>)),
358  SLOT(slotPrepareItemRetrieval(Akonadi::Item)) );
359  connect( d->scheduler, SIGNAL(executeResourceCollectionDeletion()),
360  SLOT(slotDeleteResourceCollection()) );
361  connect ( d->scheduler, SIGNAL(executeCacheInvalidation(Akonadi::Collection)),
362  SLOT(slotInvalidateCache(Akonadi::Collection)) );
363  connect( d->scheduler, SIGNAL(status(int,QString)),
364  SIGNAL(status(int,QString)) );
365  connect( d->scheduler, SIGNAL(executeChangeReplay()),
366  d->mChangeRecorder, SLOT(replayNext()) );
367  connect( d->scheduler, SIGNAL(executeRecursiveMoveReplay(RecursiveMover*)),
368  SLOT(slotRecursiveMoveReplay(RecursiveMover*)) );
369  connect( d->scheduler, SIGNAL(fullSyncComplete()), SIGNAL(synchronized()) );
370  connect( d->scheduler, SIGNAL(collectionTreeSyncComplete()), SIGNAL(collectionTreeSynchronized()) );
371  connect( d->mChangeRecorder, SIGNAL(nothingToReplay()), d->scheduler, SLOT(taskDone()) );
372  connect( d->mChangeRecorder, SIGNAL(collectionRemoved(Akonadi::Collection)),
373  d->scheduler, SLOT(collectionRemoved(Akonadi::Collection)) );
374  connect( this, SIGNAL(abortRequested()), this, SLOT(slotAbortRequested()) );
375  connect( this, SIGNAL(synchronized()), d->scheduler, SLOT(taskDone()) );
376  connect( this, SIGNAL(collectionTreeSynchronized()), d->scheduler, SLOT(taskDone()) );
377  connect( this, SIGNAL(agentNameChanged(QString)),
378  this, SIGNAL(nameChanged(QString)) );
379 
380  connect( &d->mProgressEmissionCompressor, SIGNAL(timeout()),
381  this, SLOT(slotDelayedEmitProgress()) );
382 
383  d->scheduler->setOnline( d->mOnline );
384  if ( !d->mChangeRecorder->isEmpty() )
385  d->scheduler->scheduleChangeReplay();
386 
387  DBusConnectionPool::threadConnection().registerObject( QLatin1String( "/Debug" ), d, QDBusConnection::ExportScriptableSlots );
388 
389  new ResourceSelectJob( identifier() );
390 
391  connect( d->mChangeRecorder->session(), SIGNAL(reconnected()), SLOT(slotSessionReconnected()) );
392 }
393 
394 ResourceBase::~ResourceBase()
395 {
396 }
397 
398 void ResourceBase::synchronize()
399 {
400  d_func()->scheduler->scheduleFullSync();
401 }
402 
403 void ResourceBase::setName( const QString &name )
404 {
405  AgentBase::setAgentName( name );
406 }
407 
408 QString ResourceBase::name() const
409 {
410  return AgentBase::agentName();
411 }
412 
413 QString ResourceBase::parseArguments( int argc, char **argv )
414 {
415  QString identifier;
416  if ( argc < 3 ) {
417  kDebug() << "Not enough arguments passed...";
418  exit( 1 );
419  }
420 
421  for ( int i = 1; i < argc - 1; ++i ) {
422  if ( QLatin1String( argv[ i ] ) == QLatin1String( "--identifier" ) )
423  identifier = QLatin1String( argv[ i + 1 ] );
424  }
425 
426  if ( identifier.isEmpty() ) {
427  kDebug() << "Identifier argument missing";
428  exit( 1 );
429  }
430 
431  const QFileInfo fi( QString::fromLocal8Bit( argv[0] ) );
432  // strip off full path and possible .exe suffix
433  const QByteArray catalog = fi.baseName().toLatin1();
434 
435  KCmdLineArgs::init( argc, argv, ServerManager::addNamespace( identifier ).toLatin1(), catalog,
436  ki18nc( "@title application name", "Akonadi Resource" ), KDEPIMLIBS_VERSION,
437  ki18nc( "@title application description", "Akonadi Resource" ) );
438 
439  KCmdLineOptions options;
440  options.add( "identifier <argument>",
441  ki18nc( "@label commandline option", "Resource identifier" ) );
442  KCmdLineArgs::addCmdLineOptions( options );
443 
444  return identifier;
445 }
446 
447 int ResourceBase::init( ResourceBase *r )
448 {
449  QApplication::setQuitOnLastWindowClosed( false );
450  KGlobal::locale()->insertCatalog( QLatin1String( "libakonadi" ) );
451  int rv = kapp->exec();
452  delete r;
453  return rv;
454 }
455 
456 void ResourceBasePrivate::slotAbortRequested()
457 {
458  Q_Q( ResourceBase );
459 
460  scheduler->cancelQueues();
461  QMetaObject::invokeMethod( q, "abortActivity" );
462 }
463 
464 void ResourceBase::itemRetrieved( const Item &item )
465 {
466  Q_D( ResourceBase );
467  Q_ASSERT( d->scheduler->currentTask().type == ResourceScheduler::FetchItem );
468  if ( !item.isValid() ) {
469  d->scheduler->currentTask().sendDBusReplies( i18nc( "@info", "Invalid item retrieved" ) );
470  d->scheduler->taskDone();
471  return;
472  }
473 
474  Item i( item );
475  QSet<QByteArray> requestedParts = d->scheduler->currentTask().itemParts;
476  foreach ( const QByteArray &part, requestedParts ) {
477  if ( !item.loadedPayloadParts().contains( part ) ) {
478  kWarning() << "Item does not provide part" << part;
479  }
480  }
481 
482  ItemModifyJob *job = new ItemModifyJob( i );
483  // FIXME: remove once the item with which we call retrieveItem() has a revision number
484  job->disableRevisionCheck();
485  connect( job, SIGNAL(result(KJob*)), SLOT(slotDeliveryDone(KJob*)) );
486 }
487 
488 void ResourceBasePrivate::slotDeliveryDone(KJob * job)
489 {
490  Q_Q( ResourceBase );
491  Q_ASSERT( scheduler->currentTask().type == ResourceScheduler::FetchItem );
492  if ( job->error() ) {
493  emit q->error( i18nc( "@info", "Error while creating item: %1" ).arg( job->errorString() ) );
494  }
495  scheduler->currentTask().sendDBusReplies( job->error() ? job->errorString() : QString() );
496  scheduler->taskDone();
497 }
498 
499 void ResourceBase::collectionAttributesRetrieved( const Collection &collection )
500 {
501  Q_D( ResourceBase );
502  Q_ASSERT( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes );
503  if ( !collection.isValid() ) {
504  emit attributesSynchronized( d->scheduler->currentTask().collection.id() );
505  d->scheduler->taskDone();
506  return;
507  }
508 
509  CollectionModifyJob *job = new CollectionModifyJob( collection );
510  connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionAttributesSyncDone(KJob*)) );
511 }
512 
513 void ResourceBasePrivate::slotCollectionAttributesSyncDone(KJob * job)
514 {
515  Q_Q( ResourceBase );
516  Q_ASSERT( scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes );
517  if ( job->error() ) {
518  emit q->error( i18nc( "@info", "Error while updating collection: %1" ).arg( job->errorString() ) );
519  }
520  emit q->attributesSynchronized( scheduler->currentTask().collection.id() );
521  scheduler->taskDone();
522 }
523 
524 void ResourceBasePrivate::slotDeleteResourceCollection()
525 {
526  Q_Q( ResourceBase );
527 
528  CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel );
529  job->fetchScope().setResource( q->identifier() );
530  connect( job, SIGNAL(result(KJob*)), q, SLOT(slotDeleteResourceCollectionDone(KJob*)) );
531 }
532 
533 void ResourceBasePrivate::slotDeleteResourceCollectionDone( KJob *job )
534 {
535  Q_Q( ResourceBase );
536  if ( job->error() ) {
537  emit q->error( job->errorString() );
538  scheduler->taskDone();
539  } else {
540  const CollectionFetchJob *fetchJob = static_cast<const CollectionFetchJob*>( job );
541 
542  if ( !fetchJob->collections().isEmpty() ) {
543  CollectionDeleteJob *job = new CollectionDeleteJob( fetchJob->collections().first() );
544  connect( job, SIGNAL(result(KJob*)), q, SLOT(slotCollectionDeletionDone(KJob*)) );
545  } else {
546  // there is no resource collection, so just ignore the request
547  scheduler->taskDone();
548  }
549  }
550 }
551 
552 void ResourceBasePrivate::slotCollectionDeletionDone( KJob *job )
553 {
554  Q_Q( ResourceBase );
555  if ( job->error() ) {
556  emit q->error( job->errorString() );
557  }
558 
559  scheduler->taskDone();
560 }
561 
562 void ResourceBasePrivate::slotInvalidateCache( const Akonadi::Collection &collection )
563 {
564  Q_Q( ResourceBase );
565  InvalidateCacheJob *job = new InvalidateCacheJob( collection, q );
566  connect( job, SIGNAL(result(KJob*)), scheduler, SLOT(taskDone()) );
567 }
568 
569 void ResourceBase::changeCommitted( const Item& item )
570 {
571  Q_D( ResourceBase );
572  ItemModifyJob *job = new ItemModifyJob( item );
573  job->d_func()->setClean();
574  job->disableRevisionCheck(); // TODO: remove, but where/how do we handle the error?
575  job->setIgnorePayload( true ); // we only want to reset the dirty flag and update the remote id
576  d->changeProcessed();
577 }
578 
579 void ResourceBase::changeCommitted( const Collection &collection )
580 {
581  CollectionModifyJob *job = new CollectionModifyJob( collection );
582  connect( job, SIGNAL(result(KJob*)), SLOT(changeCommittedResult(KJob*)) );
583 }
584 
585 void ResourceBasePrivate::changeCommittedResult( KJob *job )
586 {
587  Q_Q( ResourceBase );
588  if ( job->error() )
589  emit q->error( i18nc( "@info", "Updating local collection failed: %1.", job->errorText() ) );
590  mChangeRecorder->d_ptr->invalidateCache( static_cast<CollectionModifyJob*>( job )->collection() );
591  changeProcessed();
592 }
593 
594 bool ResourceBase::requestItemDelivery( qint64 uid, const QString &remoteId,
595  const QString &mimeType, const QStringList &parts )
596 {
597  return requestItemDeliveryV2( uid, remoteId, mimeType, parts ).isEmpty();
598 }
599 
600 QString ResourceBase::requestItemDeliveryV2(qint64 uid, const QString &remoteId, const QString &mimeType, const QStringList &_parts)
601 {
602  Q_D( ResourceBase );
603  if ( !isOnline() ) {
604  const QString errorMsg = i18nc( "@info", "Cannot fetch item in offline mode." );
605  emit error( errorMsg );
606  return errorMsg;
607  }
608 
609  setDelayedReply( true );
610  // FIXME: we need at least the revision number too
611  Item item( uid );
612  item.setMimeType( mimeType );
613  item.setRemoteId( remoteId );
614 
615  QSet<QByteArray> parts;
616  Q_FOREACH( const QString &str, _parts )
617  parts.insert( str.toLatin1() );
618 
619  d->scheduler->scheduleItemFetch( item, parts, message() );
620 
621  return QString();
622 
623 }
624 
625 void ResourceBase::collectionsRetrieved( const Collection::List & collections )
626 {
627  Q_D( ResourceBase );
628  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
629  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
630  "ResourceBase::collectionsRetrieved()",
631  "Calling collectionsRetrieved() although no collection retrieval is in progress" );
632  if ( !d->mCollectionSyncer ) {
633  d->mCollectionSyncer = new CollectionSync( identifier() );
634  d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid );
635  connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) );
636  connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) );
637  }
638  d->mCollectionSyncer->setRemoteCollections( collections );
639 }
640 
641 void ResourceBase::collectionsRetrievedIncremental( const Collection::List & changedCollections,
642  const Collection::List & removedCollections )
643 {
644  Q_D( ResourceBase );
645  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
646  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
647  "ResourceBase::collectionsRetrievedIncremental()",
648  "Calling collectionsRetrievedIncremental() although no collection retrieval is in progress" );
649  if ( !d->mCollectionSyncer ) {
650  d->mCollectionSyncer = new CollectionSync( identifier() );
651  d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid );
652  connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) );
653  connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) );
654  }
655  d->mCollectionSyncer->setRemoteCollections( changedCollections, removedCollections );
656 }
657 
658 void ResourceBase::setCollectionStreamingEnabled( bool enable )
659 {
660  Q_D( ResourceBase );
661  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
662  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
663  "ResourceBase::setCollectionStreamingEnabled()",
664  "Calling setCollectionStreamingEnabled() although no collection retrieval is in progress" );
665  if ( !d->mCollectionSyncer ) {
666  d->mCollectionSyncer = new CollectionSync( identifier() );
667  d->mCollectionSyncer->setHierarchicalRemoteIds( d->mHierarchicalRid );
668  connect( d->mCollectionSyncer, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)) );
669  connect( d->mCollectionSyncer, SIGNAL(result(KJob*)), SLOT(slotCollectionSyncDone(KJob*)) );
670  }
671  d->mCollectionSyncer->setStreamingEnabled( enable );
672 }
673 
674 void ResourceBase::collectionsRetrievalDone()
675 {
676  Q_D( ResourceBase );
677  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
678  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
679  "ResourceBase::collectionsRetrievalDone()",
680  "Calling collectionsRetrievalDone() although no collection retrieval is in progress" );
681  // streaming enabled, so finalize the sync
682  if ( d->mCollectionSyncer ) {
683  d->mCollectionSyncer->retrievalDone();
684  }
685  // user did the sync himself, we are done now
686  else {
687  // FIXME: we need the same special case for SyncAll as in slotCollectionSyncDone here!
688  d->scheduler->taskDone();
689  }
690 }
691 
692 void ResourceBasePrivate::slotCollectionSyncDone( KJob * job )
693 {
694  Q_Q( ResourceBase );
695  mCollectionSyncer = 0;
696  if ( job->error() ) {
697  if ( job->error() != Job::UserCanceled )
698  emit q->error( job->errorString() );
699  } else {
700  if ( scheduler->currentTask().type == ResourceScheduler::SyncAll ) {
701  CollectionFetchJob *list = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive );
702  list->setFetchScope( q->changeRecorder()->collectionFetchScope() );
703  list->fetchScope().setResource( mId );
704  q->connect( list, SIGNAL(result(KJob*)), q, SLOT(slotLocalListDone(KJob*)) );
705  return;
706  } else if ( scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ) {
707  scheduler->scheduleCollectionTreeSyncCompletion();
708  }
709  }
710  scheduler->taskDone();
711 }
712 
713 void ResourceBasePrivate::slotLocalListDone( KJob * job )
714 {
715  Q_Q( ResourceBase );
716  if ( job->error() ) {
717  emit q->error( job->errorString() );
718  } else {
719  Collection::List cols = static_cast<CollectionFetchJob*>( job )->collections();
720  foreach ( const Collection &col, cols ) {
721  scheduler->scheduleSync( col );
722  }
723  scheduler->scheduleFullSyncCompletion();
724  }
725  scheduler->taskDone();
726 }
727 
728 void ResourceBasePrivate::slotSynchronizeCollection( const Collection &col )
729 {
730  Q_Q( ResourceBase );
731  currentCollection = col;
732  // check if this collection actually can contain anything
733  QStringList contentTypes = currentCollection.contentMimeTypes();
734  contentTypes.removeAll( Collection::mimeType() );
735  if ( !contentTypes.isEmpty() || (col.rights() & (Collection::CanLinkItem)) ) { // HACK to check for virtual collections
736  if ( mAutomaticProgressReporting ) {
737  emit q->status( AgentBase::Running, i18nc( "@info:status", "Syncing folder '%1'", currentCollection.name() ) );
738  }
739  q->retrieveItems( currentCollection );
740  return;
741  }
742  scheduler->taskDone();
743 }
744 
745 void ResourceBasePrivate::slotSynchronizeCollectionAttributes( const Collection &col )
746 {
747  Q_Q( ResourceBase );
748  QMetaObject::invokeMethod( q, "retrieveCollectionAttributes", Q_ARG( Akonadi::Collection, col ) );
749 }
750 
751 void ResourceBasePrivate::slotPrepareItemRetrieval( const Akonadi::Item &item )
752 {
753  Q_Q( ResourceBase );
754  ItemFetchJob *fetch = new ItemFetchJob( item, this );
755  fetch->fetchScope().setAncestorRetrieval( q->changeRecorder()->itemFetchScope().ancestorRetrieval() );
756  fetch->fetchScope().setCacheOnly( true );
757 
758  // copy list of attributes to fetch
759  const QSet<QByteArray> attributes = q->changeRecorder()->itemFetchScope().attributes();
760  foreach ( const QByteArray &attribute, attributes )
761  fetch->fetchScope().fetchAttribute( attribute );
762 
763  q->connect( fetch, SIGNAL(result(KJob*)), SLOT(slotPrepareItemRetrievalResult(KJob*)) );
764 }
765 
766 void ResourceBasePrivate::slotPrepareItemRetrievalResult( KJob* job )
767 {
768  Q_Q( ResourceBase );
769  Q_ASSERT_X( scheduler->currentTask().type == ResourceScheduler::FetchItem,
770  "ResourceBasePrivate::slotPrepareItemRetrievalResult()",
771  "Preparing item retrieval although no item retrieval is in progress" );
772  if ( job->error() ) {
773  q->cancelTask( job->errorText() );
774  return;
775  }
776  ItemFetchJob *fetch = qobject_cast<ItemFetchJob*>( job );
777  if ( fetch->items().count() != 1 ) {
778  q->cancelTask( i18n( "The requested item no longer exists" ) );
779  return;
780  }
781  const Item item = fetch->items().first();
782  const QSet<QByteArray> parts = scheduler->currentTask().itemParts;
783  if ( !q->retrieveItem( item, parts ) )
784  q->cancelTask();
785 }
786 
787 void ResourceBasePrivate::slotRecursiveMoveReplay( RecursiveMover *mover )
788 {
789  Q_Q( ResourceBase );
790  Q_ASSERT( mover );
791  Q_ASSERT( !m_recursiveMover );
792  m_recursiveMover = mover;
793  connect( mover, SIGNAL(result(KJob*)), q, SLOT(slotRecursiveMoveReplayResult(KJob*)) );
794  mover->start();
795 }
796 
797 void ResourceBasePrivate::slotRecursiveMoveReplayResult( KJob *job )
798 {
799  Q_Q( ResourceBase );
800  m_recursiveMover = 0;
801 
802  if ( job->error() ) {
803  q->deferTask();
804  return;
805  }
806 
807  changeProcessed();
808 }
809 
810 void ResourceBase::itemsRetrievalDone()
811 {
812  Q_D( ResourceBase );
813  // streaming enabled, so finalize the sync
814  if ( d->mItemSyncer ) {
815  d->mItemSyncer->deliveryDone();
816  }
817  // user did the sync himself, we are done now
818  else {
819  d->scheduler->taskDone();
820  }
821 }
822 
823 void ResourceBase::clearCache()
824 {
825  Q_D( ResourceBase );
826  d->scheduler->scheduleResourceCollectionDeletion();
827 }
828 
829 void ResourceBase::invalidateCache(const Collection& collection)
830 {
831  Q_D( ResourceBase );
832  d->scheduler->scheduleCacheInvalidation( collection );
833 }
834 
835 Collection ResourceBase::currentCollection() const
836 {
837  Q_D( const ResourceBase );
838  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::SyncCollection ,
839  "ResourceBase::currentCollection()",
840  "Trying to access current collection although no item retrieval is in progress" );
841  return d->currentCollection;
842 }
843 
844 Item ResourceBase::currentItem() const
845 {
846  Q_D( const ResourceBase );
847  Q_ASSERT_X( d->scheduler->currentTask().type == ResourceScheduler::FetchItem ,
848  "ResourceBase::currentItem()",
849  "Trying to access current item although no item retrieval is in progress" );
850  return d->scheduler->currentTask().item;
851 }
852 
853 void ResourceBase::synchronizeCollectionTree()
854 {
855  d_func()->scheduler->scheduleCollectionTreeSync();
856 }
857 
858 void ResourceBase::cancelTask()
859 {
860  Q_D( ResourceBase );
861  switch ( d->scheduler->currentTask().type ) {
862  case ResourceScheduler::FetchItem:
863  itemRetrieved( Item() ); // sends the error reply and
864  break;
865  case ResourceScheduler::ChangeReplay:
866  d->changeProcessed();
867  break;
868  case ResourceScheduler::SyncCollectionTree:
869  case ResourceScheduler::SyncAll:
870  if ( d->mCollectionSyncer )
871  d->mCollectionSyncer->rollback();
872  else
873  d->scheduler->taskDone();
874  break;
875  case ResourceScheduler::SyncCollection:
876  if ( d->mItemSyncer ) {
877  d->mItemSyncer->rollback();
878  } else {
879  d->scheduler->taskDone();
880  }
881  break;
882  default:
883  d->scheduler->taskDone();
884  }
885 }
886 
887 void ResourceBase::cancelTask( const QString &msg )
888 {
889  cancelTask();
890 
891  emit error( msg );
892 }
893 
894 void ResourceBase::deferTask()
895 {
896  Q_D( ResourceBase );
897  d->scheduler->deferTask();
898 }
899 
900 void ResourceBase::doSetOnline( bool state )
901 {
902  d_func()->scheduler->setOnline( state );
903 }
904 
905 void ResourceBase::synchronizeCollection( qint64 collectionId )
906 {
907  synchronizeCollection( collectionId, false );
908 }
909 
910 void ResourceBase::synchronizeCollection( qint64 collectionId, bool recursive )
911 {
912  CollectionFetchJob* job = new CollectionFetchJob( Collection( collectionId ), recursive ? CollectionFetchJob::Recursive : CollectionFetchJob::Base );
913  job->setFetchScope( changeRecorder()->collectionFetchScope() );
914  job->fetchScope().setResource( identifier() );
915  job->setProperty( "recursive", recursive );
916  connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionListDone(KJob*)) );
917 }
918 
919 void ResourceBasePrivate::slotCollectionListDone( KJob *job )
920 {
921  if ( !job->error() ) {
922  Collection::List list = static_cast<CollectionFetchJob*>( job )->collections();
923  if ( !list.isEmpty() ) {
924  if ( job->property( "recursive" ).toBool() ) {
925  Q_FOREACH ( const Collection &collection, list ) {
926  scheduler->scheduleSync( collection );
927  }
928  } else {
929  scheduler->scheduleSync( list.first() );
930  }
931  }
932  }
933  // TODO: error handling
934 }
935 
936 void ResourceBase::synchronizeCollectionAttributes( qint64 collectionId )
937 {
938  CollectionFetchJob* job = new CollectionFetchJob( Collection( collectionId ), CollectionFetchJob::Base );
939  job->setFetchScope( changeRecorder()->collectionFetchScope() );
940  job->fetchScope().setResource( identifier() );
941  connect( job, SIGNAL(result(KJob*)), SLOT(slotCollectionListForAttributesDone(KJob*)) );
942 }
943 
944 void ResourceBasePrivate::slotCollectionListForAttributesDone( KJob *job )
945 {
946  if ( !job->error() ) {
947  Collection::List list = static_cast<CollectionFetchJob*>( job )->collections();
948  if ( !list.isEmpty() ) {
949  Collection col = list.first();
950  scheduler->scheduleAttributesSync( col );
951  }
952  }
953  // TODO: error handling
954 }
955 
956 void ResourceBase::setTotalItems( int amount )
957 {
958  kDebug() << amount;
959  Q_D( ResourceBase );
960  setItemStreamingEnabled( true );
961  if ( d->mItemSyncer ) {
962  d->mItemSyncer->setTotalItems( amount );
963  }
964 }
965 
966 void ResourceBase::setItemStreamingEnabled( bool enable )
967 {
968  Q_D( ResourceBase );
969  d->createItemSyncInstanceIfMissing();
970  if ( d->mItemSyncer ) {
971  d->mItemSyncer->setStreamingEnabled( enable );
972  }
973 }
974 
975 void ResourceBase::itemsRetrieved( const Item::List &items )
976 {
977  Q_D( ResourceBase );
978  d->createItemSyncInstanceIfMissing();
979  if ( d->mItemSyncer ) {
980  d->mItemSyncer->setFullSyncItems( items );
981  }
982 }
983 
984 void ResourceBase::itemsRetrievedIncremental( const Item::List &changedItems,
985  const Item::List &removedItems )
986 {
987  Q_D( ResourceBase );
988  d->createItemSyncInstanceIfMissing();
989  if ( d->mItemSyncer ) {
990  d->mItemSyncer->setIncrementalSyncItems( changedItems, removedItems );
991  }
992 }
993 
994 void ResourceBasePrivate::slotItemSyncDone( KJob *job )
995 {
996  mItemSyncer = 0;
997  Q_Q( ResourceBase );
998  if ( job->error() && job->error() != Job::UserCanceled ) {
999  emit q->error( job->errorString() );
1000  }
1001  scheduler->taskDone();
1002 }
1003 
1004 
1005 void ResourceBasePrivate::slotDelayedEmitProgress()
1006 {
1007  Q_Q( ResourceBase );
1008  if ( mAutomaticProgressReporting ) {
1009  emit q->percent( mUnemittedProgress );
1010 
1011  Q_FOREACH( const QVariantMap &statusMap, mUnemittedAdvancedStatus ) {
1012  emit q->advancedStatus( statusMap );
1013  }
1014  }
1015  mUnemittedProgress = 0;
1016  mUnemittedAdvancedStatus.clear();
1017 }
1018 
1019 void ResourceBasePrivate::slotPercent( KJob *job, unsigned long percent )
1020 {
1021  mUnemittedProgress = percent;
1022 
1023  const Collection collection = job->property( "collection" ).value<Collection>();
1024  if ( collection.isValid() ) {
1025  QVariantMap statusMap;
1026  statusMap.insert( QLatin1String( "key" ), QString::fromLatin1( "collectionSyncProgress" ) );
1027  statusMap.insert( QLatin1String( "collectionId" ), collection.id() );
1028  statusMap.insert( QLatin1String( "percent" ), static_cast<unsigned int>( percent ) );
1029 
1030  mUnemittedAdvancedStatus[collection.id()] = statusMap;
1031  }
1032  // deliver completion right away, intermediate progress at 1s intervals
1033  if ( percent == 100 ) {
1034  mProgressEmissionCompressor.stop();
1035  slotDelayedEmitProgress();
1036  } else if ( !mProgressEmissionCompressor.isActive() ) {
1037  mProgressEmissionCompressor.start();
1038  }
1039 }
1040 
1041 void ResourceBase::setHierarchicalRemoteIdentifiersEnabled( bool enable )
1042 {
1043  Q_D( ResourceBase );
1044  d->mHierarchicalRid = enable;
1045 }
1046 
1047 void ResourceBase::scheduleCustomTask( QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority )
1048 {
1049  Q_D( ResourceBase );
1050  d->scheduler->scheduleCustomTask( receiver, method, argument, priority );
1051 }
1052 
1053 void ResourceBase::taskDone()
1054 {
1055  Q_D( ResourceBase );
1056  d->scheduler->taskDone();
1057 }
1058 
1059 void ResourceBase::retrieveCollectionAttributes( const Collection &collection )
1060 {
1061  collectionAttributesRetrieved( collection );
1062 }
1063 
1064 void Akonadi::ResourceBase::abortActivity()
1065 {
1066 
1067 }
1068 
1069 void ResourceBase::setItemTransactionMode(ItemSync::TransactionMode mode)
1070 {
1071  Q_D( ResourceBase );
1072  d->mItemTransactionMode = mode;
1073 }
1074 
1075 void ResourceBase::setItemSynchronizationFetchScope(const ItemFetchScope& fetchScope)
1076 {
1077  Q_D( ResourceBase );
1078  if ( !d->mItemSyncFetchScope )
1079  d->mItemSyncFetchScope = new ItemFetchScope;
1080  *(d->mItemSyncFetchScope) = fetchScope;
1081 }
1082 
1083 void ResourceBase::setAutomaticProgressReporting( bool enabled )
1084 {
1085  Q_D( ResourceBase );
1086  d->mAutomaticProgressReporting = enabled;
1087 }
1088 
1089 QString ResourceBase::dumpNotificationListToString() const
1090 {
1091  Q_D( const ResourceBase );
1092  return d->dumpNotificationListToString();
1093 }
1094 
1095 QString ResourceBase::dumpSchedulerToString() const
1096 {
1097  Q_D( const ResourceBase );
1098  return d->dumpToString();
1099 }
1100 
1101 #include "resourcebase.moc"
1102 #include "moc_resourcebase.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Sat Jul 13 2013 01:27:40 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