Hello community, here is the log from the commit of package kdepim4-runtime for openSUSE:Factory checked in at 2015-07-14 17:42:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kdepim4-runtime (Old) and /work/SRC/openSUSE:Factory/.kdepim4-runtime.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kdepim4-runtime" Changes: -------- --- /work/SRC/openSUSE:Factory/kdepim4-runtime/kdepim4-runtime.changes 2015-06-04 09:54:46.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.kdepim4-runtime.new/kdepim4-runtime.changes 2015-07-14 17:44:08.000000000 +0200 @@ -1,0 +2,8 @@ +Mon Jul 6 06:34:08 UTC 2015 - [email protected] + +- Update to 4.14.10 + * KDE Applications 15.04.3 + * https://www.kde.org/announcements/announce-applications-15.04.3.php + + +------------------------------------------------------------------- Old: ---- kdepim-runtime-4.14.9.tar.xz New: ---- kdepim-runtime-4.14.10.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kdepim4-runtime.spec ++++++ --- /var/tmp/diff_new_pack.fjqRXt/_old 2015-07-14 17:44:09.000000000 +0200 +++ /var/tmp/diff_new_pack.fjqRXt/_new 2015-07-14 17:44:09.000000000 +0200 @@ -17,7 +17,7 @@ Name: kdepim4-runtime -Version: 4.14.9 +Version: 4.14.10 Release: 0 Summary: Base package of kdepim License: LGPL-2.1+ ++++++ kdepim-runtime-4.14.9.tar.xz -> kdepim-runtime-4.14.10.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/CMakeLists.txt new/kdepim-runtime-4.14.10/CMakeLists.txt --- old/kdepim-runtime-4.14.9/CMakeLists.txt 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/CMakeLists.txt 2015-06-25 22:42:43.000000000 +0200 @@ -36,7 +36,7 @@ set(KDEPIM_RUNTIME_DEV_VERSION "") endif() -set(KDEPIM_RUNTIME_VERSION "4.14.9${KDEPIM_RUNTIME_DEV_VERSION}") +set(KDEPIM_RUNTIME_VERSION "4.14.10${KDEPIM_RUNTIME_DEV_VERSION}") configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) @@ -53,7 +53,7 @@ include(KDE4Defaults) # KdepimLibs -find_package(KdepimLibs 4.14.9) +find_package(KdepimLibs 4.14.10) set_package_properties(KdepimLibs PROPERTIES DESCRIPTION "The KDEPIM libraries" URL "http://www.kde.org" TYPE REQUIRED) #Boost diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davcollectionsfetchjob.cpp new/kdepim-runtime-4.14.10/resources/dav/common/davcollectionsfetchjob.cpp --- old/kdepim-runtime-4.14.9/resources/dav/common/davcollectionsfetchjob.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davcollectionsfetchjob.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -141,7 +141,14 @@ _jobUrl.setUser( QString() ); const QString jobUrl = _jobUrl.prettyUrl(); - //kDebug() << davJob->response().toString(); + // Validate that we got a valid PROPFIND response + QDomElement rootElement = davJob->response().documentElement(); + if ( rootElement.tagName().compare( QLatin1String( "multistatus" ), Qt::CaseInsensitive ) != 0 ) { + setError( UserDefinedError ); + setErrorText( i18n( "Invalid responses from backend" ) ); + subjobFinished(); + return; + } QByteArray resp( davJob->response().toByteArray() ); QBuffer buffer( &resp ); @@ -151,12 +158,16 @@ if ( !xquery.setFocus( &buffer ) ) { setError( UserDefinedError ); setErrorText( i18n( "Error setting focus for XQuery" ) ); + subjobFinished(); + return; } xquery.setQuery( DavManager::self()->davProtocol( mUrl.protocol() )->collectionsXQuery() ); if ( !xquery.isValid() ) { setError( UserDefinedError ); setErrorText( i18n( "Invalid XQuery submitted by DAV implementation" ) ); + subjobFinished(); + return; } QString responsesStr; @@ -168,6 +179,8 @@ if ( !document.setContent( responsesStr, true ) ) { setError( UserDefinedError ); setErrorText( i18n( "Invalid responses from backend" ) ); + subjobFinished(); + return; } if ( !error() ) { @@ -301,6 +314,11 @@ } } + subjobFinished(); +} + +void DavCollectionsFetchJob::subjobFinished() +{ if ( --mSubJobCount == 0 ) emitResult(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davcollectionsfetchjob.h new/kdepim-runtime-4.14.10/resources/dav/common/davcollectionsfetchjob.h --- old/kdepim-runtime-4.14.9/resources/dav/common/davcollectionsfetchjob.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davcollectionsfetchjob.h 2015-06-25 22:42:43.000000000 +0200 @@ -73,6 +73,7 @@ private: void doCollectionsFetch( const KUrl &url ); + void subjobFinished(); DavUtils::DavUrl mUrl; DavCollection::List mCollections; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemdeletejob.cpp new/kdepim-runtime-4.14.10/resources/dav/common/davitemdeletejob.cpp --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemdeletejob.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemdeletejob.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -18,6 +18,7 @@ #include "davitemdeletejob.h" +#include "davitemfetchjob.h" #include "davmanager.h" #include <kio/deletejob.h> @@ -39,6 +40,16 @@ connect( job, SIGNAL(result(KJob*)), this, SLOT(davJobFinished(KJob*)) ); } +DavItem DavItemDeleteJob::freshItem() const +{ + return mFreshItem; +} + +int DavItemDeleteJob::freshResponseCode() const +{ + return mFreshResponseCode; +} + void DavItemDeleteJob::davJobFinished( KJob *job ) { KIO::DeleteJob *deleteJob = qobject_cast<KIO::DeleteJob*>( job ); @@ -60,6 +71,25 @@ setErrorText( i18n( "There was a problem with the request. The item has not been deleted from the server.\n" "%1 (%2).", err, responseCode ) ); } + + if ( hasConflict() ) { + DavItemFetchJob *fetchJob = new DavItemFetchJob( mUrl, mItem ); + connect( fetchJob, SIGNAL(result(KJob*)), this, SLOT(conflictingItemFetched(KJob*)) ); + fetchJob->start(); + return; + } + } + + emitResult(); +} + +void DavItemDeleteJob::conflictingItemFetched( KJob *job ) +{ + DavItemFetchJob *fetchJob = qobject_cast<DavItemFetchJob*>( job ); + mFreshResponseCode = fetchJob->latestResponseCode(); + + if ( !job->error() ) { + mFreshItem = fetchJob->item(); } emitResult(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemdeletejob.h new/kdepim-runtime-4.14.10/resources/dav/common/davitemdeletejob.h --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemdeletejob.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemdeletejob.h 2015-06-25 22:42:43.000000000 +0200 @@ -45,12 +45,25 @@ */ virtual void start(); + /** + * Returns the item that triggered the conflict, if any. + */ + DavItem freshItem() const; + + /** + * Returns the response code we got when fetching the fresh item. + */ + int freshResponseCode() const; + private Q_SLOTS: void davJobFinished( KJob* ); + void conflictingItemFetched( KJob* ); private: DavUtils::DavUrl mUrl; DavItem mItem; + DavItem mFreshItem; + int mFreshResponseCode; }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemfetchjob.cpp new/kdepim-runtime-4.14.10/resources/dav/common/davitemfetchjob.cpp --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemfetchjob.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemfetchjob.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -41,7 +41,7 @@ DavItemFetchJob::DavItemFetchJob( const DavUtils::DavUrl &url, const DavItem &item, QObject *parent ) - : KJob( parent ), mUrl( url ), mItem( item ) + : DavJobBase( parent ), mUrl( url ), mItem( item ) { } @@ -67,11 +67,12 @@ void DavItemFetchJob::davJobFinished( KJob *job ) { KIO::StoredTransferJob *storedJob = qobject_cast<KIO::StoredTransferJob*>( job ); + const int responseCode = storedJob->queryMetaData( QLatin1String("responsecode") ).isEmpty() ? + 0 : + storedJob->queryMetaData( QLatin1String("responsecode") ).toInt(); + setLatestResponseCode( responseCode ); if ( storedJob->error() ) { - const int responseCode = storedJob->queryMetaData( QLatin1String("responsecode") ).isEmpty() ? - 0 : - storedJob->queryMetaData( QLatin1String("responsecode") ).toInt(); QString err; if ( storedJob->error() != KIO::ERR_SLAVE_DEFINED ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemfetchjob.h new/kdepim-runtime-4.14.10/resources/dav/common/davitemfetchjob.h --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemfetchjob.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemfetchjob.h 2015-06-25 22:42:43.000000000 +0200 @@ -20,14 +20,13 @@ #define DAVITEMFETCHJOB_H #include "davitem.h" +#include "davjobbase.h" #include "davutils.h" -#include <kjob.h> - /** * @short A job that fetches a DAV item from the DAV server. */ -class DavItemFetchJob : public KJob +class DavItemFetchJob : public DavJobBase { Q_OBJECT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemmodifyjob.cpp new/kdepim-runtime-4.14.10/resources/dav/common/davitemmodifyjob.cpp --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemmodifyjob.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemmodifyjob.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -25,7 +25,7 @@ #include <klocale.h> DavItemModifyJob::DavItemModifyJob( const DavUtils::DavUrl &url, const DavItem &item, QObject *parent ) - : DavJobBase( parent ), mUrl( url ), mItem( item ) + : DavJobBase( parent ), mUrl( url ), mItem( item ), mFreshResponseCode( 0 ) { } @@ -50,6 +50,16 @@ return mItem; } +DavItem DavItemModifyJob::freshItem() const +{ + return mFreshItem; +} + +int DavItemModifyJob::freshResponseCode() const +{ + return mFreshResponseCode; +} + void DavItemModifyJob::davJobFinished( KJob *job ) { KIO::StoredTransferJob *storedJob = qobject_cast<KIO::StoredTransferJob*>( job ); @@ -70,7 +80,15 @@ setErrorText( i18n( "There was a problem with the request. The item was not modified on the server.\n" "%1 (%2).", err, responseCode ) ); - emitResult(); + if ( hasConflict() ) { + DavItemFetchJob *fetchJob = new DavItemFetchJob( mUrl, mItem ); + connect( fetchJob, SIGNAL(result(KJob*)), this, SLOT(conflictingItemFetched(KJob*)) ); + fetchJob->start(); + } + else { + emitResult(); + } + return; } @@ -111,3 +129,15 @@ emitResult(); } +void DavItemModifyJob::conflictingItemFetched( KJob *job ) +{ + DavItemFetchJob *fetchJob = qobject_cast<DavItemFetchJob*>( job ); + mFreshResponseCode = fetchJob->latestResponseCode(); + + if ( !job->error() ) { + mFreshItem = fetchJob->item(); + } + + emitResult(); +} + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davitemmodifyjob.h new/kdepim-runtime-4.14.10/resources/dav/common/davitemmodifyjob.h --- old/kdepim-runtime-4.14.9/resources/dav/common/davitemmodifyjob.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davitemmodifyjob.h 2015-06-25 22:42:43.000000000 +0200 @@ -50,13 +50,26 @@ */ DavItem item() const; + /** + * Returns the item that triggered the conflict, if any. + */ + DavItem freshItem() const; + + /** + * Returns the response code we got when fetching the fresh item. + */ + int freshResponseCode() const; + private Q_SLOTS: void davJobFinished( KJob* ); void itemRefreshed( KJob* ); + void conflictingItemFetched( KJob* ); private: DavUtils::DavUrl mUrl; DavItem mItem; + DavItem mFreshItem; + int mFreshResponseCode; }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davjobbase.cpp new/kdepim-runtime-4.14.10/resources/dav/common/davjobbase.cpp --- old/kdepim-runtime-4.14.9/resources/dav/common/davjobbase.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davjobbase.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -78,6 +78,11 @@ return ret; } +bool DavJobBase::hasConflict() const +{ + return latestResponseCode() == 412; +} + void DavJobBase::setLatestResponseCode( unsigned int code ) { mLatestResponseCode = code; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/common/davjobbase.h new/kdepim-runtime-4.14.10/resources/dav/common/davjobbase.h --- old/kdepim-runtime-4.14.9/resources/dav/common/davjobbase.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/common/davjobbase.h 2015-06-25 22:42:43.000000000 +0200 @@ -60,6 +60,11 @@ */ bool canRetryLater() const; + /** + * Check if the job failed because of a conflict + */ + bool hasConflict() const; + protected: /** * Sets the latest response code received. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/resource/davgroupwareresource.cpp new/kdepim-runtime-4.14.10/resources/dav/resource/davgroupwareresource.cpp --- old/kdepim-runtime-4.14.9/resources/dav/resource/davgroupwareresource.cpp 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/resource/davgroupwareresource.cpp 2015-06-25 22:42:43.000000000 +0200 @@ -423,7 +423,7 @@ extraItems << extraItem; } } - + if ( extraItems.isEmpty() ) { // Urrrr? // Well, just delete the item. @@ -892,12 +892,17 @@ if ( modifyJob->error() ) { kError() << "Error when uploading item:" << modifyJob->error() << modifyJob->errorString(); - if ( modifyJob->canRetryLater() ) { + + if ( modifyJob->hasConflict() ) { + handleConflict( item, dependentItems, modifyJob->freshItem(), isRemoval, modifyJob->freshResponseCode() ); + } + else if ( modifyJob->canRetryLater() ) { retryAfterFailure(modifyJob->errorString()); } else { cancelTask( i18n( "Unable to change item: %1", modifyJob->errorString() ) ); } + return; } @@ -928,7 +933,38 @@ dependentItems[i].setRemoteRevision( davItem.etag() ); mEtagCache.setEtag( dependentItems.at( i ).remoteId(), davItem.etag() ); } - + + Akonadi::ItemModifyJob *j = new Akonadi::ItemModifyJob( dependentItems ); + j->setIgnorePayload( true ); + } + } +} + +void DavGroupwareResource::onDeletedItemRecreated(KJob* job) +{ + const DavItemCreateJob *createJob = qobject_cast<DavItemCreateJob*>( job ); + const DavItem davItem = createJob->item(); + Akonadi::Item item = createJob->property( "item" ).value<Akonadi::Item>(); + Akonadi::Item::List dependentItems = createJob->property( "dependentItems" ).value<Akonadi::Item::List>(); + + if ( davItem.etag().isEmpty() ) { + const DavUtils::DavUrl davUrl = Settings::self()->davUrlFromCollectionUrl( item.parentCollection().remoteId(), item.remoteId() ); + DavItemFetchJob *fetchJob = new DavItemFetchJob( davUrl, davItem ); + fetchJob->setProperty( "item", QVariant::fromValue( item ) ); + fetchJob->setProperty( "dependentItems", QVariant::fromValue( dependentItems ) ); + connect( fetchJob, SIGNAL(result(KJob*)), SLOT(onItemRefreshed(KJob*)) ); + fetchJob->start(); + } else { + item.setRemoteRevision( davItem.etag() ); + mEtagCache.setEtag( davItem.url(), davItem.etag() ); + changeCommitted( item ); + + if ( !dependentItems.isEmpty() ) { + for ( int i = 0; i < dependentItems.size(); ++i ) { + dependentItems[i].setRemoteRevision( davItem.etag() ); + mEtagCache.setEtag( dependentItems.at( i ).remoteId(), davItem.etag() ); + } + Akonadi::ItemModifyJob *j = new Akonadi::ItemModifyJob( dependentItems ); j->setIgnorePayload( true ); } @@ -940,14 +976,15 @@ if ( job->error() ) { const DavItemDeleteJob *deleteJob = qobject_cast<DavItemDeleteJob*>( job ); - if ( deleteJob->canRetryLater() ) { + if ( deleteJob->hasConflict() ) { + // Use a shortcut here as we don't show a conflict dialog to the user. + handleConflict( Akonadi::Item(), Akonadi::Item::List(), deleteJob->freshItem(), true, 0 ); + } else if ( deleteJob->canRetryLater() ) { retryAfterFailure(job->errorString()); - } - else { + } else { cancelTask( i18n( "Unable to remove item: %1", job->errorString() ) ); } - } - else { + } else { Akonadi::Item item = job->property( "item" ).value<Akonadi::Item>(); Akonadi::Collection collection = job->property( "collection" ).value<Akonadi::Collection>(); mItemsRidCache[collection.remoteId()].remove( item.remoteId() ); @@ -966,6 +1003,129 @@ mEtagCache.setEtag( itemUrl, etag ); } +void DavGroupwareResource::handleConflict( const Item& lI, const Item::List& localDependentItems, const DavItem& rI, bool isLocalRemoval, int responseCode ) +{ + Akonadi::Item localItem( lI ); + Akonadi::Item remoteItem, tmpRemoteItem; // The tmp* vars are here to store the result of the parseDavData() call + Akonadi::Item::List remoteDependentItems, tmpRemoteDependentItems; // as we have no idea which item triggered the conflict. + kDebug() << "Fresh response code is" << responseCode; + bool isRemoteRemoval = ( responseCode == 404 || responseCode == 410 ); + + if ( !isRemoteRemoval ) { + if ( !DavUtils::parseDavData( rI, tmpRemoteItem, tmpRemoteDependentItems ) ) { + // TODO: set a more correct error message here + cancelTask( i18n( "Unable to change item: %1", QLatin1String( "conflict resolution failed" ) ) ); + return; + // TODO: we can end up here if the remote item was deleted + } + + // Now try to find the item that really triggered the conflict + Akonadi::Item::List allRemoteItems; allRemoteItems << tmpRemoteItem << tmpRemoteDependentItems; + foreach ( const Akonadi::Item &tmpItem, allRemoteItems ) { + if ( tmpItem.payloadData() != localItem.payloadData() ) { + if ( remoteItem.isValid() ) { + // Oops, we can only manage one changed item at this stage, sorry... + // TODO: make this translatable + cancelTask( i18n( "Unable to change item: %1", QLatin1String( "more than one item was changed in the backend" ) ) ); + return; + } + remoteItem = tmpItem; + } else { + remoteDependentItems << tmpItem; + } + } + } + + if ( isLocalRemoval ) { + // TODO: implement with the configurable strategy + /* + * Here by default we don't delete an event that was modified in the backend, and + * instead we just abort the current task. + * Also, trigger an immediate sync to refresh the item. + */ + kDebug() << "Local removal conflict"; + // TODO: make this translatable + cancelTask( i18n( "Unable to remove item: %1", QLatin1String( "it was changed in the backend in the meantime" ) ) ); + synchronize(); + } else if ( isRemoteRemoval ) { + // TODO: implement with the configurable strategy + /* + * Here also it is a bit tricky to clear the item in the local cache as the resource + * will not get notified if the user chooses to delete the item and abandon the local + * modification. For the time being let's just re-upload the changed item. + */ + kDebug() << "Remote removal conflict"; + Akonadi::Collection collection = localItem.parentCollection(); + DavItem davItem = DavUtils::createDavItem( localItem, collection, localDependentItems ); + + QString urlStr = localItem.remoteId(); + if ( urlStr.contains( QChar( '#' ) ) ) + urlStr.truncate( urlStr.indexOf( QChar( '#' ) ) ); + davItem.setUrl( urlStr ); + const DavUtils::DavUrl davUrl = Settings::self()->davUrlFromCollectionUrl( collection.remoteId(), urlStr ); + + DavItemCreateJob *job = new DavItemCreateJob( davUrl, davItem ); + job->setProperty( "item", QVariant::fromValue( localItem ) ); + job->setProperty( "dependentItems", QVariant::fromValue( localDependentItems ) ); + connect( job, SIGNAL(result(KJob*)), SLOT(onDeletedItemRecreated(KJob*)) ); + job->start(); + } else { + const QString remoteEtag = rI.etag(); + + localItem.setRemoteRevision( remoteEtag ); + changeCommitted( localItem ); + + // Update the ETag cache in all cases as the new ETag will have to be used + // later for any update or deletion + mEtagCache.setEtag( rI.url(), remoteEtag ); + + // The first step is to fire a first modify job that will replace the item currently + // in the local cache with the one that was found in the backend. + Akonadi::Item updatedItem( localItem ); + updatedItem.setPayloadFromData( remoteItem.payloadData() ); + updatedItem.setRemoteRevision( remoteEtag ); + Akonadi::ItemModifyJob *j = new Akonadi::ItemModifyJob( updatedItem ); + j->setIgnorePayload( false ); + j->start(); + + // So now we have in the cache what's in the backend but the user is not aware + // that behind the scenes something terrible is happening. Well, nearly... + // To notify him of this, and due to the way the conflict handler works, we have + // to re-attempt a modification to revert the modify job that was just fired. + // So yes, we are effectively re-submitting the client-provided content, but + // with a revision that will trigger the conflict dialog. + // The only problem is that the user will see that we update the item before + // the conflict dialog has time to display (if it's not behind the application + // window). + localItem.setRevision( 0 ); + j = new Akonadi::ItemModifyJob( localItem ); + j->setIgnorePayload( false ); + connect( j, SIGNAL(result(KJob*)), this, SLOT(onConflictModifyJobFinished(KJob*)) ); + j->start(); + + // Hopefully for the dependent items everything will be fine. Right? + // Not so sure in fact. + if ( !remoteDependentItems.isEmpty() ) { + for ( int i = 0; i < remoteDependentItems.size(); ++i ) { + remoteDependentItems[i].setRemoteRevision( remoteEtag ); + mEtagCache.setEtag( remoteDependentItems.at( i ).remoteId(), remoteEtag ); + } + + Akonadi::ItemModifyJob *j = new Akonadi::ItemModifyJob( remoteDependentItems ); + j->setIgnorePayload( true ); + } + } +} + +void DavGroupwareResource::onConflictModifyJobFinished( KJob *job ) +{ + Akonadi::ItemModifyJob *j = qobject_cast<Akonadi::ItemModifyJob*>( job ); + if ( j->error() ) { + kError() << "Conflict update failed: " << job->errorText(); + // TODO: what do we do now? We just committed an item that's in a weird state... + } +} + bool DavGroupwareResource::configurationIsValid() { if ( Settings::self()->configuredDavUrls().empty() ) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kdepim-runtime-4.14.9/resources/dav/resource/davgroupwareresource.h new/kdepim-runtime-4.14.10/resources/dav/resource/davgroupwareresource.h --- old/kdepim-runtime-4.14.9/resources/dav/resource/davgroupwareresource.h 2015-05-28 10:18:16.000000000 +0200 +++ new/kdepim-runtime-4.14.10/resources/dav/resource/davgroupwareresource.h 2015-06-25 22:42:43.000000000 +0200 @@ -101,10 +101,18 @@ void onCollectionDiscovered( int protocol, const QString &collectionUrl, const QString &configuredUrl ); void onEtagChanged( const QString &itemUrl, const QString &etag ); + void onConflictModifyJobFinished( KJob *job ); + void onDeletedItemRecreated( KJob *job ); private: void doItemChange( const Akonadi::Item &item, const Akonadi::Item::List &dependentItems = Akonadi::Item::List() ); void doItemRemoval( const Akonadi::Item &item ); + void handleConflict( const Akonadi::Item &localItem, + const Akonadi::Item::List &localDependentItems, + const DavItem &remoteItem, + bool isLocalRemoval, + int responseCode + ); bool configurationIsValid(); void retryAfterFailure(const QString &errorMessage);
