~ubuntu-branches/ubuntu/karmic/kdepim/karmic-backports

« back to all changes in this revision

Viewing changes to akonadi/kresources/shared/resourceprivatebase.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christian Mangold
  • Date: 2009-07-10 06:34:50 UTC
  • mfrom: (1.1.40 upstream)
  • Revision ID: james.westby@ubuntu.com-20090710063450-neojgew2fh0n3y0u
Tags: 4:4.2.96-0ubuntu1
* New upstream release
* Bump kde build-deps to 4.2.96

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
    This file is part of kdepim.
3
 
    Copyright (c) 2009 Kevin Krammer <kevin.krammer@gmx.at>
4
 
 
5
 
    This library is free software; you can redistribute it and/or
6
 
    modify it under the terms of the GNU Library General Public
7
 
    License as published by the Free Software Foundation; either
8
 
    version 2 of the License, or (at your option) any later version.
9
 
 
10
 
    This library is distributed in the hope that it will be useful,
11
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
    Library General Public 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
17
 
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 
    Boston, MA 02110-1301, USA.
19
 
*/
20
 
 
21
 
#include "resourceprivatebase.h"
22
 
 
23
 
#include "concurrentjobs.h"
24
 
#include "idarbiterbase.h"
25
 
#include "itemsavecontext.h"
26
 
#include "storecollectiondialog.h"
27
 
#include "subresourcebase.h"
28
 
 
29
 
#include <akonadi/control.h>
30
 
#include <akonadi/mimetypechecker.h>
31
 
 
32
 
#include <KDebug>
33
 
#include <KLocale>
34
 
 
35
 
ResourcePrivateBase::ResourcePrivateBase( IdArbiterBase *idArbiter, QObject *parent )
36
 
  : QObject( parent ),
37
 
    mIdArbiter( idArbiter ),
38
 
    mStoreCollectionDialog( 0 ),
39
 
    mState( Closed ),
40
 
    mLoadingInProgress( false )
41
 
{
42
 
}
43
 
 
44
 
ResourcePrivateBase::ResourcePrivateBase( const KConfigGroup &config, IdArbiterBase *idArbiter, QObject *parent )
45
 
  : QObject( parent ),
46
 
    mConfig( config ),
47
 
    mIdArbiter( idArbiter ),
48
 
    mStoreCollectionDialog( 0 ),
49
 
    mState( Closed ),
50
 
    mLoadingInProgress( false )
51
 
{
52
 
  const QLatin1String resourceKey( "DefaultAkonadiResourceIdentifier" );
53
 
  mDefaultResourceIdentifier = config.readEntry( resourceKey, QString() );
54
 
 
55
 
  const QLatin1String urlKey( "CollectionUrl" );
56
 
  KUrl url = config.readEntry( urlKey, KUrl() );
57
 
  if ( url.isValid() ) {
58
 
    mDefaultStoreCollection = Akonadi::Collection::fromUrl( url );
59
 
  }
60
 
 
61
 
  const KConfigGroup storeConfig = config.group( QLatin1String( "StoreConfig" ) );
62
 
  if ( storeConfig.isValid() ) {
63
 
    const QStringList groups = storeConfig.groupList();
64
 
    foreach ( const QString &group, groups ) {
65
 
      KConfigGroup mimeConfig = storeConfig.group( group );
66
 
 
67
 
      url = mimeConfig.readEntry( urlKey );
68
 
      kDebug( 5650 ) << "read MIME config pair: mimeType=" << group
69
 
                    << ", url=" << url;
70
 
      if ( url.isValid() ) {
71
 
        mStoreCollectionsByMimeType[ group ] = Akonadi::Collection::fromUrl( url );
72
 
      }
73
 
    }
74
 
  }
75
 
}
76
 
 
77
 
ResourcePrivateBase::~ResourcePrivateBase()
78
 
{
79
 
  delete mIdArbiter;
80
 
  delete mStoreCollectionDialog;
81
 
}
82
 
 
83
 
bool ResourcePrivateBase::doOpen()
84
 
{
85
 
  kDebug( 5650 );
86
 
  if ( mState == Opened ) {
87
 
    kWarning( 5650 ) << "Trying to open already opened resource";
88
 
    return true;
89
 
  }
90
 
 
91
 
  if ( !startAkonadi() ) {
92
 
    kError( 5650 ) << "Failed to start Akonadi";
93
 
    mState = Failed;
94
 
    return false;
95
 
  }
96
 
 
97
 
  if ( !openResource() ) {
98
 
    kError( 5650 ) << "Failed to do type specific open";
99
 
    mState = Failed;
100
 
    return false;
101
 
  }
102
 
 
103
 
  mState = Opened;
104
 
  return true;
105
 
}
106
 
 
107
 
bool ResourcePrivateBase::doClose()
108
 
{
109
 
  if ( mConfig.isValid() ) {
110
 
    writeConfig( mConfig );
111
 
  }
112
 
 
113
 
  bool result = closeResource();
114
 
 
115
 
  mState = Closed;
116
 
 
117
 
  return result;
118
 
}
119
 
 
120
 
bool ResourcePrivateBase::doLoad()
121
 
{
122
 
  kDebug( 5650 ) << "isLoading=" << isLoading();
123
 
  mLoadingInProgress = true;
124
 
 
125
 
  bool result = loadResource();
126
 
 
127
 
  Q_ASSERT( !mLoadingInProgress ); // subclass must have called loadingResult
128
 
 
129
 
  return result;
130
 
}
131
 
 
132
 
bool ResourcePrivateBase::doAsyncLoad()
133
 
{
134
 
  kDebug( 5650 ) << "isLoading=" << isLoading();
135
 
  mLoadingInProgress = true;
136
 
 
137
 
  return asyncLoadResource();
138
 
}
139
 
 
140
 
bool ResourcePrivateBase::doSave()
141
 
{
142
 
  kDebug( 5650 ) << mChanges.count() << "changes";
143
 
 
144
 
  if ( mState == Closed ) {
145
 
    const QString message = i18nc( "@info:status", "Cannot save to closed resource" );
146
 
    savingResult( false, message );
147
 
    return false;
148
 
  }
149
 
 
150
 
  if ( mState == Failed ) {
151
 
    const QString message = i18nc( "@info:status", "Cannot save while not connected to Akonadi" );
152
 
    savingResult( false, message );
153
 
    return false;
154
 
  }
155
 
 
156
 
  if ( mChanges.isEmpty() ) {
157
 
    return true;
158
 
  }
159
 
 
160
 
  ItemSaveContext saveContext;
161
 
  if ( !prepareItemSaveContext( saveContext ) ) {
162
 
    const QString message = i18nc( "@info:status", "Processing change set failed" );
163
 
    savingResult( false, message );
164
 
    return false;
165
 
  }
166
 
 
167
 
  ConcurrentItemSaveJob itemSaveJob( saveContext );
168
 
  if ( !itemSaveJob.exec() ) {
169
 
    savingResult( false, itemSaveJob->errorString() );
170
 
    return false;
171
 
  }
172
 
 
173
 
  return true;
174
 
}
175
 
 
176
 
bool ResourcePrivateBase::doAsyncSave()
177
 
{
178
 
  kDebug( 5650 ) << mChanges.count() << "changes";
179
 
 
180
 
  if ( mState == Closed ) {
181
 
    const QString message = i18nc( "@info:status", "Cannot save to closed resource" );
182
 
    savingResult( false, message );
183
 
    return false;
184
 
  }
185
 
 
186
 
  if ( mState == Failed ) {
187
 
    const QString message = i18nc( "@info:status", "Cannot save while not connected to Akonadi" );
188
 
    savingResult( false, message );
189
 
    return false;
190
 
  }
191
 
 
192
 
  if ( mChanges.isEmpty() ) {
193
 
    return true;
194
 
  }
195
 
 
196
 
  ItemSaveContext saveContext;
197
 
  if ( !prepareItemSaveContext( saveContext ) ) {
198
 
    const QString message = i18nc( "@info:status", "Processing change set failed" );
199
 
    savingResult( false, message );
200
 
    return false;
201
 
  }
202
 
 
203
 
  ItemSaveJob *itemSaveJob = new ItemSaveJob( saveContext );
204
 
  connect( itemSaveJob, SIGNAL( result( KJob* ) ),
205
 
           SLOT( savingResult( KJob* ) ) );
206
 
 
207
 
  return true;
208
 
}
209
 
 
210
 
void ResourcePrivateBase::writeConfig( KConfigGroup &config ) const
211
 
{
212
 
  const QLatin1String urlKey( "CollectionUrl" );
213
 
  const QLatin1String resourceKey( "DefaultAkonadiResourceIdentifier" );
214
 
 
215
 
  Akonadi::Collection defaultStoreCollection = mDefaultStoreCollection;
216
 
  QString defaultResourceIdentifier = mDefaultResourceIdentifier;
217
 
 
218
 
  // if we have new store config, then delete the old keys
219
 
  if ( !mStoreCollectionsByMimeType.isEmpty() ) {
220
 
    defaultStoreCollection = Akonadi::Collection();
221
 
    defaultResourceIdentifier = QString();
222
 
  }
223
 
 
224
 
  // if we have a valid default store collection, then delete the resource identifier key
225
 
  if ( defaultStoreCollection.isValid() ) {
226
 
    defaultResourceIdentifier = QString();
227
 
    config.writeEntry( urlKey, defaultStoreCollection.url() );
228
 
  } else {
229
 
    config.deleteEntry( urlKey );
230
 
  }
231
 
 
232
 
  if ( !defaultResourceIdentifier.isEmpty() ) {
233
 
    config.writeEntry( resourceKey, defaultResourceIdentifier );
234
 
  } else {
235
 
    config.deleteEntry( resourceKey );
236
 
  }
237
 
 
238
 
  KConfigGroup storeConfig = config.group( QLatin1String( "StoreConfig" ) );
239
 
  QSet<QString> mimeConfigs = QSet<QString>::fromList( storeConfig.groupList() );
240
 
 
241
 
  CollectionsByMimeType::const_iterator it    = mStoreCollectionsByMimeType.constBegin();
242
 
  CollectionsByMimeType::const_iterator endIt = mStoreCollectionsByMimeType.constEnd();
243
 
  for ( ; it != endIt; ++it ) {
244
 
    KConfigGroup mimeConfig = storeConfig.group( it.key() );
245
 
 
246
 
    mimeConfig.writeEntry( urlKey, it.value().url() );
247
 
    mimeConfigs.remove( it.key() );
248
 
 
249
 
    kDebug( 5650 ) << "wrote MIME config pair: mimeType=" << it.key()
250
 
                   << ", url=" << it.value().url();
251
 
  }
252
 
 
253
 
  foreach ( const QString &group, mimeConfigs ) {
254
 
    storeConfig.deleteGroup( group );
255
 
  }
256
 
 
257
 
  writeResourceConfig( config );
258
 
}
259
 
 
260
 
void ResourcePrivateBase::clear()
261
 
{
262
 
  mIdArbiter->clear();
263
 
  mChanges.clear();
264
 
  clearResource();
265
 
}
266
 
 
267
 
void ResourcePrivateBase::setStoreCollectionsByMimeType( const StoreConfigIface::CollectionsByMimeType &collections )
268
 
{
269
 
  mStoreCollectionsByMimeType = collections;
270
 
 
271
 
  mDefaultStoreCollection = Akonadi::Collection();
272
 
}
273
 
 
274
 
StoreConfigIface::CollectionsByMimeType ResourcePrivateBase::storeCollectionsByMimeType() const
275
 
{
276
 
  if ( mStoreCollectionsByMimeType.isEmpty() && mDefaultStoreCollection.isValid() ) {
277
 
    return storeCollectionsFromOldDefault();
278
 
  }
279
 
 
280
 
  return mStoreCollectionsByMimeType;
281
 
}
282
 
 
283
 
bool ResourcePrivateBase::addLocalItem( const QString &uid, const QString &mimeType )
284
 
{
285
 
  kDebug( 5650 ) << "uid=" << uid << ", mimeType=" << mimeType;
286
 
 
287
 
  // check if there is a sub resource with a mapped item for the identifier
288
 
  // if there is, we have a change, otherwise its an add
289
 
  const SubResourceBase *resource = findSubResourceForMappedItem( uid );
290
 
  if ( resource == 0 ) {
291
 
    mChanges[ uid ] = Added;
292
 
 
293
 
    if ( mStoreCollectionDialog == 0 ) {
294
 
      mStoreCollectionDialog = new StoreCollectionDialog();
295
 
      mStoreCollectionDialog->setSubResourceModel( subResourceModel() );
296
 
    }
297
 
 
298
 
    resource = storeSubResourceForMimeType( mimeType );
299
 
    if ( resource == 0 ) {
300
 
      // check possible store sub resources. if there is only one, use it
301
 
      // otherwise we need to ask the user
302
 
      QList<const SubResourceBase*> possibleStores = writableSubResourcesForMimeType( mimeType );
303
 
      if ( possibleStores.count() == 1 ) {
304
 
        kDebug( 5650 ) << "Only one possible sub resource for MIME type="
305
 
                       << mimeType;
306
 
        resource = possibleStores.first();
307
 
      } else {
308
 
        resource = storeSubResourceFromUser( uid, mimeType );
309
 
        if ( resource == 0 ) {
310
 
          mChanges.remove( uid );
311
 
          return false;
312
 
        }
313
 
      }
314
 
    }
315
 
  } else {
316
 
    mChanges[ uid ] = Changed;
317
 
  }
318
 
 
319
 
  Q_ASSERT( resource != 0 );
320
 
  mUidToResourceMap[ uid ] = resource->subResourceIdentifier();
321
 
 
322
 
  return true;
323
 
}
324
 
 
325
 
void ResourcePrivateBase::changeLocalItem( const QString &uid )
326
 
{
327
 
  const QString subResource = mUidToResourceMap.value( uid );
328
 
  kDebug( 5650 ) << "uid=" << uid << ", subResource=" << subResource;
329
 
 
330
 
  Q_ASSERT( !subResource.isEmpty() );
331
 
 
332
 
  const SubResourceBase *resource = subResourceBase( subResource );
333
 
  Q_ASSERT( resource != 0 );
334
 
 
335
 
  if ( resource->hasMappedItem( uid ) ) {
336
 
    mChanges[ uid ] = Changed;
337
 
  } else {
338
 
    mChanges[ uid ] = Added;
339
 
  }
340
 
}
341
 
 
342
 
void ResourcePrivateBase::removeLocalItem( const QString &uid )
343
 
{
344
 
  const QString subResource = mUidToResourceMap.value( uid );
345
 
  kDebug( 5650 ) << "uid=" << uid << ", subResource=" << subResource;
346
 
 
347
 
  Q_ASSERT( !subResource.isEmpty() );
348
 
 
349
 
  const SubResourceBase *resource = subResourceBase( subResource );
350
 
  Q_ASSERT( resource != 0 );
351
 
 
352
 
  if ( resource->hasMappedItem( uid ) ) {
353
 
    mChanges[ uid ] = Removed;
354
 
  } else {
355
 
    mChanges.remove( uid );
356
 
  }
357
 
}
358
 
 
359
 
ResourcePrivateBase::State ResourcePrivateBase::state() const
360
 
{
361
 
  return mState;
362
 
}
363
 
 
364
 
bool ResourcePrivateBase::isLoading() const
365
 
{
366
 
  return mLoadingInProgress;
367
 
}
368
 
 
369
 
bool ResourcePrivateBase::startAkonadi()
370
 
{
371
 
  return Akonadi::Control::start();
372
 
}
373
 
 
374
 
Akonadi::Collection ResourcePrivateBase::storeCollectionForMimeType( const QString &mimeType ) const
375
 
{
376
 
  kDebug( 5650 ) << "mimeType=" << mimeType;
377
 
 
378
 
  if ( mStoreCollectionsByMimeType.isEmpty() && mDefaultStoreCollection.isValid() ) {
379
 
    if ( Akonadi::MimeTypeChecker::isWantedCollection( mDefaultStoreCollection, mimeType ) ) {
380
 
      kDebug( 5650 ) << "Taking DefaultStoreCollection: id=" << mDefaultStoreCollection.id()
381
 
                    << ", remoteId=" << mDefaultStoreCollection.remoteId();
382
 
      return mDefaultStoreCollection;
383
 
    }
384
 
  }
385
 
 
386
 
  Akonadi::Collection collection = mStoreCollectionsByMimeType.value( mimeType );
387
 
  if ( collection.isValid() ) {
388
 
    kDebug( 5650 ) << "Found storage collection in map: id=" << collection.id()
389
 
                   << ", remoteId=" << collection.remoteId();
390
 
    return collection;
391
 
  }
392
 
 
393
 
  return Akonadi::Collection();
394
 
}
395
 
 
396
 
bool ResourcePrivateBase::prepareItemSaveContext( ItemSaveContext &saveContext )
397
 
{
398
 
  ChangeByKResId::const_iterator it    = mChanges.constBegin();
399
 
  ChangeByKResId::const_iterator endIt = mChanges.constEnd();
400
 
  for ( ; it != endIt; ++it ) {
401
 
    if ( !prepareItemSaveContext( it, saveContext ) ) {
402
 
      return false;
403
 
    }
404
 
  }
405
 
 
406
 
  return true;
407
 
}
408
 
 
409
 
 
410
 
bool ResourcePrivateBase::prepareItemSaveContext( const ChangeByKResId::const_iterator &it, ItemSaveContext &saveContext )
411
 
{
412
 
  const QString kresId = it.key();
413
 
  const SubResourceBase *resource = subResourceBase( mUidToResourceMap.value( kresId ) );
414
 
  Q_ASSERT( resource != 0 );
415
 
 
416
 
  switch ( it.value() ) {
417
 
    case Added: {
418
 
      ItemAddContext addContext;
419
 
      addContext.collection = resource->collection();
420
 
      addContext.item = createItem( kresId );
421
 
 
422
 
      saveContext.addedItems << addContext;
423
 
      break;
424
 
    }
425
 
 
426
 
    case Changed: {
427
 
      Akonadi::Item item = updateItem( resource->mappedItem( kresId ), kresId, mIdArbiter->mapToOriginalId( kresId ) );
428
 
 
429
 
      saveContext.changedItems << item;
430
 
      break;
431
 
    }
432
 
 
433
 
    case Removed:
434
 
      saveContext.removedItems << resource->mappedItem( kresId );
435
 
      break;
436
 
 
437
 
    case NoChange:
438
 
       Q_ASSERT( "Invalid ChangeType in change map" );
439
 
       break;
440
 
  }
441
 
 
442
 
  return true;
443
 
}
444
 
 
445
 
 
446
 
void ResourcePrivateBase::subResourceAdded( SubResourceBase *subResource )
447
 
{
448
 
  Q_ASSERT( mIdArbiter != 0 );
449
 
 
450
 
  subResource->setIdArbiter( mIdArbiter );
451
 
  subResource->readConfig( mConfig );
452
 
 
453
 
  // if we don't have a valid default store collection yet, check if our
454
 
  // config indicates a default resource and whether the newly added sub resource
455
 
  // maps to one of its collections
456
 
  if ( !mDefaultStoreCollection.isValid() ) {
457
 
    if ( !mDefaultResourceIdentifier.isEmpty() ) {
458
 
      if ( subResource->collection().resource() == mDefaultResourceIdentifier ) {
459
 
        mDefaultStoreCollection = subResource->collection();
460
 
        mDefaultResourceIdentifier = QString();
461
 
      }
462
 
    }
463
 
  } else if ( mDefaultStoreCollection == subResource->collection() ) {
464
 
    // update locally cached instance
465
 
    mDefaultStoreCollection = subResource->collection();
466
 
  }
467
 
 
468
 
  // check if any of the MIME type specific collections can be updated
469
 
  CollectionsByMimeType::iterator it    = mStoreCollectionsByMimeType.begin();
470
 
  CollectionsByMimeType::iterator endIt = mStoreCollectionsByMimeType.end();
471
 
  for (; it != endIt; ++it ) {
472
 
    if ( it.value() == subResource->collection() ) {
473
 
      // update locally cached instance
474
 
      it.value() = subResource->collection();
475
 
    }
476
 
  }
477
 
}
478
 
 
479
 
void ResourcePrivateBase::subResourceRemoved( SubResourceBase *subResource )
480
 
{
481
 
  Q_UNUSED( subResource );
482
 
}
483
 
 
484
 
void ResourcePrivateBase::loadingResult( bool ok, const QString &errorString )
485
 
{
486
 
  Q_UNUSED( ok );
487
 
  Q_UNUSED( errorString );
488
 
 
489
 
  Q_ASSERT( mLoadingInProgress );
490
 
 
491
 
  mLoadingInProgress = false;
492
 
}
493
 
 
494
 
void ResourcePrivateBase::savingResult( bool ok, const QString &errorString )
495
 
{
496
 
  Q_UNUSED( errorString );
497
 
  if ( ok ) {
498
 
    mChanges.clear();
499
 
  }
500
 
}
501
 
 
502
 
#include "resourceprivatebase.moc"
503
 
 
504
 
// kate: space-indent on; indent-width 2; replace-tabs on;