Branch 'kolab/integration/4.13.0' - 9 commits - akonadi/contact akonadi/itemcreatejob.cpp akonadi/itemcreatejob.h akonadi/itemdeletejob.cpp akonadi/itemsync.cpp akonadi/itemsync.h akonadi/protocolhelper.cpp akonadi/session_p.h akonadi/tagcreatejob.cpp akonadi/tageditwidget.cpp akonadi/tagmodifyjob.cpp akonadi/tests kholidays/holidays
Christian Mollekopf
mollekopf at kolabsys.com
Thu May 8 12:03:16 CEST 2014
akonadi/contact/contactgroupsearchjob.cpp | 2
akonadi/contact/contactsearchjob.cpp | 2
akonadi/itemcreatejob.cpp | 85 +++++++++++
akonadi/itemcreatejob.h | 30 ++++
akonadi/itemdeletejob.cpp | 8 +
akonadi/itemsync.cpp | 213 ++++--------------------------
akonadi/itemsync.h | 5
akonadi/protocolhelper.cpp | 2
akonadi/session_p.h | 2
akonadi/tagcreatejob.cpp | 6
akonadi/tageditwidget.cpp | 2
akonadi/tagmodifyjob.cpp | 10 -
akonadi/tests/itemappendtest.cpp | 101 +++++++++++++-
akonadi/tests/itemappendtest.h | 2
akonadi/tests/itemdeletetest.cpp | 38 +++++
akonadi/tests/itemsynctest.cpp | 69 ++++++++-
akonadi/tests/tagtest.cpp | 35 ++++
kholidays/holidays/plan2/holiday_gr_el | 89 ++++++------
18 files changed, 450 insertions(+), 251 deletions(-)
New commits:
commit e9df106d2ccdcf0a5057713122c4ea3f2b622dec
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date: Wed May 7 01:10:41 2014 +0200
Drop akonadi requirement to kolab version.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c5da8d..5b67f7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,7 +80,7 @@ endif()
if (NOT KDEPIM_ONLY_KLEO)
#FindAkonadi.cmake is only there for compatibility reasons, but we don't want to use that.
- set(Akonadi_MIN_VERSION "1.12.44")
+ set(Akonadi_MIN_VERSION "1.12.42")
find_package(Akonadi ${Akonadi_MIN_VERSION} QUIET NO_MODULE)
set_package_properties(Akonadi PROPERTIES DESCRIPTION "Akonadi server libraries" URL "http://pim.kde.org/akonadi" TYPE REQUIRED PURPOSE "Access to PIM storage and services")
commit 4b4dffa33201f5d7129052c4513e8a54736f0030
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date: Tue May 6 16:29:56 2014 +0200
ItemSync: Use serverside merging.
Instead of fetching all local items to check for changes, to then write modified items
back to the server, we use the serverside merging instead.
Additionally this patch allows to merge by gid instead of rid, simply by setting
the gid additionally to the rid.
By using the serverside merging, the code could be simplified and we only need
to fetch the local items anymore to detect removed items when doing a full
listing.
This patch renders ItemSync::updateItem useless, but that should be ok as
I couldn't find a single subclass of ItemSync, and I really don't
expect there to be one out there.
diff --git a/akonadi/itemsync.cpp b/akonadi/itemsync.cpp
index 034226c..9f833e6 100644
--- a/akonadi/itemsync.cpp
+++ b/akonadi/itemsync.cpp
@@ -67,12 +67,10 @@ public:
mFetchScope.fetchAllAttributes();
}
- void createLocalItem(const Item &item);
- void modifyLocalItem(const Item &remoteItem, Akonadi::Item::Id localId);
+ void createOrMerge(const Item &item);
void checkDone();
void slotItemsReceived(const Item::List &items);
void slotLocalListDone(KJob *job);
- void slotLocalFetchDone(KJob *job);
void slotLocalDeleteDone(KJob *);
void slotLocalChangeDone(KJob *job);
void execute();
@@ -82,14 +80,13 @@ public:
void slotTransactionResult(KJob *job);
void requestTransaction();
Job *subjobParent() const;
- void fetchLocalItems();
+ void fetchLocalItemsToDelete();
QString jobDebuggingString() const /*Q_DECL_OVERRIDE*/;
bool allProcessed() const;
Q_DECLARE_PUBLIC(ItemSync)
Collection mSyncCollection;
- QSet<Akonadi::Item::Id> mUnprocessedLocalIds;
- QHash<QString, Akonadi::Item::Id> mLocalIdByRid;
+ QSet<QString> mListedItems;
ItemSync::TransactionMode mTransactionMode;
TransactionSequence *mCurrentTransaction;
@@ -102,6 +99,7 @@ public:
Akonadi::Item::List mRemovedRemoteItemQueue;
Akonadi::Item::List mCurrentBatchRemoteItems;
Akonadi::Item::List mCurrentBatchRemovedRemoteItems;
+ Akonadi::Item::List mItemsToDelete;
// create counter
int mPendingJobs;
@@ -120,7 +118,7 @@ public:
int mBatchSize;
};
-void ItemSyncPrivate::createLocalItem(const Item &item)
+void ItemSyncPrivate::createOrMerge(const Item &item)
{
Q_Q(ItemSync);
// don't try to do anything in error state
@@ -129,57 +127,12 @@ void ItemSyncPrivate::createLocalItem(const Item &item)
}
mPendingJobs++;
ItemCreateJob *create = new ItemCreateJob(item, mSyncCollection, subjobParent());
- q->connect(create, SIGNAL(result(KJob*)), q, SLOT(slotLocalChangeDone(KJob*)));
-}
-
-void ItemSyncPrivate::modifyLocalItem(const Item &remoteItem, Akonadi::Item::Id localId)
-{
- Q_Q(ItemSync);
- // don't try to do anything in error state
- if (q->error()) {
- return;
- }
-
- //we fetch the local item to check if a modification is required and to make sure we have all parts
- Akonadi::ItemFetchJob *fetchJob = new Akonadi::ItemFetchJob(Akonadi::Item(localId), subjobParent());
- fetchJob->setFetchScope(mFetchScope);
- fetchJob->fetchScope().setCacheOnly(true);
- fetchJob->setDeliveryOption(ItemFetchJob::ItemGetter);
- q->connect(fetchJob, SIGNAL(result(KJob*)), q, SLOT(slotLocalFetchDone(KJob*)));
- fetchJob->setProperty("remoteItem", QVariant::fromValue(remoteItem));
- mPendingJobs++;
-}
-
-void ItemSyncPrivate::slotLocalFetchDone(KJob *job)
-{
- Q_Q(ItemSync);
- mPendingJobs--;
- if (job->error()) {
- kWarning() << job->errorString();
- checkDone();
- return;
- }
- Akonadi::ItemFetchJob *fetchJob = static_cast<Akonadi::ItemFetchJob*>(job);
- Akonadi::Item remoteItem = fetchJob->property("remoteItem").value<Akonadi::Item>();
- if (fetchJob->items().isEmpty()) {
- kWarning() << "Failed to fetch local item: " << remoteItem.remoteId() << remoteItem.gid();
- checkDone();
- return;
- }
- const Akonadi::Item localItem = fetchJob->items().first();
- if (q->updateItem(localItem, remoteItem)) {
- remoteItem.setId(localItem.id());
- remoteItem.setRevision(localItem.revision());
- remoteItem.setSize(localItem.size());
- remoteItem.setRemoteId(localItem.remoteId()); // in case someone clears remoteId by accident
- ItemModifyJob *mod = new ItemModifyJob(remoteItem, subjobParent());
- mod->disableRevisionCheck();
- q->connect(mod, SIGNAL(result(KJob*)), q, SLOT(slotLocalChangeDone(KJob*)));
- mPendingJobs++;
+ if (!item.gid().isEmpty()) {
+ create->setMerge(ItemCreateJob::GID|ItemCreateJob::Silent);
} else {
- mProgress++;
+ create->setMerge(ItemCreateJob::RID|ItemCreateJob::Silent);
}
- checkDone();
+ q->connect(create, SIGNAL(result(KJob*)), q, SLOT(slotLocalChangeDone(KJob*)));
}
bool ItemSyncPrivate::allProcessed() const
@@ -323,103 +276,26 @@ void ItemSync::doStart()
bool ItemSync::updateItem(const Item &storedItem, Item &newItem)
{
- Q_D(ItemSync);
- // we are in error state, better not change anything at all anymore
- if (error()) {
- return false;
- }
-
- /*
- * We know that this item has changed (as it is part of the
- * incremental changed list), so we just put it into the
- * storage.
- */
- if (d->mIncremental) {
- return true;
- }
-
- if (newItem.d_func()->mClearPayload) {
- return true;
- }
-
- // Check whether the remote revisions differ
- if (storedItem.remoteRevision() != newItem.remoteRevision()) {
- return true;
- }
-
- // Check whether the flags differ
- if (storedItem.flags() != newItem.flags()) {
- kDebug() << "Stored flags " << storedItem.flags()
- << "new flags " << newItem.flags();
- return true;
- }
-
- // Check whether the new item contains unknown parts
- QSet<QByteArray> missingParts = newItem.loadedPayloadParts();
- missingParts.subtract(storedItem.loadedPayloadParts());
- if (!missingParts.isEmpty()) {
- return true;
- }
-
- // ### FIXME SLOW!!!
- // If the available part identifiers don't differ, check
- // whether the content of the payload differs
- if (newItem.hasPayload()
- && storedItem.payloadData() != newItem.payloadData()) {
- return true;
- }
-
- // check if remote attributes have been changed
- foreach (Attribute *attr, newItem.attributes()) {
- if (!storedItem.hasAttribute(attr->type())) {
- return true;
- }
- if (attr->serialized() != storedItem.attribute(attr->type())->serialized()) {
- return true;
- }
- }
-
+ Q_UNUSED(storedItem);
+ Q_UNUSED(newItem);
return false;
}
-void ItemSyncPrivate::fetchLocalItems()
+void ItemSyncPrivate::fetchLocalItemsToDelete()
{
- Q_Q( ItemSync );
- ItemFetchJob* job;
+ Q_Q(ItemSync);
if (mIncremental) {
- //Try fetching the items so we have their id and know if they're available
- const Akonadi::Item::List itemsToFetch = mCurrentBatchRemoteItems + mCurrentBatchRemovedRemoteItems;
- if (itemsToFetch.isEmpty()) {
- // The fetch job produces an error with an empty set
- processBatch();
- return;
- }
- // We need to fetch the items only to detect if they are new or modified
- job = new ItemFetchJob(itemsToFetch, subjobParent());
- job->fetchScope().setFetchRemoteIdentification(true);
- job->fetchScope().setFetchModificationTime(false);
- job->setCollection(mSyncCollection);
- job->setDeliveryOption(ItemFetchJob::EmitItemsIndividually);
- // We use this to check if items are available locally, so errors are inevitable
- job->fetchScope().setIgnoreRetrievalErrors(true);
- QObject::connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)), q, SLOT(slotItemsReceived(Akonadi::Item::List)));
- } else {
- if (mFullListingDone) {
- processBatch();
- return;
- }
- //Otherwise we'll remove the created items again during the second run
- mFullListingDone = true;
- job = new ItemFetchJob(mSyncCollection, subjobParent());
- job->fetchScope().setFetchRemoteIdentification(true);
- job->fetchScope().setFetchModificationTime(false);
- job->setDeliveryOption(ItemFetchJob::EmitItemsIndividually);
- QObject::connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)), q, SLOT(slotItemsReceived(Akonadi::Item::List)));
+ kFatal() << "This must not be called while in incremental mode";
+ return;
}
-
+ ItemFetchJob *job = new ItemFetchJob(mSyncCollection, subjobParent());
+ job->fetchScope().setFetchRemoteIdentification(true);
+ job->fetchScope().setFetchModificationTime(false);
+ job->setDeliveryOption(ItemFetchJob::EmitItemsIndividually);
// we only can fetch parts already in the cache, otherwise this will deadlock
job->fetchScope().setCacheOnly(true);
+ QObject::connect(job, SIGNAL(itemsReceived(Akonadi::Item::List)), q, SLOT(slotItemsReceived(Akonadi::Item::List)));
QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(slotLocalListDone(KJob*)));
mPendingJobs++;
}
@@ -431,13 +307,8 @@ void ItemSyncPrivate::slotItemsReceived(const Item::List &items)
if (item.remoteId().isEmpty()) {
continue;
}
- if (mLocalIdByRid.contains(item.remoteId())) {
- kWarning() << "Found multiple items with the same rid : " << item.remoteId() << item.id();
- } else {
- mLocalIdByRid.insert(item.remoteId(), item.id());
- }
- if (!mIncremental) {
- mUnprocessedLocalIds << item.id();
+ if (!mListedItems.contains(item.remoteId())) {
+ mItemsToDelete << Item(item.id());
}
}
}
@@ -448,7 +319,8 @@ void ItemSyncPrivate::slotLocalListDone(KJob *job)
if (job->error()) {
kWarning() << job->errorString();
}
- processBatch();
+ deleteItems(mItemsToDelete);
+ checkDone();
}
QString ItemSyncPrivate::jobDebuggingString() const /*Q_DECL_OVERRIDE*/
@@ -482,7 +354,7 @@ void ItemSyncPrivate::execute()
return;
}
mProcessingBatch = true;
- fetchLocalItems();
+ processBatch();
return;
}
checkDone();
@@ -502,29 +374,14 @@ void ItemSyncPrivate::processBatch()
processItems();
// removed
- Akonadi::Item::List itemsToDelete;
if (!mIncremental && allProcessed()) {
//the full listing is done and we know which items to remove
- foreach (Akonadi::Item::Id id, mUnprocessedLocalIds) {
- itemsToDelete << Akonadi::Item(id);
- }
- mUnprocessedLocalIds.clear();
+ fetchLocalItemsToDelete();
} else {
- foreach (const Akonadi::Item &removedItem, mCurrentBatchRemovedRemoteItems) {
- if (!mLocalIdByRid.contains(removedItem.remoteId())) {
- kWarning() << "cannot remove item because it's not available locally. RID: " << removedItem.remoteId();
- continue;
- }
- itemsToDelete << Akonadi::Item(mLocalIdByRid.value(removedItem.remoteId()));
- }
+ deleteItems(mCurrentBatchRemovedRemoteItems);
mCurrentBatchRemovedRemoteItems.clear();
}
- deleteItems(itemsToDelete);
- if (mIncremental) {
- //no longer required, we processed all items of the current batch
- mLocalIdByRid.clear();
- }
checkDone();
}
@@ -534,21 +391,13 @@ void ItemSyncPrivate::processItems()
// added / updated
foreach (const Item &remoteItem, mCurrentBatchRemoteItems) {
if (remoteItem.remoteId().isEmpty()) {
- kWarning() << "Item " << remoteItem.id() << " does not have a remote identifier";
+ kWarning() << "Item without rid passed to itemsync";
continue;
}
-
- //TODO also check by id and gid
- //Locally available
- if (mLocalIdByRid.contains(remoteItem.remoteId())) {
- const Akonadi::Item::Id localId = mLocalIdByRid.value(remoteItem.remoteId());
- if (!mIncremental) {
- mUnprocessedLocalIds.remove(localId);
- }
- modifyLocalItem(remoteItem, localId);
- } else {
- createLocalItem(remoteItem);
+ if (!mIncremental) {
+ mListedItems << remoteItem.remoteId();
}
+ createOrMerge(remoteItem);
}
mCurrentBatchRemoteItems.clear();
}
diff --git a/akonadi/itemsync.h b/akonadi/itemsync.h
index ab409f3..08729bd 100644
--- a/akonadi/itemsync.h
+++ b/akonadi/itemsync.h
@@ -236,8 +236,10 @@ protected:
* @param newItem the item as it should be
* You can update the @p newItem according to the @p storedItem before
* it gets committed.
+ *
+ * @deprecated This method is disabled internally.
*/
- virtual bool updateItem(const Item &storedItem, Item &newItem);
+ AKONADI_DEPRECATED virtual bool updateItem(const Item &storedItem, Item &newItem);
private:
//@cond PRIVATE
@@ -249,7 +251,6 @@ private:
Q_PRIVATE_SLOT(d_func(), void slotLocalChangeDone(KJob *))
Q_PRIVATE_SLOT(d_func(), void slotTransactionResult(KJob *))
Q_PRIVATE_SLOT(d_func(), void slotItemsReceived(const Akonadi::Item::List &))
- Q_PRIVATE_SLOT(d_func(), void slotLocalFetchDone(KJob *))
//@endcond
};
diff --git a/akonadi/tests/itemsynctest.cpp b/akonadi/tests/itemsynctest.cpp
index 2a2e22f..41a32c4 100644
--- a/akonadi/tests/itemsynctest.cpp
+++ b/akonadi/tests/itemsynctest.cpp
@@ -27,6 +27,7 @@
#include <akonadi/itemfetchjob.h>
#include <akonadi/itemfetchscope.h>
#include <akonadi/itemsync.h>
+#include <akonadi/itemcreatejob.h>
#include <krandom.h>
@@ -66,6 +67,14 @@ class ItemsyncTest : public QObject
qRegisterMetaType<ItemSync::TransactionMode>();
}
+ static Item modifyItem(Item item)
+ {
+ static int counter = 0;
+ item.setFlag(QByteArray("\\READ")+ QByteArray::number(counter));
+ counter++;
+ return item;
+ }
+
void testFullSync()
{
const Collection col = Collection( collectionIdFromPath( "res1/foo" ) );
@@ -146,7 +155,8 @@ class ItemsyncTest : public QObject
for ( int i = 0; i < origItems.count(); ++i ) {
Item::List l;
- l << origItems[i];
+ //Modify to trigger a changed signal
+ l << modifyItem(origItems[i]);
syncer->setFullSyncItems( l );
if (goToEventLoopAfterAddingItems) {
QTest::qWait(0);
@@ -174,7 +184,7 @@ class ItemsyncTest : public QObject
QTest::qWait(100);
QTRY_COMPARE(deletedSpy.count(), 0);
QTRY_COMPARE(addedSpy.count(), 0);
- QTRY_COMPARE(changedSpy.count(), 0);
+ QTRY_COMPARE(changedSpy.count(), origItems.count());
}
void testIncrementalSync()
@@ -207,7 +217,7 @@ class ItemsyncTest : public QObject
QTest::qWait(100);
QTRY_COMPARE(deletedSpy.count(), 0);
QCOMPARE(addedSpy.count(), 0);
- QTRY_COMPARE(changedSpy.count(), origItems.count());
+ QTRY_COMPARE(changedSpy.count(), 0);
deletedSpy.clear();
addedSpy.clear();
changedSpy.clear();
@@ -243,7 +253,7 @@ class ItemsyncTest : public QObject
QTest::qWait(100);
QTRY_COMPARE(deletedSpy.count(), 2);
QCOMPARE(addedSpy.count(), 0);
- QTRY_COMPARE(changedSpy.count(), resultItems.count());
+ QTRY_COMPARE(changedSpy.count(), 0);
}
void testIncrementalStreamingSync()
@@ -274,7 +284,8 @@ class ItemsyncTest : public QObject
for ( int i = 0; i < origItems.count(); ++i ) {
Item::List l;
- l << origItems[i];
+ //Modify to trigger a changed signal
+ l << modifyItem(origItems[i]);
syncer->setIncrementalSyncItems( l, Item::List() );
if ( i < origItems.count() - 1 ) {
QTest::qWait( 0 ); // enter the event loop so itemsync actually can do something
@@ -296,7 +307,7 @@ class ItemsyncTest : public QObject
QTest::qWait(100);
QCOMPARE(deletedSpy.count(), 0);
QCOMPARE(addedSpy.count(), 0);
- QTRY_COMPARE(changedSpy.count(), origItems.count());
+ QTRY_COMPARE(changedSpy.count(), origItems.size());
}
void testEmptyIncrementalSync()
@@ -359,7 +370,8 @@ class ItemsyncTest : public QObject
for ( int i = 0; i < syncer->batchSize(); ++i ) {
Item::List l;
- l << origItems[i];
+ //Modify to trigger a changed signal
+ l << modifyItem(origItems[i]);
syncer->setIncrementalSyncItems( l, Item::List() );
if ( i < (syncer->batchSize() - 1) ) {
QTest::qWait( 0 ); // enter the event loop so itemsync actually can do something
@@ -373,7 +385,8 @@ class ItemsyncTest : public QObject
for ( int i = syncer->batchSize(); i < origItems.count(); ++i ) {
Item::List l;
- l << origItems[i];
+ //Modify to trigger a changed signal
+ l << modifyItem(origItems[i]);
syncer->setIncrementalSyncItems( l, Item::List() );
if ( i < origItems.count() - 1 ) {
QTest::qWait( 0 ); // enter the event loop so itemsync actually can do something
@@ -395,6 +408,46 @@ class ItemsyncTest : public QObject
QTRY_COMPARE(changedSpy.count(), resultItems.count());
}
+ void testGidMerge()
+ {
+ Collection col(collectionIdFromPath("res3"));
+ {
+ Item item("application/octet-stream");
+ item.setRemoteId("rid1");
+ item.setGid("gid1");
+ item.setPayload<QByteArray>("payload1");
+ ItemCreateJob *job = new ItemCreateJob(item, col);
+ AKVERIFYEXEC(job);
+ }
+ {
+ Item item("application/octet-stream");
+ item.setRemoteId("rid2");
+ item.setGid("gid2");
+ item.setPayload<QByteArray>("payload1");
+ ItemCreateJob *job = new ItemCreateJob(item, col);
+ AKVERIFYEXEC(job);
+ }
+ Item modifiedItem("application/octet-stream");
+ modifiedItem.setRemoteId("rid3");
+ modifiedItem.setGid("gid2");
+ modifiedItem.setPayload<QByteArray>("payload2");
+
+ ItemSync* syncer = new ItemSync(col);
+ syncer->setTransactionMode(ItemSync::MultipleTransactions);
+ syncer->setIncrementalSyncItems(Item::List() << modifiedItem, Item::List());
+ AKVERIFYEXEC(syncer);
+
+ Item::List resultItems = fetchItems(col);
+ QCOMPARE(resultItems.count(), 2);
+
+ ItemFetchJob *fetchJob = new ItemFetchJob(modifiedItem);
+ fetchJob->fetchScope().fetchFullPayload();
+ AKVERIFYEXEC(fetchJob);
+ QCOMPARE(fetchJob->items().size(), 1);
+ QCOMPARE(fetchJob->items().first().payload<QByteArray>(), QByteArray("payload2"));
+ QCOMPARE(fetchJob->items().first().remoteId(), QString::fromLatin1("rid3"));
+ }
+
};
QTEST_AKONADIMAIN( ItemsyncTest, NoGUI )
commit 1c5dfc3388d9f8bbfcf08e14c22a10dead69fc22
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date: Wed May 7 00:17:22 2014 +0200
TagCreateJob/TagModifyJob: tag type support + proper REMOTEID key + only finish job once complete.
REVIEW: 117771
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 53b3501..6c5da8d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,7 +80,7 @@ endif()
if (NOT KDEPIM_ONLY_KLEO)
#FindAkonadi.cmake is only there for compatibility reasons, but we don't want to use that.
- set(Akonadi_MIN_VERSION "1.12.43")
+ set(Akonadi_MIN_VERSION "1.12.44")
find_package(Akonadi ${Akonadi_MIN_VERSION} QUIET NO_MODULE)
set_package_properties(Akonadi PROPERTIES DESCRIPTION "Akonadi server libraries" URL "http://pim.kde.org/akonadi" TYPE REQUIRED PURPOSE "Access to PIM storage and services")
diff --git a/akonadi/protocolhelper.cpp b/akonadi/protocolhelper.cpp
index fe20c58..d9f30da 100644
--- a/akonadi/protocolhelper.cpp
+++ b/akonadi/protocolhelper.cpp
@@ -604,6 +604,8 @@ void ProtocolHelper::parseTagFetchResult( const QList<QByteArray> &lineTokens, T
tag.setRemoteId(value);
} else if (key == "PARENT") {
tag.setParent(Tag(value.toLongLong()));
+ } else if ( key == "MIMETYPE" ) {
+ tag.setType(value);
} else {
Attribute *attr = AttributeFactory::createAttribute(key);
if (!attr) {
diff --git a/akonadi/session_p.h b/akonadi/session_p.h
index 2f034ef..40e3fe6 100644
--- a/akonadi/session_p.h
+++ b/akonadi/session_p.h
@@ -116,7 +116,7 @@ public:
static int minimumProtocolVersion()
{
- return 38;
+ return 39;
}
/**
diff --git a/akonadi/tagcreatejob.cpp b/akonadi/tagcreatejob.cpp
index a9e8408..5e6ce18 100644
--- a/akonadi/tagcreatejob.cpp
+++ b/akonadi/tagcreatejob.cpp
@@ -73,8 +73,12 @@ void TagCreateJob::doStart()
list << "MERGE";
}
+ if (!d->mTag.type().isEmpty()) {
+ list << "MIMETYPE";
+ list << ImapParser::quote(d->mTag.type());
+ }
if (!d->mTag.remoteId().isEmpty()) {
- list << "RID";
+ list << "REMOTEID";
list << ImapParser::quote(d->mTag.remoteId());
}
if (d->mTag.parent().isValid()) {
diff --git a/akonadi/tagmodifyjob.cpp b/akonadi/tagmodifyjob.cpp
index 338c3f6..c4f8432 100644
--- a/akonadi/tagmodifyjob.cpp
+++ b/akonadi/tagmodifyjob.cpp
@@ -48,9 +48,13 @@ void TagModifyJob::doStart()
QList<QByteArray> list;
if (!d->mTag.remoteId().isEmpty()) {
- list << "RID";
+ list << "REMOTEID";
list << ImapParser::quote(d->mTag.remoteId());
}
+ if (!d->mTag.type().isEmpty()) {
+ list << "MIMETYPE";
+ list << ImapParser::quote(d->mTag.type());
+ }
if (d->mTag.parent().isValid() && !d->mTag.isImmutable()) {
list << "PARENT";
list << QString::number(d->mTag.parent().id()).toLatin1();
@@ -92,8 +96,6 @@ void TagModifyJob::doHandleResponse(const QByteArray &tag, const QByteArray &dat
if (data.startsWith("OK")) { //krazy:exclude=strings
ChangeMediator::invalidateTag(d->mTag);
+ emitResult();
}
-
- emitResult();
- return;
}
diff --git a/akonadi/tests/tagtest.cpp b/akonadi/tests/tagtest.cpp
index 9b24b12..6595464 100644
--- a/akonadi/tests/tagtest.cpp
+++ b/akonadi/tests/tagtest.cpp
@@ -28,6 +28,7 @@
#include <akonadi/tagattribute.h>
#include <akonadi/tagfetchscope.h>
#include <tagmodifyjob.h>
+#include <resourceselectjob_p.h>
#include <akonadi/qtest_akonadi.h>
#include <akonadi/item.h>
#include <akonadi/itemcreatejob.h>
@@ -47,6 +48,7 @@ private Q_SLOTS:
void initTestCase();
void testCreateFetch();
+ void testRID();
void testDelete();
void testModify();
void testCreateMerge();
@@ -69,6 +71,7 @@ void TagTest::testCreateFetch()
{
Tag tag;
tag.setGid("gid");
+ tag.setType("mytype");
TagCreateJob *createjob = new TagCreateJob(tag, this);
AKVERIFYEXEC(createjob);
QVERIFY(createjob->tag().isValid());
@@ -78,6 +81,7 @@ void TagTest::testCreateFetch()
AKVERIFYEXEC(fetchJob);
QCOMPARE(fetchJob->tags().size(), 1);
QCOMPARE(fetchJob->tags().first().gid(), QByteArray("gid"));
+ QCOMPARE(fetchJob->tags().first().type(), QByteArray("mytype"));
kDebug() << fetchJob->tags().first().id();
TagDeleteJob *deleteJob = new TagDeleteJob(fetchJob->tags().first(), this);
@@ -91,6 +95,37 @@ void TagTest::testCreateFetch()
}
}
+void TagTest::testRID()
+{
+ {
+ ResourceSelectJob *select = new ResourceSelectJob("akonadi_knut_resource_0");
+ AKVERIFYEXEC(select);
+ }
+ Tag tag;
+ tag.setGid("gid");
+ tag.setType("mytype");
+ tag.setRemoteId("rid");
+ TagCreateJob *createjob = new TagCreateJob(tag, this);
+ AKVERIFYEXEC(createjob);
+ QVERIFY(createjob->tag().isValid());
+
+ {
+ TagFetchJob *fetchJob = new TagFetchJob(this);
+ AKVERIFYEXEC(fetchJob);
+ QCOMPARE(fetchJob->tags().size(), 1);
+ QCOMPARE(fetchJob->tags().first().gid(), QByteArray("gid"));
+ QCOMPARE(fetchJob->tags().first().type(), QByteArray("mytype"));
+ QCOMPARE(fetchJob->tags().first().remoteId(), QByteArray("rid"));
+
+ TagDeleteJob *deleteJob = new TagDeleteJob(fetchJob->tags().first(), this);
+ AKVERIFYEXEC(deleteJob);
+ }
+ {
+ ResourceSelectJob *select = new ResourceSelectJob("");
+ AKVERIFYEXEC(select);
+ }
+}
+
void TagTest::testDelete()
{
Tag tag1;
commit fd6439ba8a6ac9c1daf862710b98500199459fff
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date: Tue May 6 16:02:23 2014 +0200
ItemCreateJob: Explicit merge API + support for incremental changes + tag support.
* Merge only by certain identifiers.
* Only merge certain parts of an item (such as flags)
* allow to pass tags with a created/merged item.
diff --git a/akonadi/itemcreatejob.cpp b/akonadi/itemcreatejob.cpp
index 10b978c..621ad6a 100644
--- a/akonadi/itemcreatejob.cpp
+++ b/akonadi/itemcreatejob.cpp
@@ -23,6 +23,7 @@
#include "collection.h"
#include "imapparser_p.h"
#include "item.h"
+#include "item_p.h"
#include "itemserializer_p.h"
#include "job_p.h"
#include "protocolhelper_p.h"
@@ -39,7 +40,8 @@ class Akonadi::ItemCreateJobPrivate : public JobPrivate
public:
ItemCreateJobPrivate(ItemCreateJob *parent)
: JobPrivate(parent)
- , mMerge(false)
+ , mMergeOptions(ItemCreateJob::NoMerge)
+ , mItemReceived(false)
{
}
@@ -51,7 +53,8 @@ public:
Item::Id mUid;
QDateTime mDatetime;
QByteArray mPendingData;
- bool mMerge;
+ ItemCreateJob::MergeOptions mMergeOptions;
+ bool mItemReceived;
};
QByteArray ItemCreateJobPrivate::nextPartHeader()
@@ -106,7 +109,6 @@ void ItemCreateJob::doStart()
QList<QByteArray> flags;
flags.append("\\MimeType[" + d->mItem.mimeType().toLatin1() + ']');
const QString gid = GidExtractor::getGid(d->mItem);
- const bool merge = d->mMerge && !d->mItem.gid().isNull();
if (!gid.isNull()) {
flags.append(ImapParser::quote("\\Gid[" + gid.toUtf8() + ']'));
}
@@ -116,17 +118,48 @@ void ItemCreateJob::doStart()
if (!d->mItem.remoteRevision().isEmpty()) {
flags.append(ImapParser::quote("\\RemoteRevision[" + d->mItem.remoteRevision().toUtf8() + ']'));
}
- flags += d->mItem.flags().toList();
+ const bool mergeByGid = (d->mMergeOptions & GID) && !d->mItem.gid().isEmpty();
+ const bool mergeByRid = (d->mMergeOptions & RID) && !d->mItem.remoteId().isEmpty();
+ const bool mergeSilent = (d->mMergeOptions & Silent);
+ const bool merge = mergeByGid || mergeByRid;
+ if (d->mItem.d_func()->mFlagsOverwritten || !merge) {
+ flags += d->mItem.flags().toList();
+ } else {
+ Q_FOREACH(const QByteArray &flag, d->mItem.d_func()->mAddedFlags.toList()) {
+ flags += "+" + flag;
+ }
+ Q_FOREACH(const QByteArray &flag, d->mItem.d_func()->mDeletedFlags.toList()) {
+ flags += "-" + flag;
+ }
+ }
+ if (d->mItem.d_func()->mTagsOverwritten || !merge) {
+ Q_FOREACH(const Akonadi::Tag &tag, d->mItem.d_func()->mAddedTags) {
+ flags += "TAG " + tag.remoteId();
+ }
+ } else {
+ Q_FOREACH(const Akonadi::Tag &tag, d->mItem.d_func()->mAddedTags) {
+ flags += "+TAG " + tag.remoteId();
+ }
+ Q_FOREACH(const Akonadi::Tag &tag, d->mItem.d_func()->mDeletedTags) {
+ flags += "-TAG " + tag.remoteId();
+ }
+ }
QByteArray command = d->newTag();
if (merge) {
- command += " MERGE (GID";
- if (!d->mItem.remoteId().isEmpty()) {
- command += " REMOTEID";
- }
- command += ") ";
+ QList<QByteArray> mergeArgs;
+ if (mergeByGid) {
+ mergeArgs << "GID";
+ }
+ if (mergeByRid) {
+ mergeArgs << "REMOTEID";
+ }
+ if (mergeSilent) {
+ mergeArgs << "SILENT";
+ }
+ command += " MERGE (" + ImapParser::join(mergeArgs, " ") + ") ";
} else {
- command += " X-AKAPPEND ";
+ command += " X-AKAPPEND ";
}
command += QByteArray::number(d->mCollection.id())
@@ -164,6 +197,7 @@ void ItemCreateJob::doHandleResponse(const QByteArray &tag, const QByteArray &da
// Error, maybe?
return;
}
+ d->mItemReceived = true;
d->mItem = item;
}
return;
@@ -189,17 +223,21 @@ void ItemCreateJob::doHandleResponse(const QByteArray &tag, const QByteArray &da
}
}
-void ItemCreateJob::setMergeIfExists(bool merge)
+void ItemCreateJob::setMerge(ItemCreateJob::MergeOptions options)
{
- Q_D(ItemCreateJob);
+ Q_D(ItemCreateJob);
- d->mMerge = merge;
+ d->mMergeOptions = options;
}
Item ItemCreateJob::item() const
{
Q_D(const ItemCreateJob);
+ if (d->mItemReceived) {
+ return d->mItem;
+ }
+
if (d->mUid == 0) {
return Item();
}
diff --git a/akonadi/itemcreatejob.h b/akonadi/itemcreatejob.h
index b55ae3a..f4da1f4 100644
--- a/akonadi/itemcreatejob.h
+++ b/akonadi/itemcreatejob.h
@@ -95,23 +95,33 @@ public:
*/
Item item() const;
+ enum MergeOption {
+ NoMerge = 0, ///< Don't merge
+ RID = 1, ///< Merge by remote id
+ GID = 2, ///< Merge by GID
+ Silent = 4 ///< Only return the id of the merged/created item.
+ };
+ Q_DECLARE_FLAGS(MergeOptions, MergeOption)
+
/**
- * Merge this item into an existing one
+ * Merge this item into an existing one if available.
+ *
+ * If an item with same GID and/or remote ID as the created item exists in
+ * specified collection (depending on the provided options), the new item will
+ * be merged into the existing one and the merged item will be returned
+ * (unless the Silent option is used).
*
- * If an item with same GID and remote ID as the created item exists in
- * specified collection, the new item will be merged into the existing one
- * and the merged item will be returned.
+ * If no matching item is found a new item is created.
*
- * If the new item does not have remote ID specified, only GID-based merging
- * will be performed. If the item does not have GID, this option will be
+ * If the item does not have a GID or RID, this option will be
* ignored and a new item will be created.
*
* By default, merging is disabled.
*
- * @param merge Whether to enable or disable merging
+ * @param options Merge options.
* @since 4.14
*/
- void setMergeIfExists(bool merge);
+ void setMerge(MergeOptions options);
protected:
virtual void doStart();
@@ -121,6 +131,8 @@ private:
Q_DECLARE_PRIVATE(ItemCreateJob)
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(ItemCreateJob::MergeOptions)
+
}
#endif
diff --git a/akonadi/tests/itemappendtest.cpp b/akonadi/tests/itemappendtest.cpp
index 354f272..873d1a0 100644
--- a/akonadi/tests/itemappendtest.cpp
+++ b/akonadi/tests/itemappendtest.cpp
@@ -271,27 +271,53 @@ void ItemAppendTest::testItemMerge_data()
QTest::addColumn<Akonadi::Item>( "item1" );
QTest::addColumn<Akonadi::Item>( "item2" );
QTest::addColumn<Akonadi::Item>( "mergedItem" );
-
- Item i1( "application/octet-stream" );
- i1.setPayload( QByteArray( "ABCD" ) );
- i1.setSize( 4 );
- i1.setRemoteId( "XYZ" );
- i1.setGid( "XYZ" );
- i1.setFlag( "TestFlag1" );
- i1.setRemoteRevision( "5" );
-
- Item i2( "application/octet-stream" );
- i2.setPayload( QByteArray( "DEFGH" ) );
- i2.setSize( 5 );
- i2.setRemoteId(( "XYZ" ) );
- i2.setGid( "XYZ" );
- i2.setFlag( "TestFlag2" );
- i2.setRemoteRevision( "6" );
-
- Item mergedItem( i2 );
- mergedItem.setFlag( "TestFlag1" );
-
- QTest::newRow( "ok merge" ) << i1 << i2 << mergedItem;
+ QTest::addColumn<bool>( "silent" );
+
+ {
+ Item i1( "application/octet-stream" );
+ i1.setPayload( QByteArray( "ABCD" ) );
+ i1.setSize( 4 );
+ i1.setRemoteId( "XYZ" );
+ i1.setGid( "XYZ" );
+ i1.setFlag( "TestFlag1" );
+ i1.setRemoteRevision( "5" );
+
+ Item i2( "application/octet-stream" );
+ i2.setPayload( QByteArray( "DEFGH" ) );
+ i2.setSize( 5 );
+ i2.setRemoteId(( "XYZ" ) );
+ i2.setGid( "XYZ" );
+ i2.setFlag( "TestFlag2" );
+ i2.setRemoteRevision( "6" );
+
+ Item mergedItem( i2 );
+ mergedItem.setFlag( "TestFlag1" );
+
+ QTest::newRow( "merge" ) << i1 << i2 << mergedItem << false;
+ QTest::newRow( "merge (silent)" ) << i1 << i2 << mergedItem << true;
+ }
+ {
+ Item i1( "application/octet-stream" );
+ i1.setPayload( QByteArray( "ABCD" ) );
+ i1.setSize( 4 );
+ i1.setRemoteId( "RID2" );
+ i1.setGid( "GID2" );
+ i1.setFlag( "TestFlag1" );
+ i1.setRemoteRevision( "5" );
+
+ Item i2( "application/octet-stream" );
+ i2.setRemoteId(( "RID2" ) );
+ i2.setGid( "GID2" );
+ i2.setFlags( Item::Flags() << "TestFlag2" );
+ i2.setRemoteRevision( "6" );
+
+ Item mergedItem( i1 );
+ mergedItem.setFlags( i2.flags() );
+ mergedItem.setRemoteRevision( i2.remoteRevision() );
+
+ QTest::newRow( "overwrite flags, and don't remove existing payload" ) << i1 << i2 << mergedItem << false;
+ QTest::newRow( "overwrite flags, and don't remove existing payload (silent)" ) << i1 << i2 << mergedItem << true;
+ }
}
void ItemAppendTest::testItemMerge()
@@ -299,22 +325,38 @@ void ItemAppendTest::testItemMerge()
QFETCH( Akonadi::Item, item1 );
QFETCH( Akonadi::Item, item2 );
QFETCH( Akonadi::Item, mergedItem );
+ QFETCH( bool, silent );
const Collection col( collectionIdFromPath( "res2/space folder" ) );
QVERIFY( col.isValid() );
ItemCreateJob *create = new ItemCreateJob( item1, col, this );
AKVERIFYEXEC( create );
+ const Item createdItem = create->item();
ItemCreateJob *merge = new ItemCreateJob( item2, col, this );
- merge->setMergeIfExists( true );
+ ItemCreateJob::MergeOptions options = ItemCreateJob::GID | ItemCreateJob::RID;
+ if ( silent ) {
+ options |= ItemCreateJob::Silent;
+ }
+ merge->setMerge( options );
AKVERIFYEXEC( merge );
- QCOMPARE( mergedItem.gid(), merge->item().gid() );
- QCOMPARE( mergedItem.remoteId(), merge->item().remoteId() );
- QCOMPARE( mergedItem.remoteRevision(), merge->item().remoteRevision() );
- QCOMPARE( mergedItem.payloadData(), merge->item().payloadData() );
- QCOMPARE( mergedItem.size(), merge->item().size() );
- QCOMPARE( mergedItem.flags(), merge->item().flags() );
+ QCOMPARE( merge->item().id(), createdItem.id() );
+ if ( !silent ) {
+ QCOMPARE( merge->item().gid(), mergedItem.gid() );
+ QCOMPARE( merge->item().remoteId(), mergedItem.remoteId() );
+ QCOMPARE( merge->item().remoteRevision(), mergedItem.remoteRevision() );
+ QCOMPARE( merge->item().payloadData(), mergedItem.payloadData() );
+ QCOMPARE( merge->item().size(), mergedItem.size() );
+ QCOMPARE( merge->item().flags(), mergedItem.flags() );
+ }
+
+ if ( merge->item().id() != createdItem.id() ) {
+ ItemDeleteJob *del = new ItemDeleteJob( merge->item(), this );
+ AKVERIFYEXEC( del );
+ }
+ ItemDeleteJob *del = new ItemDeleteJob( createdItem, this );
+ AKVERIFYEXEC( del );
}
commit 0555bdeeecc4e8ea3b52a757abdad9e60b7db4dd
Author: Patrick Spendrin <ps_ml at gmx.de>
Date: Mon May 5 11:11:28 2014 +0200
fix errors gcc ignores
diff --git a/akonadi/contact/contactgroupsearchjob.cpp b/akonadi/contact/contactgroupsearchjob.cpp
index cf9cb97..4ea7d9f 100644
--- a/akonadi/contact/contactgroupsearchjob.cpp
+++ b/akonadi/contact/contactgroupsearchjob.cpp
@@ -68,7 +68,7 @@ static Akonadi::SearchTerm::Condition matchType( ContactGroupSearchJob::Match ma
case ContactGroupSearchJob::ContainsMatch:
return Akonadi::SearchTerm::CondContains;
}
- return Akonadi::SearchTerm::SearchTerm::CondEqual;
+ return Akonadi::SearchTerm::CondEqual;
}
void ContactGroupSearchJob::setQuery( Criterion criterion, const QString &value, Match match )
diff --git a/akonadi/contact/contactsearchjob.cpp b/akonadi/contact/contactsearchjob.cpp
index ee9b5cd..2ccbdfd 100644
--- a/akonadi/contact/contactsearchjob.cpp
+++ b/akonadi/contact/contactsearchjob.cpp
@@ -66,7 +66,7 @@ static Akonadi::SearchTerm::Condition matchType( ContactSearchJob::Match match )
case ContactSearchJob::ContainsMatch:
return Akonadi::SearchTerm::CondContains;
}
- return Akonadi::SearchTerm::SearchTerm::CondEqual;
+ return Akonadi::SearchTerm::CondEqual;
}
void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Match match )
diff --git a/akonadi/tageditwidget.cpp b/akonadi/tageditwidget.cpp
index 04efd06..d0e0177 100644
--- a/akonadi/tageditwidget.cpp
+++ b/akonadi/tageditwidget.cpp
@@ -106,7 +106,7 @@ void TagEditWidget::Private::select(const QModelIndex &parent, int start, int en
void TagEditWidget::Private::onRowsInserted(const QModelIndex &parent, int start, int end)
{
- select(parent, start, end, QItemSelectionModel::QItemSelectionModel::Select);
+ select(parent, start, end, QItemSelectionModel::Select);
}
void TagEditWidget::Private::slotCreateTag()
commit 50ffd66ac3401a54e4a0a08fb0102f773fd8b809
Author: Christophe Giboudeaux <cgiboudeaux at gmx.com>
Date: Sun Apr 27 11:12:16 2014 +0200
Update the Greek holidays file
BUG: 312840
FIXED-IN: 4.13.1
diff --git a/kholidays/holidays/plan2/holiday_gr_el b/kholidays/holidays/plan2/holiday_gr_el
index 6b0def1..36c4bb4 100644
--- a/kholidays/holidays/plan2/holiday_gr_el
+++ b/kholidays/holidays/plan2/holiday_gr_el
@@ -4,60 +4,64 @@
:: Language: Greek
::
:: Author: capthookb <praktoreio2002 at yahoo.gr>
+:: Dimitrios Glentadakis <dglent at gmail.com>
+:: Marios Andreopoulos <opensource at andmarios.com>
+:: Updated: 2014-04-25
::
-:: Updated:
-::
-:: Source:
-::
+:: Source: http://www.eortologio.gr/index_uk.php
+:: http://www.argies.gr/
:: Metadata
country "GR"
language "el"
-:name "optional - defaults to country name"
-:description "(please add description in source language) National holiday file for Greece"
+: "National holiday file for Greece"
+:description "ÎοÏÏÎÏ ÎºÎ±Î¹ αÏÎ³Î¯ÎµÏ ÏÏην Îλλάδα"
-:: Public Holidays
-"Î ÏÏÏοÏÏονιά (ÎÏγία)" weekend on january 1
-"Î ÏÏÏομαγιά (ÎÏγία)" weekend on may 1
-"ÎÏÎÏÎµÎ¹Î¿Ï ÏοÏ
ÎΧΠ(ÎÏγία)" weekend on october 28
-"ÎÏÎÏÎµÎ¹Î¿Ï ÏÎ·Ï ÎµÏανάÏÏαÏÎ·Ï ÏοÏ
1821 (ÎÏγία)" weekend on march 25
-"Îεγάλη ΠαÏαÏκεÏ
ή (ÎÏγία)" weekend on pascha minus 2 days
+:: Public Holidays - ÎημÏÏÎ¹ÎµÏ Î±ÏγίεÏ
+"Î ÏÏÏοÏÏονιά" weekend on january 1
+"Îγια ÎεοÏάνεια" weekend on january 6
+"Î ÏÏÏομαγιά" weekend on may 1
+"ÎÏÎÏÎµÎ¹Î¿Ï ÏοÏ
ÎΧÎ" weekend on october 28
+"ÎÏÎÏÎµÎ¹Î¿Ï ÏÎ·Ï ÎµÏανάÏÏαÏÎ·Ï ÏοÏ
1821" weekend on march 25
+"ÎαθαÏή ÎεÏ
ÏÎÏα" weekend on pascha minus 48 days
+"Îεγάλη ΠαÏαÏκεÏ
ή" weekend on pascha minus 2 days
"Îεγάλο ΣάββαÏο" weekend on pascha minus 1 days
"Το Îγιον ΠάÏÏα" weekend on pascha
-"ÎεÏ
ÏÎÏα ÏοÏ
ΠάÏÏα (ÎÏγία)" weekend on pascha plus 1 days
-"ÎεÏεμίαÏ" weekend on may 1
-"ÎοίμηÏη ÏÎ·Ï ÎεοÏÏκοÏ
(ÎÏγία)" weekend on august 15
+"ÎεÏ
ÏÎÏα ÏοÏ
ΠάÏÏα" weekend on pascha plus 1 days
+"ÎγίοÏ
ΠνεÏμαÏοÏ" weekend on pascha plus 50 days
+"ÎοίμηÏη ÏÎ·Ï ÎεοÏÏκοÏ
" weekend on august 15
"ÎαÏία, ÎάÏιοÏ, ΠαναγιÏÏηÏ, ΠαναγιÏÏα, ÎÎÏÏοινα, ÎεοÏÏκηÏ" weekend on august 15
-"ΣÏÎ½Î±Î¾Î¹Ï ÎεοÏÏκοÏ
(ÎÏγία)" weekend on december 26
-"ÎανÏληÏ, ÎμμανοÏ
Îλα, Îαβίδ" weekend on december 26
-"ΧÏιÏÏοÏγεννα (ÎÏγία)" weekend on december 25
+"ΧÏιÏÏοÏγεννα" weekend on december 25
+"ΣÏÎ½Î±Î¾Î¹Ï ÎεοÏÏκοÏ
" weekend on december 26
-:: Religious
+:: Religious - ÎÏηÏκεÏ
ÏικÎÏ
:: Financial
:: Cultural
+: Mothers' day,
+"ÎιοÏÏή ÏÎ·Ï Î¼Î·ÏÎÏαÏ" on second sunday in may
+: Fathers' day
+"ÎιοÏÏή ÏοÏ
ÏαÏÎÏα" on third sunday in june
-:: School
+:: School - ΣÏολικÎÏ ÎÏγίεÏ
+"ÎξÎγεÏÏη ÏοÏ
ΠολÏ
ÏεÏνείοÏ
(ΣÏολική ÎÏγία)" on november 17
:: Daylight Saving
-"Îλλαγή ÏÏÎ±Ï (1 ÏÏα μÏÏοÏÏά) " on last sunday in march
-"Îλλαγή ÏÏÎ±Ï (1 ÏÏα ÏίÏÏ)" on last sunday in october
+: summer
+"ÎεÏινή ÏÏα (1 ÏÏα μÏÏοÏÏά)" on last sunday in march
+:winter
+"ΧειμεÏινή ÏÏα (1 ÏÏα ÏίÏÏ)" on last sunday in october
:: Seasons
-:: Name Days
-
-
-
:: To be sorted, it's all Greek to me :-)
-:: ÎνομαÏÏικÎÏ ÎµÎ¿ÏÏÎÏ
+:: Name Days - ÎνομαÏÏικÎÏ ÎµÎ¿ÏÏÎÏ
"ÎαÏίληÏ, ÎαÏιλική, ÎαÏιλεία, Îίβιαν" on january 1
"ΣÏ
λβÎÏÏÏοÏ" on january 2
"ÎεÏνη" on january 5
-"Îγια ÎεοÏάνεια (ÎÏγία)" on january 6
"ΦÏÏηÏ, ΦÏÏεινή, ÎεοÏάνηÏ, ÎεοÏανία, Φανή, ÎοÏδάνηÏ, ÎεανÏ, ÎÏ
Ïανία, ÎεοÏοÏλα" on january 6
"ÎÏάννηÏ, ÎÏάννα, Î ÏÏδÏομοÏ" on january 7
"ÎγάθÏν, Îομινίκη, ΠαÏθÎνα, ÎÏÏοÏ" on january 8
@@ -89,11 +93,12 @@ language "el"
"ÎλάÏηÏ, ÎλαÏία, ÎοδÏÏήÏ, ÎοδÏÏα, ÎÏ
γή " on february 11
"ÎελÎÏηÏ, ΠλÏÏίνοÏ" on february 12
"Î ÏίÏκιλλα" on february 13
-"ÎαλενÏίνοÏ, ÎαλενÏίνη, ΠαγκÏÏμια ÎμÎÏα ÎÏÏÏεÏ
μÎνÏν" on february 14
+"ΧλÏη" on sunday after ([february 13])
+"ÎαλενÏίνοÏ, ÎαλενÏίνη, ημÎÏα ÏÏν εÏÏÏεÏ
μÎνÏν" on february 14
"ÎÏ
ÏÎβιοÏ, ÎÏ
Ïεβία" on february 15
"ΠάμÏιλοÏ, ΠαμÏίλη, ΣÎλεÏ
κοÏ, ΣελεÏκη" on february 16
"ÎÎÏν, ÎγαÏηÏÏÏ" on february 18
-"ΦιλοθÎη, ΧλÏη" on february 19
+"ΦιλοθÎη" on february 19
"ÎνθοÏÏα" on february 22
"ΠολÏκαÏÏοÏ" on february 23
"ΠοÏÏÏÏηÏ, ΦÏÏεινή" on february 26
@@ -128,14 +133,16 @@ language "el"
"ÎεÏνίδαÏ, ÎάζαÏοÏ" on april 15
"ÎάÏοÏ, ÎάÏα, ÎάÏνη, Îαλήνη, Χιονία" on april 16
"Îαθαναήλ, ÎÎαÏÏοÏ, ÎιάÏÏοÏ" on april 22
-"ÎναÏÏάÏιοÏ, ÎναÏÏαÏία, ÎάμÏÏοÏ, ÎαμÏÏινή, ΠαÏÏÎ¬Î»Î·Ï " on april 23
-"ÎεÏÏγιοÏ, ÎεÏÏγία, ÎλιÏάβεÏ, ÎÏιλλÎαÏ" on april 24
-"ÎάÏκοÏ, Îίκη, ΡαÏαήλ" on april 25
+"ÎεÏÏγιοÏ, ÎεÏÏγία" on ( ( ([april 23]) >= ([pascha]) ) ? ([april 23]) : ([pascha] + 1) )
+"ÎλιÏάβεÏ, ÎÏιλλÎαÏ" on april 24
+"ÎάÏκοÏ, Îίκη, ΡαÏαήλ" on ( ( ([april 23]) >= ([pascha]) ) ? ([april 25]) : ([pascha] + 2) )
+"Îίκη" on april 25
"ÎÏή, Πηγή" on april 28
"ÎάÏÏναÏ" on april 29
"ÎάκÏβοÏ, ÎÏμάÏ, ÎÏμαή, ÎÏημίνα" on april 30
+"ÎεÏεμίαÏ" on may 1
"ÎÏÏεÏοÏ, ÎÏÏÎÏια" on may 2
-"ΡοδÏÏη" on may 3
+"ΡοδÏÏη, Îενία" on may 3
"ÎιÏήνη, EιÏηναίοÏ, ÎÏ
ÏÏαίμ" on may 5
"ÎÏ
ÏοÏÏÏα" on may 7
"ÎεολÏγοÏ" on may 8
@@ -162,7 +169,7 @@ language "el"
"ÎαλλιÏÏη" on june 8
"Ροδάνθη" on june 9
"ÎαÏθολομαίοÏ, ÎαÏνάβαÏ" on june 11
-"ÎνοÏÏÏιοÏ, ÎήνÏν,ÎοÏίνα, ÎγίοÏ
ΠνεÏμαÏοÏ" on june 12
+"ÎνοÏÏÏιοÏ" on june 12
"ÎλιÏαίοÏ" on june 14
"ÎÏ
γοÏ
ÏÏίνοÏ, ÎÏ
γοÏÏÏα, ÎεÏÏνÏ
μοÏ, ÎÏνικα, ÎÏÏανÏία" on june 15
"ÎιοÏÏή ÏοÏ
ÏαÏÎÏα" on june 16
@@ -286,10 +293,10 @@ language "el"
"ÎεÏκληÏοÏ, ÎακÏβ" on december 1
"ÎαÏβάÏα, ÎαμαÏκηνÏÏ" on december 4
"ΣάββαÏ, ΣαββοÏλα, ÎιογÎνηÏ" on december 5
-"ÎίκοÏ, ÎικολÎÏÏα" on december 6
+"ÎίκοÏ, ÎικολÎÏα" on december 6
"ÎμβÏÏÏιοÏ" on december 7
"Îννα" on december 9
-"ÎαÏÏν, Îδάμ, ÎαÏ
ίδ, Îανάη, ÎÏα, ÎÏαάκ, ÎÏβ, ΡαÏήλ, ΡοÏ
μÏίνη" on december 11
+"ÎαÏÏν, Îδάμ, ÎαÏ
ίδ, Îανάη, ÎÏα, ÎÏαάκ, ÎÏβ, ΡαÏήλ, ΡοÏ
μÏίνη" on sunday after ([december 11])
"ΣÏÏÏοÏ, ΣÏÏ
ÏιδοÏλα" on december 12
"ÎÏ
ÏÏÏάÏιοÏ, ÎοÏ
κάÏ, ÎοÏ
κία, ÎÏηÏ" on december 13
"ÎλεÏ
θÎÏιοÏ, ÎλεÏ
θεÏία, Îνθή, ΣÏλβια" on december 15
@@ -301,21 +308,18 @@ language "el"
"ÎναÏÏαÏία" on december 22
"ÎÏ
γÎνιοÏ, ÎÏ
γενία" on december 24
"ΧÏήÏÏοÏ, ΧÏιÏÏίνα, ΧÏÏÏα" on december 25
+"ÎανÏληÏ, ÎμμανοÏ
Îλα" on december 26
"ΣÏÎÏανοÏ, ΣÏεÏανία" on december 27
"ÎÏμνα" on december 28
"ÎÏÏήÏ" on december 30
-:: ÎÏÎ³Î¯ÎµÏ - ÎÏÎÏειοι
-"ÎξÎγεÏÏη ÏοÏ
ΠολÏ
ÏεÏνείοÏ
(ΣÏολική ÎÏγία)" on november 17
-
:: ÎινηÏÎÏ ÎµÎ¿ÏÏÎÏ
"ΤελÏνοÏ
και ΦαÏιÏαίοÏ
- ÎÏÏή ΤÏιÏδίοÏ
" on pascha minus 70 days
"ΤοÏ
ÎÏÏÏοÏ
" on pascha minus 63 days
"ΤÏικνοÏÎμÏÏη" on pascha minus 59 days
"ÎÏ
Ïιακή ÏÏν ÎÏÏκÏεÏ" on pascha minus 56 days
"ΤÏ
ÏοÏάγοÏ
" on pascha minus 49 days
-"ÎαθαÏή ÎεÏ
ÏÎÏα (ÎÏγία)" on pascha minus 48 days
"ÎεÏδÏÏοÏ, ÎεοδÏÏα, ÎÏÏα, ÎÏδηÏ, ÎÏδοÏ, ÎÏÏηÏ" on pascha minus 43 days
"ÎÏ
Ïιακή ÏÎ·Ï ÎÏθοδοξίαÏ" on pascha minus 42 days
"ΣάββαÏο ÏοÏ
ÎαζάÏοÏ
" on pascha minus 8 days
@@ -324,9 +328,10 @@ language "el"
"Îεγάλη ΤÏίÏη" on pascha minus 5 days
"Îεγάλη ΤεÏάÏÏη" on pascha minus 4 days
"Îεγάλη Î ÎμÏÏη" on pascha minus 3 days
+"ÎναÏÏάÏιοÏ, ÎναÏÏαÏία, ÎάμÏÏοÏ, ÎαμÏÏινή, ΠαÏÏÎ¬Î»Î·Ï " on pascha
+"ΡαÏαήλ, ÎιÏήνη" on pascha plus 2 days
"Πηγή, ÎήÏηÏ, ÎηÏοÏλα, ÎήÏιμοÏ, ÎÏή, ÎÏηÏ" on pascha plus 5 days
"ΤοÏ
ÎÏμά" on pascha plus 7 days
"ÎνάληÏη ÏοÏ
ΧÏιÏÏοÏ" on pascha plus 39 days
"ΠενÏηκοÏÏή" on pascha plus 49 days
-"Îγ. ΠνεÏμαÏοÏ" on pascha plus 50 days
"Îγ. ΠάνÏÏν" on pascha plus 56 days
commit 45d9dc55a5a4363025a5302d54054b869ae0ff77
Author: Dan Vrátil <dvratil at redhat.com>
Date: Fri May 2 16:42:50 2014 +0200
Update itemappendtest to reflect changes in Akonadi
Akonadi can now tell the difference between empty payload and
no payload, so once Item::setPayload() is called, the item is
considered to have a payload, even if it's empty.
diff --git a/akonadi/tests/itemappendtest.cpp b/akonadi/tests/itemappendtest.cpp
index f8d3e51..354f272 100644
--- a/akonadi/tests/itemappendtest.cpp
+++ b/akonadi/tests/itemappendtest.cpp
@@ -122,7 +122,9 @@ void ItemAppendTest::testContent()
Item item;
item.setMimeType( "application/octet-stream" );
- item.setPayload( data );
+ if ( !data.isNull() ) {
+ item.setPayload( data );
+ }
ItemCreateJob* job = new ItemCreateJob( item, testFolder1, this );
AKVERIFYEXEC( job );
@@ -134,8 +136,7 @@ void ItemAppendTest::testContent()
AKVERIFYEXEC( fjob );
QCOMPARE( fjob->items().count(), 1 );
Item item2 = fjob->items().first();
- //akonadi does not distinguish empty and no payload
- QCOMPARE( item2.hasPayload(), !data.isEmpty() );
+ QCOMPARE( item2.hasPayload(), !data.isNull() );
if( item2.hasPayload() ) {
QCOMPARE( item2.payload<QByteArray>(), data );
}
commit ff60f4729476d181dfc8bd62e3ada66ef43c4457
Author: Dan Vrátil <dvratil at redhat.com>
Date: Tue Apr 29 10:17:12 2014 +0200
ItemDeleteJob: add missing implementation of tag-scope delete
Looks like I added the ItemDeleteJob(Tag&) constructor to header but forgot
to actually implement it.
diff --git a/akonadi/itemdeletejob.cpp b/akonadi/itemdeletejob.cpp
index 6d9f7ea..5eb1ee9 100644
--- a/akonadi/itemdeletejob.cpp
+++ b/akonadi/itemdeletejob.cpp
@@ -70,6 +70,14 @@ ItemDeleteJob::ItemDeleteJob(const Collection &collection, QObject *parent)
d->mCollection = collection;
}
+ItemDeleteJob::ItemDeleteJob(const Tag &tag, QObject *parent)
+ : Job(new ItemDeleteJobPrivate(this), parent)
+{
+ Q_D(ItemDeleteJob);
+
+ d->mTag = tag;
+}
+
ItemDeleteJob::~ItemDeleteJob()
{
}
diff --git a/akonadi/tests/itemdeletetest.cpp b/akonadi/tests/itemdeletetest.cpp
index 7b0cefa..b5bcdf3 100644
--- a/akonadi/tests/itemdeletetest.cpp
+++ b/akonadi/tests/itemdeletetest.cpp
@@ -25,6 +25,8 @@
#include <akonadi/itemdeletejob.h>
#include <akonadi/itemfetchjob.h>
#include <akonadi/transactionjobs.h>
+#include <akonadi/tagcreatejob.h>
+#include <akonadi/itemmodifyjob.h>
#include "test_utils.h"
#include <QtCore/QObject>
@@ -118,6 +120,42 @@ class ItemDeleteTest : public QObject
QVERIFY( !fjob->exec() );
}
+ void testTagDelete()
+ {
+ // Create tag
+ Tag tag;
+ tag.setName( QLatin1String( "Tag1" ) );
+ tag.setRemoteId( "Tag1" );
+ tag.setGid( "Tag1" );
+ TagCreateJob *tjob = new TagCreateJob( tag, this );
+ AKVERIFYEXEC( tjob );
+ tag = tjob->tag();
+
+ const Collection col( collectionIdFromPath( "res1/foo" ) );
+ QVERIFY( col.isValid() );
+
+ Item i;
+ i.setRemoteId( "D" );
+
+ ItemFetchJob *fjob = new ItemFetchJob( i, this );
+ fjob->setCollection( col );
+ AKVERIFYEXEC( fjob );
+ QCOMPARE( fjob->items().count(), 1 );
+
+ i = fjob->items().first();
+ i.setTag(tag);
+ ItemModifyJob *mjob = new ItemModifyJob( i, this );
+ AKVERIFYEXEC( mjob );
+
+ // Delete the tagged item
+ ItemDeleteJob *djob = new ItemDeleteJob( tag, this );
+ AKVERIFYEXEC( djob );
+
+ // Try to fetch the item again, there should be none
+ fjob = new ItemFetchJob( i, this );
+ QVERIFY( !fjob->exec() );
+ }
+
void testCollectionDelete()
{
const Collection col( collectionIdFromPath( "res1/foo" ) );
commit 079c6e6b84fd6f6dd5fadbfad955f2a327c9b55e
Author: Dan Vrátil <dvratil at redhat.com>
Date: Thu Apr 24 16:08:58 2014 +0200
Implement support for MERGE command in ItemCreateJob
By setting ItemCreateJob::setMergeIfExists to true, the server job will
use MERGE instead of AK-APPEND command. Server will try to merge the
new item with an existing one (if any such exists) by GID and RID match.
If no such item exists, the server will fallback to AK-APPEND behavior and
a new item is inserted. Otherwise the merged item is returned.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b67f7a..53b3501 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,7 +80,7 @@ endif()
if (NOT KDEPIM_ONLY_KLEO)
#FindAkonadi.cmake is only there for compatibility reasons, but we don't want to use that.
- set(Akonadi_MIN_VERSION "1.12.42")
+ set(Akonadi_MIN_VERSION "1.12.43")
find_package(Akonadi ${Akonadi_MIN_VERSION} QUIET NO_MODULE)
set_package_properties(Akonadi PROPERTIES DESCRIPTION "Akonadi server libraries" URL "http://pim.kde.org/akonadi" TYPE REQUIRED PURPOSE "Access to PIM storage and services")
diff --git a/akonadi/itemcreatejob.cpp b/akonadi/itemcreatejob.cpp
index b0b81c6..10b978c 100644
--- a/akonadi/itemcreatejob.cpp
+++ b/akonadi/itemcreatejob.cpp
@@ -39,6 +39,7 @@ class Akonadi::ItemCreateJobPrivate : public JobPrivate
public:
ItemCreateJobPrivate(ItemCreateJob *parent)
: JobPrivate(parent)
+ , mMerge(false)
{
}
@@ -50,6 +51,7 @@ public:
Item::Id mUid;
QDateTime mDatetime;
QByteArray mPendingData;
+ bool mMerge;
};
QByteArray ItemCreateJobPrivate::nextPartHeader()
@@ -104,6 +106,7 @@ void ItemCreateJob::doStart()
QList<QByteArray> flags;
flags.append("\\MimeType[" + d->mItem.mimeType().toLatin1() + ']');
const QString gid = GidExtractor::getGid(d->mItem);
+ const bool merge = d->mMerge && !d->mItem.gid().isNull();
if (!gid.isNull()) {
flags.append(ImapParser::quote("\\Gid[" + gid.toUtf8() + ']'));
}
@@ -115,10 +118,21 @@ void ItemCreateJob::doStart()
}
flags += d->mItem.flags().toList();
- QByteArray command = d->newTag() + " X-AKAPPEND " + QByteArray::number(d->mCollection.id())
- + ' ' + QByteArray::number(d->mItem.size())
- + " (" + ImapParser::join(flags, " ") + ")"
- + " ("; // list of parts
+ QByteArray command = d->newTag();
+ if (merge) {
+ command += " MERGE (GID";
+ if (!d->mItem.remoteId().isEmpty()) {
+ command += " REMOTEID";
+ }
+ command += ") ";
+ } else {
+ command += " X-AKAPPEND ";
+ }
+
+ command += QByteArray::number(d->mCollection.id())
+ + ' ' + QByteArray::number(d->mItem.size())
+ + " (" + ImapParser::join(flags, " ") + ")"
+ + " ("; // list of parts
const QByteArray attrs = ProtocolHelper::attributesToByteArray(d->mItem, true);
if (!attrs.isEmpty()) {
command += attrs;
@@ -138,6 +152,22 @@ void ItemCreateJob::doHandleResponse(const QByteArray &tag, const QByteArray &da
d->writeData(d->nextPartHeader());
return;
}
+ if (tag == "*") {
+ int begin = data.indexOf("FETCH");
+ if (begin >= 0) {
+ QList<QByteArray> fetchResponse;
+ ImapParser::parseParenthesizedList(data, fetchResponse, begin + 6);
+
+ Item item;
+ ProtocolHelper::parseItemFetchResult(fetchResponse, item);
+ if (!item.isValid()) {
+ // Error, maybe?
+ return;
+ }
+ d->mItem = item;
+ }
+ return;
+ }
if (tag == d->tag()) {
int uidNextPos = data.indexOf("UIDNEXT");
if (uidNextPos != -1) {
@@ -159,6 +189,13 @@ void ItemCreateJob::doHandleResponse(const QByteArray &tag, const QByteArray &da
}
}
+void ItemCreateJob::setMergeIfExists(bool merge)
+{
+ Q_D(ItemCreateJob);
+
+ d->mMerge = merge;
+}
+
Item ItemCreateJob::item() const
{
Q_D(const ItemCreateJob);
diff --git a/akonadi/itemcreatejob.h b/akonadi/itemcreatejob.h
index 82e1814..b55ae3a 100644
--- a/akonadi/itemcreatejob.h
+++ b/akonadi/itemcreatejob.h
@@ -95,6 +95,24 @@ public:
*/
Item item() const;
+ /**
+ * Merge this item into an existing one
+ *
+ * If an item with same GID and remote ID as the created item exists in
+ * specified collection, the new item will be merged into the existing one
+ * and the merged item will be returned.
+ *
+ * If the new item does not have remote ID specified, only GID-based merging
+ * will be performed. If the item does not have GID, this option will be
+ * ignored and a new item will be created.
+ *
+ * By default, merging is disabled.
+ *
+ * @param merge Whether to enable or disable merging
+ * @since 4.14
+ */
+ void setMergeIfExists(bool merge);
+
protected:
virtual void doStart();
virtual void doHandleResponse(const QByteArray &tag, const QByteArray &data);
diff --git a/akonadi/session_p.h b/akonadi/session_p.h
index 6f418a1..2f034ef 100644
--- a/akonadi/session_p.h
+++ b/akonadi/session_p.h
@@ -116,7 +116,7 @@ public:
static int minimumProtocolVersion()
{
- return 37;
+ return 38;
}
/**
diff --git a/akonadi/tests/itemappendtest.cpp b/akonadi/tests/itemappendtest.cpp
index 3adcf0e..f8d3e51 100644
--- a/akonadi/tests/itemappendtest.cpp
+++ b/akonadi/tests/itemappendtest.cpp
@@ -265,3 +265,55 @@ void ItemAppendTest::testItemSize()
QCOMPARE( fetch->items().first().size(), size );
}
+void ItemAppendTest::testItemMerge_data()
+{
+ QTest::addColumn<Akonadi::Item>( "item1" );
+ QTest::addColumn<Akonadi::Item>( "item2" );
+ QTest::addColumn<Akonadi::Item>( "mergedItem" );
+
+ Item i1( "application/octet-stream" );
+ i1.setPayload( QByteArray( "ABCD" ) );
+ i1.setSize( 4 );
+ i1.setRemoteId( "XYZ" );
+ i1.setGid( "XYZ" );
+ i1.setFlag( "TestFlag1" );
+ i1.setRemoteRevision( "5" );
+
+ Item i2( "application/octet-stream" );
+ i2.setPayload( QByteArray( "DEFGH" ) );
+ i2.setSize( 5 );
+ i2.setRemoteId(( "XYZ" ) );
+ i2.setGid( "XYZ" );
+ i2.setFlag( "TestFlag2" );
+ i2.setRemoteRevision( "6" );
+
+ Item mergedItem( i2 );
+ mergedItem.setFlag( "TestFlag1" );
+
+ QTest::newRow( "ok merge" ) << i1 << i2 << mergedItem;
+}
+
+void ItemAppendTest::testItemMerge()
+{
+ QFETCH( Akonadi::Item, item1 );
+ QFETCH( Akonadi::Item, item2 );
+ QFETCH( Akonadi::Item, mergedItem );
+
+ const Collection col( collectionIdFromPath( "res2/space folder" ) );
+ QVERIFY( col.isValid() );
+
+ ItemCreateJob *create = new ItemCreateJob( item1, col, this );
+ AKVERIFYEXEC( create );
+
+ ItemCreateJob *merge = new ItemCreateJob( item2, col, this );
+ merge->setMergeIfExists( true );
+ AKVERIFYEXEC( merge );
+
+ QCOMPARE( mergedItem.gid(), merge->item().gid() );
+ QCOMPARE( mergedItem.remoteId(), merge->item().remoteId() );
+ QCOMPARE( mergedItem.remoteRevision(), merge->item().remoteRevision() );
+ QCOMPARE( mergedItem.payloadData(), merge->item().payloadData() );
+ QCOMPARE( mergedItem.size(), merge->item().size() );
+ QCOMPARE( mergedItem.flags(), merge->item().flags() );
+}
+
diff --git a/akonadi/tests/itemappendtest.h b/akonadi/tests/itemappendtest.h
index e536265..c2a204a 100644
--- a/akonadi/tests/itemappendtest.h
+++ b/akonadi/tests/itemappendtest.h
@@ -37,6 +37,8 @@ class ItemAppendTest : public QObject
void testInvalidMultipartAppend();
void testItemSize_data();
void testItemSize();
+ void testItemMerge_data();
+ void testItemMerge();
};
#endif
More information about the commits
mailing list