2
This file is part of kdepim.
3
Copyright (c) 2009 Kevin Krammer <kevin.krammer@gmx.at>
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.
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.
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.
21
#include "resourceprivatebase.h"
23
#include "concurrentjobs.h"
24
#include "idarbiterbase.h"
25
#include "itemsavecontext.h"
26
#include "storecollectiondialog.h"
27
#include "subresourcebase.h"
29
#include <akonadi/control.h>
30
#include <akonadi/mimetypechecker.h>
35
ResourcePrivateBase::ResourcePrivateBase( IdArbiterBase *idArbiter, QObject *parent )
37
mIdArbiter( idArbiter ),
38
mStoreCollectionDialog( 0 ),
40
mLoadingInProgress( false )
44
ResourcePrivateBase::ResourcePrivateBase( const KConfigGroup &config, IdArbiterBase *idArbiter, QObject *parent )
47
mIdArbiter( idArbiter ),
48
mStoreCollectionDialog( 0 ),
50
mLoadingInProgress( false )
52
const QLatin1String resourceKey( "DefaultAkonadiResourceIdentifier" );
53
mDefaultResourceIdentifier = config.readEntry( resourceKey, QString() );
55
const QLatin1String urlKey( "CollectionUrl" );
56
KUrl url = config.readEntry( urlKey, KUrl() );
57
if ( url.isValid() ) {
58
mDefaultStoreCollection = Akonadi::Collection::fromUrl( url );
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 );
67
url = mimeConfig.readEntry( urlKey );
68
kDebug( 5650 ) << "read MIME config pair: mimeType=" << group
70
if ( url.isValid() ) {
71
mStoreCollectionsByMimeType[ group ] = Akonadi::Collection::fromUrl( url );
77
ResourcePrivateBase::~ResourcePrivateBase()
80
delete mStoreCollectionDialog;
83
bool ResourcePrivateBase::doOpen()
86
if ( mState == Opened ) {
87
kWarning( 5650 ) << "Trying to open already opened resource";
91
if ( !startAkonadi() ) {
92
kError( 5650 ) << "Failed to start Akonadi";
97
if ( !openResource() ) {
98
kError( 5650 ) << "Failed to do type specific open";
107
bool ResourcePrivateBase::doClose()
109
if ( mConfig.isValid() ) {
110
writeConfig( mConfig );
113
bool result = closeResource();
120
bool ResourcePrivateBase::doLoad()
122
kDebug( 5650 ) << "isLoading=" << isLoading();
123
mLoadingInProgress = true;
125
bool result = loadResource();
127
Q_ASSERT( !mLoadingInProgress ); // subclass must have called loadingResult
132
bool ResourcePrivateBase::doAsyncLoad()
134
kDebug( 5650 ) << "isLoading=" << isLoading();
135
mLoadingInProgress = true;
137
return asyncLoadResource();
140
bool ResourcePrivateBase::doSave()
142
kDebug( 5650 ) << mChanges.count() << "changes";
144
if ( mState == Closed ) {
145
const QString message = i18nc( "@info:status", "Cannot save to closed resource" );
146
savingResult( false, message );
150
if ( mState == Failed ) {
151
const QString message = i18nc( "@info:status", "Cannot save while not connected to Akonadi" );
152
savingResult( false, message );
156
if ( mChanges.isEmpty() ) {
160
ItemSaveContext saveContext;
161
if ( !prepareItemSaveContext( saveContext ) ) {
162
const QString message = i18nc( "@info:status", "Processing change set failed" );
163
savingResult( false, message );
167
ConcurrentItemSaveJob itemSaveJob( saveContext );
168
if ( !itemSaveJob.exec() ) {
169
savingResult( false, itemSaveJob->errorString() );
176
bool ResourcePrivateBase::doAsyncSave()
178
kDebug( 5650 ) << mChanges.count() << "changes";
180
if ( mState == Closed ) {
181
const QString message = i18nc( "@info:status", "Cannot save to closed resource" );
182
savingResult( false, message );
186
if ( mState == Failed ) {
187
const QString message = i18nc( "@info:status", "Cannot save while not connected to Akonadi" );
188
savingResult( false, message );
192
if ( mChanges.isEmpty() ) {
196
ItemSaveContext saveContext;
197
if ( !prepareItemSaveContext( saveContext ) ) {
198
const QString message = i18nc( "@info:status", "Processing change set failed" );
199
savingResult( false, message );
203
ItemSaveJob *itemSaveJob = new ItemSaveJob( saveContext );
204
connect( itemSaveJob, SIGNAL( result( KJob* ) ),
205
SLOT( savingResult( KJob* ) ) );
210
void ResourcePrivateBase::writeConfig( KConfigGroup &config ) const
212
const QLatin1String urlKey( "CollectionUrl" );
213
const QLatin1String resourceKey( "DefaultAkonadiResourceIdentifier" );
215
Akonadi::Collection defaultStoreCollection = mDefaultStoreCollection;
216
QString defaultResourceIdentifier = mDefaultResourceIdentifier;
218
// if we have new store config, then delete the old keys
219
if ( !mStoreCollectionsByMimeType.isEmpty() ) {
220
defaultStoreCollection = Akonadi::Collection();
221
defaultResourceIdentifier = QString();
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() );
229
config.deleteEntry( urlKey );
232
if ( !defaultResourceIdentifier.isEmpty() ) {
233
config.writeEntry( resourceKey, defaultResourceIdentifier );
235
config.deleteEntry( resourceKey );
238
KConfigGroup storeConfig = config.group( QLatin1String( "StoreConfig" ) );
239
QSet<QString> mimeConfigs = QSet<QString>::fromList( storeConfig.groupList() );
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() );
246
mimeConfig.writeEntry( urlKey, it.value().url() );
247
mimeConfigs.remove( it.key() );
249
kDebug( 5650 ) << "wrote MIME config pair: mimeType=" << it.key()
250
<< ", url=" << it.value().url();
253
foreach ( const QString &group, mimeConfigs ) {
254
storeConfig.deleteGroup( group );
257
writeResourceConfig( config );
260
void ResourcePrivateBase::clear()
267
void ResourcePrivateBase::setStoreCollectionsByMimeType( const StoreConfigIface::CollectionsByMimeType &collections )
269
mStoreCollectionsByMimeType = collections;
271
mDefaultStoreCollection = Akonadi::Collection();
274
StoreConfigIface::CollectionsByMimeType ResourcePrivateBase::storeCollectionsByMimeType() const
276
if ( mStoreCollectionsByMimeType.isEmpty() && mDefaultStoreCollection.isValid() ) {
277
return storeCollectionsFromOldDefault();
280
return mStoreCollectionsByMimeType;
283
bool ResourcePrivateBase::addLocalItem( const QString &uid, const QString &mimeType )
285
kDebug( 5650 ) << "uid=" << uid << ", mimeType=" << mimeType;
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;
293
if ( mStoreCollectionDialog == 0 ) {
294
mStoreCollectionDialog = new StoreCollectionDialog();
295
mStoreCollectionDialog->setSubResourceModel( subResourceModel() );
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="
306
resource = possibleStores.first();
308
resource = storeSubResourceFromUser( uid, mimeType );
309
if ( resource == 0 ) {
310
mChanges.remove( uid );
316
mChanges[ uid ] = Changed;
319
Q_ASSERT( resource != 0 );
320
mUidToResourceMap[ uid ] = resource->subResourceIdentifier();
325
void ResourcePrivateBase::changeLocalItem( const QString &uid )
327
const QString subResource = mUidToResourceMap.value( uid );
328
kDebug( 5650 ) << "uid=" << uid << ", subResource=" << subResource;
330
Q_ASSERT( !subResource.isEmpty() );
332
const SubResourceBase *resource = subResourceBase( subResource );
333
Q_ASSERT( resource != 0 );
335
if ( resource->hasMappedItem( uid ) ) {
336
mChanges[ uid ] = Changed;
338
mChanges[ uid ] = Added;
342
void ResourcePrivateBase::removeLocalItem( const QString &uid )
344
const QString subResource = mUidToResourceMap.value( uid );
345
kDebug( 5650 ) << "uid=" << uid << ", subResource=" << subResource;
347
Q_ASSERT( !subResource.isEmpty() );
349
const SubResourceBase *resource = subResourceBase( subResource );
350
Q_ASSERT( resource != 0 );
352
if ( resource->hasMappedItem( uid ) ) {
353
mChanges[ uid ] = Removed;
355
mChanges.remove( uid );
359
ResourcePrivateBase::State ResourcePrivateBase::state() const
364
bool ResourcePrivateBase::isLoading() const
366
return mLoadingInProgress;
369
bool ResourcePrivateBase::startAkonadi()
371
return Akonadi::Control::start();
374
Akonadi::Collection ResourcePrivateBase::storeCollectionForMimeType( const QString &mimeType ) const
376
kDebug( 5650 ) << "mimeType=" << mimeType;
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;
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();
393
return Akonadi::Collection();
396
bool ResourcePrivateBase::prepareItemSaveContext( ItemSaveContext &saveContext )
398
ChangeByKResId::const_iterator it = mChanges.constBegin();
399
ChangeByKResId::const_iterator endIt = mChanges.constEnd();
400
for ( ; it != endIt; ++it ) {
401
if ( !prepareItemSaveContext( it, saveContext ) ) {
410
bool ResourcePrivateBase::prepareItemSaveContext( const ChangeByKResId::const_iterator &it, ItemSaveContext &saveContext )
412
const QString kresId = it.key();
413
const SubResourceBase *resource = subResourceBase( mUidToResourceMap.value( kresId ) );
414
Q_ASSERT( resource != 0 );
416
switch ( it.value() ) {
418
ItemAddContext addContext;
419
addContext.collection = resource->collection();
420
addContext.item = createItem( kresId );
422
saveContext.addedItems << addContext;
427
Akonadi::Item item = updateItem( resource->mappedItem( kresId ), kresId, mIdArbiter->mapToOriginalId( kresId ) );
429
saveContext.changedItems << item;
434
saveContext.removedItems << resource->mappedItem( kresId );
438
Q_ASSERT( "Invalid ChangeType in change map" );
446
void ResourcePrivateBase::subResourceAdded( SubResourceBase *subResource )
448
Q_ASSERT( mIdArbiter != 0 );
450
subResource->setIdArbiter( mIdArbiter );
451
subResource->readConfig( mConfig );
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();
463
} else if ( mDefaultStoreCollection == subResource->collection() ) {
464
// update locally cached instance
465
mDefaultStoreCollection = subResource->collection();
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();
479
void ResourcePrivateBase::subResourceRemoved( SubResourceBase *subResource )
481
Q_UNUSED( subResource );
484
void ResourcePrivateBase::loadingResult( bool ok, const QString &errorString )
487
Q_UNUSED( errorString );
489
Q_ASSERT( mLoadingInProgress );
491
mLoadingInProgress = false;
494
void ResourcePrivateBase::savingResult( bool ok, const QString &errorString )
496
Q_UNUSED( errorString );
502
#include "resourceprivatebase.moc"
504
// kate: space-indent on; indent-width 2; replace-tabs on;