diff --git a/src/akonadi/akonadiserializer.cpp b/src/akonadi/akonadiserializer.cpp index 599f92f7..a183ec25 100644 --- a/src/akonadi/akonadiserializer.cpp +++ b/src/akonadi/akonadiserializer.cpp @@ -1,1076 +1,1078 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 RĂ©mi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akonadiserializer.h" #include #include #include #include #include #include #include #include "akonadi/akonadiapplicationselectedattribute.h" #include "akonadi/akonaditimestampattribute.h" #include #include #include using namespace Akonadi; Serializer::Serializer() { } Serializer::~Serializer() { } bool Serializer::representsCollection(SerializerInterface::QObjectPtr object, Collection collection) { return object->property("collectionId").toLongLong() == collection.id(); } bool Serializer::representsItem(QObjectPtr object, Item item) { return object->property("itemId").toLongLong() == item.id(); } bool Serializer::representsAkonadiTag(Domain::Tag::Ptr tag, Tag akonadiTag) const { return tag->property("tagId").toLongLong() == akonadiTag.id(); } QString Serializer::objectUid(SerializerInterface::QObjectPtr object) { return object->property("todoUid").toString(); } Domain::DataSource::Ptr Serializer::createDataSourceFromCollection(Collection collection, DataSourceNameScheme naming) { if (!collection.isValid()) return Domain::DataSource::Ptr(); auto dataSource = Domain::DataSource::Ptr::create(); updateDataSourceFromCollection(dataSource, collection, naming); return dataSource; } void Serializer::updateDataSourceFromCollection(Domain::DataSource::Ptr dataSource, Collection collection, DataSourceNameScheme naming) { if (!collection.isValid()) return; QString name = collection.displayName(); if (naming == FullPath) { auto parent = collection.parentCollection(); while (parent.isValid() && parent != Akonadi::Collection::root()) { name = parent.displayName() + "/" + name; parent = parent.parentCollection(); } } dataSource->setName(name); const auto mimeTypes = collection.contentMimeTypes(); auto types = Domain::DataSource::ContentTypes(); if (mimeTypes.contains(NoteUtils::noteMimeType())) types |= Domain::DataSource::Notes; if (mimeTypes.contains(KCalCore::Todo::todoMimeType())) types |= Domain::DataSource::Tasks; dataSource->setContentTypes(types); if (collection.hasAttribute()) { auto iconName = collection.attribute()->iconName(); dataSource->setIconName(iconName); } if (!collection.hasAttribute()) { dataSource->setSelected(true); } else { auto isSelected = collection.attribute()->isSelected(); dataSource->setSelected(isSelected); } if (collection.enabled()) dataSource->setListStatus(Domain::DataSource::Bookmarked); else if (collection.referenced()) dataSource->setListStatus(Domain::DataSource::Listed); else dataSource->setListStatus(Domain::DataSource::Unlisted); dataSource->setProperty("collectionId", collection.id()); dataSource->setProperty("collection", QVariant::fromValue(collection)); dataSource->setPerson(isPersonCollection(collection)); if (isPersonCollection(collection)) { dataSource->setIconName("meeting-participant"); } } Collection Serializer::createCollectionFromDataSource(Domain::DataSource::Ptr dataSource) { if (!dataSource) { return Akonadi::Collection(); } auto collection = dataSource->property("collection").value(); collection.attribute(Akonadi::Collection::AddIfMissing); auto selectedAttribute = collection.attribute(Akonadi::Collection::AddIfMissing); selectedAttribute->setSelected(dataSource->isSelected()); switch (dataSource->listStatus()) { case Domain::DataSource::Unlisted: collection.setReferenced(false); collection.setEnabled(false); break; case Domain::DataSource::Listed: collection.setReferenced(true); collection.setEnabled(false); break; case Domain::DataSource::Bookmarked: collection.setReferenced(false); collection.setEnabled(true); break; default: qFatal("Shouldn't happen"); break; } return collection; } bool Serializer::isListedCollection(Collection collection) { return collection.enabled() || collection.referenced(); } bool Serializer::isSelectedCollection(Collection collection) { if (!isListedCollection(collection)) return false; if (!isNoteCollection(collection) && !isTaskCollection(collection)) return false; if (!collection.hasAttribute()) return true; return collection.attribute()->isSelected(); } bool Akonadi::Serializer::isNoteCollection(Akonadi::Collection collection) { return collection.contentMimeTypes().contains(NoteUtils::noteMimeType()); } bool Akonadi::Serializer::isTaskCollection(Akonadi::Collection collection) { return collection.contentMimeTypes().contains(KCalCore::Todo::todoMimeType()); } bool Akonadi::Serializer::isPersonCollection(Akonadi::Collection collection) { auto attr = collection.attribute(); return (attr && attr->collectionNamespace() == "usertoplevel"); } bool Serializer::isTaskItem(Item item) { if (!item.hasPayload()) return false; + return true; - auto todo = item.payload(); - return todo->customProperty("Zanshin", "Project").isEmpty(); + //Projects are disabled + // auto todo = item.payload(); + // return todo->customProperty("Zanshin", "Project").isEmpty(); } Domain::Task::Ptr Serializer::createTaskFromItem(Item item) { if (!isTaskItem(item)) return Domain::Task::Ptr(); auto task = Domain::Task::Ptr::create(); updateTaskFromItem(task, item); return task; } static Domain::Task::Status fromKCalStatus(KCalCore::Incidence::Status status) { switch (status) { case KCalCore::Incidence::StatusNone: return Domain::Task::None; case KCalCore::Incidence::StatusInProcess: return Domain::Task::InProcess; case KCalCore::Incidence::StatusCanceled: return Domain::Task::Cancelled; case KCalCore::Incidence::StatusCompleted: return Domain::Task::Complete; case KCalCore::Incidence::StatusNeedsAction: return Domain::Task::NeedsAction; default: break; } return Domain::Task::None; } static KCalCore::Incidence::Status toKCalStatus(Domain::Task::Status status) { switch (status) { case Domain::Task::None: return KCalCore::Incidence::StatusNone; case Domain::Task::InProcess: return KCalCore::Incidence::StatusInProcess; case Domain::Task::Cancelled: return KCalCore::Incidence::StatusCanceled; case Domain::Task::Complete: case Domain::Task::FullComplete: return KCalCore::Incidence::StatusCompleted; case Domain::Task::NeedsAction: return KCalCore::Incidence::StatusNeedsAction; default: break; } return KCalCore::Incidence::StatusNone; } int toWeekDay(Domain::Recurrence::Weekday wday) { switch (wday) { case Domain::Recurrence::Monday: return 1; case Domain::Recurrence::Tuesday: return 2; case Domain::Recurrence::Wednesday: return 3; case Domain::Recurrence::Thursday: return 4; case Domain::Recurrence::Friday: return 5; case Domain::Recurrence::Saturday: return 6; case Domain::Recurrence::Sunday: return 7; default: qFatal("unhddandled wday"); } return 1; } Domain::Recurrence::Weekday fromWeekDay(int wday) { switch (wday) { case 1: return Domain::Recurrence::Monday; case 2: return Domain::Recurrence::Tuesday; case 3: return Domain::Recurrence::Wednesday; case 4: return Domain::Recurrence::Thursday; case 5: return Domain::Recurrence::Friday; case 6: return Domain::Recurrence::Saturday; case 7: return Domain::Recurrence::Sunday; default: qFatal("unhandled wday"); } return Domain::Recurrence::Monday; } KCalCore::RecurrenceRule::PeriodType toRecurrenceType(Domain::Recurrence::Frequency freq) { switch(freq) { case Domain::Recurrence::Frequency::None: qWarning() << "no recurrence?"; break; case Domain::Recurrence::Frequency::Yearly: return KCalCore::RecurrenceRule::rYearly; case Domain::Recurrence::Frequency::Monthly: return KCalCore::RecurrenceRule::rMonthly; case Domain::Recurrence::Frequency::Weekly: return KCalCore::RecurrenceRule::rWeekly; case Domain::Recurrence::Frequency::Daily: return KCalCore::RecurrenceRule::rDaily; case Domain::Recurrence::Frequency::Hourly: return KCalCore::RecurrenceRule::rHourly; case Domain::Recurrence::Frequency::Minutely: return KCalCore::RecurrenceRule::rMinutely; case Domain::Recurrence::Frequency::Secondly: return KCalCore::RecurrenceRule::rSecondly; default: qFatal("unhandled recurrencetype"); } return KCalCore::RecurrenceRule::rNone; } Domain::Recurrence::Frequency fromRecurrenceType(KCalCore::RecurrenceRule::PeriodType freq) { switch(freq) { case KCalCore::RecurrenceRule::rNone: qWarning() << "no recurrence?"; break; case KCalCore::RecurrenceRule::rYearly: return Domain::Recurrence::Frequency::Yearly; case KCalCore::RecurrenceRule::rMonthly: return Domain::Recurrence::Frequency::Monthly; case KCalCore::RecurrenceRule::rWeekly: return Domain::Recurrence::Frequency::Weekly; case KCalCore::RecurrenceRule::rDaily: return Domain::Recurrence::Frequency::Daily; case KCalCore::RecurrenceRule::rHourly: return Domain::Recurrence::Frequency::Hourly; case KCalCore::RecurrenceRule::rMinutely: return Domain::Recurrence::Frequency::Minutely; case KCalCore::RecurrenceRule::rSecondly: return Domain::Recurrence::Frequency::Secondly; default: qFatal("unhandled recurrenceType"); } return Domain::Recurrence::Frequency::None; } Domain::Recurrence::Ptr fromKCalRecurrence(const KCalCore::Recurrence *rec) { Domain::Recurrence recurrence; recurrence.setAllDay(rec->allDay()); recurrence.setInterval(rec->frequency()); QList rdates; foreach (const KDateTime &dt, rec->rDateTimes()) { rdates.append(dt.dateTime()); } foreach (const QDate &dt, rec->rDates()) { rdates.append(QDateTime(dt)); } recurrence.setRecurrenceDates(rdates); QList exdates; foreach (const KDateTime &dt, rec->exDateTimes()) { exdates.append(dt.dateTime()); } foreach (const QDate &dt, rec->exDates()) { exdates.append(QDateTime(dt)); } recurrence.setExceptionDates(exdates); const KCalCore::RecurrenceRule *defaultRR = rec->defaultRRuleConst(); if (defaultRR) { if (defaultRR->duration() != 0) { //Inidcates if end date is set or not if (defaultRR->duration() > 0) { recurrence.setCount(defaultRR->duration()); } else if (defaultRR->duration() == -1) { //infinite duration recurrence.setCount(-1); } } else { recurrence.setEnd(defaultRR->endDt().dateTime()); } recurrence.setWeekStart(fromWeekDay(defaultRR->weekStart())); recurrence.setFrequency(fromRecurrenceType(defaultRR->recurrenceType())); recurrence.setBysecond(defaultRR->bySeconds()); recurrence.setByminute(defaultRR->byMinutes()); recurrence.setByhour(defaultRR->byHours()); recurrence.setBymonthday(defaultRR->byMonthDays()); recurrence.setByyearday(defaultRR->byYearDays()); recurrence.setByweekno(defaultRR->byWeekNumbers()); recurrence.setBymonth(defaultRR->byMonths()); QList daypos; const auto positions = rec->monthPositions(); if (!positions.isEmpty()) { recurrence.setByDayPosition((Domain::Recurrence::WeekPosition) positions.at(0).pos()); for (int i = positions.size()-1; i > -1; i--) { if (positions.at(i).pos() == recurrence.byDayPosition()) { daypos.append((Domain::Recurrence::Weekday) (Domain::Recurrence::Monday + positions.at(i).day() - 1)); } } } recurrence.setByday(daypos); } return Domain::Recurrence::Ptr(new Domain::Recurrence(recurrence)); } KCalCore::Alarm::Type toKCalAlarmType(Domain::Alarm::Type type) { switch(type) { case Domain::Alarm::Audio: return KCalCore::Alarm::Audio; case Domain::Alarm::Display: return KCalCore::Alarm::Display; default: qFatal("unhandled Alarm tpye"); } return KCalCore::Alarm::Invalid; } Domain::Alarm::Type fromKCalAlarmType(KCalCore::Alarm::Type type) { switch(type) { case KCalCore::Alarm::Audio: return Domain::Alarm::Audio; case KCalCore::Alarm::Display: return Domain::Alarm::Display; default: qFatal("unhandled Alarm tpye"); } return Domain::Alarm::Display; } Domain::Alarm::List fromKCalAlarm(KCalCore::Alarm::List alist) { Domain::Alarm::List alarms; foreach(auto a, alist) { Domain::Alarm::Ptr alarm(new Domain::Alarm()); alarm->setType(fromKCalAlarmType(a->type())); if (a->hasTime()) { alarm->setPosition(Domain::Alarm::Exact); alarm->setDateTime(a->time().dateTime()); } else { if (a->hasStartOffset()) { alarm->setPosition(Domain::Alarm::Start); alarm->setOffset(a->startOffset().asSeconds()); } else { alarm->setPosition(Domain::Alarm::End); alarm->setOffset(a->endOffset().asSeconds()); } } alarms.append(alarm); } return alarms; } void Serializer::updateTaskFromItem(Domain::Task::Ptr task, Item item) { if (!isTaskItem(item)) return; auto todo = item.payload(); task->setTitle(todo->summary()); task->setText(todo->description()); task->setStartDate(todo->dtStart().dateTime()); task->setDueDate(todo->dtDue().dateTime()); task->setProperty("itemId", item.id()); task->setProperty("parentCollectionId", item.parentCollection().id()); task->setProperty("todoUid", todo->uid()); if (todo->relatedTo() == todo->uid()) { qWarning() << "Todo is related to itself: " << todo->uid() << item.id(); } else { task->setProperty("relatedUid", todo->relatedTo()); } task->setProperty("revision", todo->revision()); task->setProgress(todo->percentComplete()); task->setStatus(fromKCalStatus(todo->status())); Domain::Artifact::Attachment::List attachments; for (const auto &attachment : todo->attachments()) { if (attachment->isBinary()) { auto a = Domain::Artifact::Attachment::Ptr::create(); a->data = attachment->data(); a->label = attachment->label(); a->mimetype = attachment->mimeType(); attachments << a; } else { kDebug() << "ignored non-binary attachment"; } } task->setAttachments(attachments); if (todo->attendeeCount() > 0) { const auto attendees = todo->attendees(); const auto delegate = std::find_if(attendees.begin(), attendees.end(), [] (const KCalCore::Attendee::Ptr &attendee) { return attendee->status() == KCalCore::Attendee::Accepted || attendee->status() == KCalCore::Attendee::NeedsAction; }); if (delegate != attendees.end()) { task->setDelegate(Domain::Task::Delegate((*delegate)->name(), (*delegate)->email())); } } if(todo->organizer()) { task->setProperty("organizer", todo->organizer()->fullName()); } if (!todo->alarms().isEmpty()) { task->setAlarms(fromKCalAlarm(todo->alarms())); } else { task->setAlarms(Domain::Alarm::List()); } if (todo->recurs()) { Domain::Recurrence::Ptr recurrence(fromKCalRecurrence(todo->recurrence())); task->setRecurrence(recurrence); if (task->status() == Domain::Task::Complete) { task->setStatus(Domain::Task::FullComplete); } } else { task->setRecurrence(Domain::Recurrence::Ptr(0)); } } bool Serializer::isTaskChild(Domain::Task::Ptr task, Akonadi::Item item) { if (!isTaskItem(item)) return false; auto todo = item.payload(); if (todo->relatedTo() == todo->uid()) { qWarning() << "Task is related to itself: " << todo->uid() << item.id(); return false; } if (todo->relatedTo() == task->property("todoUid")) return true; return false; } void updateKCalRecurrence(const Domain::Recurrence::Ptr &from, KCalCore::Recurrence *recurrence) { KCalCore::RecurrenceRule *defaultRR = recurrence->defaultRRule(true); Q_ASSERT(defaultRR); defaultRR->setWeekStart(toWeekDay(from->weekStart())); defaultRR->setRecurrenceType(toRecurrenceType(from->frequency())); defaultRR->setFrequency(from->interval()); recurrence->setAllDay(from->allDay()); if (from->end().isValid()) { recurrence->setDuration(0); recurrence->setEndDateTime(KDateTime(from->end())); } else { recurrence->setDuration(from->count()); } if (!from->bysecond().empty()) { defaultRR->setBySeconds(from->bysecond()); } if (!from->byminute().empty()) { defaultRR->setByMinutes(from->byminute()); } if (!from->byhour().empty()) { defaultRR->setByHours(from->byhour()); } if (!from->bymonthday().empty()) { defaultRR->setByMonthDays(from->bymonthday()); } if (!from->byyearday().empty()) { defaultRR->setByYearDays(from->byyearday()); } if (!from->byweekno().empty()) { defaultRR->setByWeekNumbers(from->byweekno()); } if (!from->bymonth().empty()) { defaultRR->setByMonths(from->bymonth()); } if (!from->byday().empty()) { QBitArray days(7, 0); foreach(auto day, from->byday()) { days.setBit(day - Domain::Recurrence::Monday); } recurrence->addMonthlyPos(from->byDayPosition(), days); } foreach (const auto &dt, from->recurrenceDates()) { if (dt.time() == QTime(0,0,0)) { recurrence->addRDate(dt.date()); } else { recurrence->addRDateTime(KDateTime(dt)); } } foreach (const auto &dt, from->exceptionDates()) { if (dt.time() == QTime(0,0,0)) { recurrence->addExDate(dt.date()); } else { recurrence->addExDateTime(KDateTime(dt)); } } } Akonadi::Item Serializer::createItemFromTask(Domain::Task::Ptr task) { auto todo = KCalCore::Todo::Ptr::create(); todo->setSummary(task->title()); todo->setDescription(task->text()); todo->setDtStart(KDateTime(task->startDate())); todo->setDtDue(KDateTime(task->dueDate())); todo->setStatus(toKCalStatus(task->status())); if (task->status() != Domain::Task::Complete || task->status() != Domain::Task::FullComplete) { //Because the serializer doesn't serialize the status as it should but instead serializes the isComplete status todo->setCompleted(false); } //We have to set this after setting complete above todo->setPercentComplete(task->progress()); if (task->recurrence()) { todo->setDtRecurrence(KDateTime(task->startDate())); updateKCalRecurrence(task->recurrence(), todo->recurrence()); } if(!task->alarms().isEmpty()) { foreach(auto a, task->alarms()) { KCalCore::Alarm::Ptr alarm(new KCalCore::Alarm(todo.data())); KCalCore::Duration duration(a->offset(), KCalCore::Duration::Seconds); alarm->setEnabled(true); alarm->setType(toKCalAlarmType(a->type())); switch (a->position()) { case Domain::Alarm::Exact: alarm->setTime(KDateTime(a->datetime())); break; case Domain::Alarm::Start: alarm->setStartOffset(duration); break; case Domain::Alarm::End: alarm->setEndOffset(duration); break; } todo->addAlarm(alarm); } } if (task->property("todoUid").isValid()) { todo->setUid(task->property("todoUid").toString()); } if (task->property("revision").isValid()) { todo->setRevision(task->property("revision").toInt()); } if (task->property("relatedUid").isValid()) { todo->setRelatedTo(task->property("relatedUid").toString()); } if (task->property("organizer").isValid()) { todo->setOrganizer(task->property("organizer").toString()); } if (task->delegate().isValid()) { KCalCore::Attendee::Ptr attendee(new KCalCore::Attendee(task->delegate().name(), task->delegate().email(), true, KCalCore::Attendee::NeedsAction)); todo->addAttendee(attendee); } for (const auto &a : task->attachments()) { auto attachment = KCalCore::Attachment::Ptr(new KCalCore::Attachment(QByteArray())); attachment->setMimeType(a->mimetype); attachment->setLabel(a->label); attachment->setData(a->data); todo->addAttachment(attachment); } Akonadi::Item item; if (task->property("itemId").isValid()) { item.setId(task->property("itemId").value()); } item.setMimeType(KCalCore::Todo::todoMimeType()); item.setPayload(todo); item.setParentCollection(Akonadi::Collection(task->property("parentCollectionId").value())); return item; } QString Serializer::relatedUidFromItem(Akonadi::Item item) { if (isTaskItem(item)) { const auto todo = item.payload(); if (todo->relatedTo() == todo->uid()) { qWarning() << "Task is related to itself: " << todo->uid() << item.id(); return QString(); } return todo->relatedTo(); } else if (isNoteItem(item)) { const auto message = item.payload(); const auto relatedHeader = message->headerByType("X-Zanshin-RelatedProjectUid"); return relatedHeader ? relatedHeader->asUnicodeString() : QString(); } else { return QString(); } } void Serializer::updateItemParent(Akonadi::Item item, Domain::Task::Ptr parent) { if (!isTaskItem(item)) return; auto todo = item.payload(); todo->setRelatedTo(parent->property("todoUid").toString()); } void Serializer::updateItemProject(Item item, Domain::Project::Ptr project) { if (isTaskItem(item)) { auto todo = item.payload(); todo->setRelatedTo(project->property("todoUid").toString()); } else if (isNoteItem(item)) { auto note = item.payload(); note->removeHeader("X-Zanshin-RelatedProjectUid"); const QByteArray parentUid = project->property("todoUid").toString().toUtf8(); if (!parentUid.isEmpty()) { auto relatedHeader = new KMime::Headers::Generic("X-Zanshin-RelatedProjectUid"); relatedHeader->from7BitString(parentUid); note->appendHeader(relatedHeader); } note->assemble(); } } void Serializer::removeItemParent(Akonadi::Item item) { if (!isTaskItem(item)) return; auto todo = item.payload(); todo->setRelatedTo(QString()); } Akonadi::Item::List Serializer::filterDescendantItems(const Akonadi::Item::List &potentialChildren, const Akonadi::Item &ancestorItem) { if (potentialChildren.isEmpty()) return Akonadi::Item::List(); Akonadi::Item::List itemsToProcess = potentialChildren; Q_ASSERT(ancestorItem.isValid() && ancestorItem.hasPayload()); KCalCore::Todo::Ptr todo = ancestorItem.payload(); const auto bound = std::partition(itemsToProcess.begin(), itemsToProcess.end(), [ancestorItem, todo](Akonadi::Item currentItem) { return (!currentItem.hasPayload() || currentItem == ancestorItem || currentItem.payload()->relatedTo() != todo->uid()); }); Akonadi::Item::List itemsRemoved; std::copy(itemsToProcess.begin(), bound, std::back_inserter(itemsRemoved)); itemsToProcess.erase(itemsToProcess.begin(), bound); auto result = std::accumulate(itemsToProcess.begin(), itemsToProcess.end(), Akonadi::Item::List(), [this, itemsRemoved](Akonadi::Item::List result, Akonadi::Item currentItem) { result << currentItem; return result += filterDescendantItems(itemsRemoved, currentItem); }); return result; } bool Serializer::isNoteItem(Item item) { return item.hasPayload(); } Domain::Note::Ptr Serializer::createNoteFromItem(Akonadi::Item item) { if (!isNoteItem(item)) return Domain::Note::Ptr(); Domain::Note::Ptr note = Domain::Note::Ptr::create(); updateNoteFromItem(note, item); return note; } void Serializer::updateNoteFromItem(Domain::Note::Ptr note, Item item) { if (!isNoteItem(item)) return; auto message = item.payload(); NoteUtils::NoteMessageWrapper wrappedNote(message); note->setTitle(wrappedNote.title()); note->setText(wrappedNote.text()); note->setProperty("itemId", item.id()); if (auto relatedHeader = message->headerByType("X-Zanshin-RelatedProjectUid")) { note->setProperty("relatedUid", relatedHeader->asUnicodeString()); } else { note->setProperty("relatedUid", QVariant()); } } Item Serializer::createItemFromNote(Domain::Note::Ptr note) { NoteUtils::NoteMessageWrapper builder; builder.setTitle(note->title()); builder.setText(note->text()); KMime::Message::Ptr message = builder.message(); if (!note->property("relatedUid").toString().isEmpty()) { auto relatedHeader = new KMime::Headers::Generic("X-Zanshin-RelatedProjectUid"); relatedHeader->from7BitString(note->property("relatedUid").toString().toUtf8()); message->appendHeader(relatedHeader); } Akonadi::Item item; if (note->property("itemId").isValid()) { item.setId(note->property("itemId").value()); } item.setMimeType(Akonadi::NoteUtils::noteMimeType()); item.setPayload(message); return item; } bool Serializer::isProjectItem(Item item) { if (!item.hasPayload()) return false; return !isTaskItem(item); } Domain::Project::Ptr Serializer::createProjectFromItem(Item item) { if (!isProjectItem(item)) return Domain::Project::Ptr(); auto project = Domain::Project::Ptr::create(); updateProjectFromItem(project, item); return project; } void Serializer::updateProjectFromItem(Domain::Project::Ptr project, Item item) { if (!isProjectItem(item)) return; auto todo = item.payload(); project->setName(todo->summary()); project->setProperty("itemId", item.id()); project->setProperty("parentCollectionId", item.parentCollection().id()); project->setProperty("todoUid", todo->uid()); } Item Serializer::createItemFromProject(Domain::Project::Ptr project) { auto todo = KCalCore::Todo::Ptr::create(); todo->setSummary(project->name()); todo->setCustomProperty("Zanshin", "Project", "1"); if (project->property("todoUid").isValid()) { todo->setUid(project->property("todoUid").toString()); } Akonadi::Item item; if (project->property("itemId").isValid()) { item.setId(project->property("itemId").value()); } if (project->property("parentCollectionId").isValid()) { auto parentId = project->property("parentCollectionId").value(); item.setParentCollection(Akonadi::Collection(parentId)); } item.setMimeType(KCalCore::Todo::todoMimeType()); item.setPayload(todo); return item; } bool Serializer::isProjectChild(Domain::Project::Ptr project, Item item) { const QString todoUid = project->property("todoUid").toString(); const QString relatedUid = relatedUidFromItem(item); return !todoUid.isEmpty() && !relatedUid.isEmpty() && todoUid == relatedUid; } Domain::Context::Ptr Serializer::createContextFromTag(Akonadi::Tag tag) { if (!isContext(tag)) return Domain::Context::Ptr(); auto context = Domain::Context::Ptr::create(); updateContextFromTag(context, tag); return context; } Akonadi::Tag Serializer::createTagFromContext(Domain::Context::Ptr context) { auto tag = Akonadi::Tag(); tag.setName(context->name()); tag.setType(Akonadi::SerializerInterface::contextTagType()); tag.setGid(QByteArray(context->name().toLatin1())); if (context->property("tagId").isValid()) tag.setId(context->property("tagId").value()); return tag; } void Serializer::updateContextFromTag(Domain::Context::Ptr context, Akonadi::Tag tag) { if (!isContext(tag)) return; context->setProperty("tagId", tag.id()); context->setName(tag.name()); } bool Serializer::hasContextTags(Item item) const { using namespace std::placeholders; Tag::List tags = item.tags(); return std::any_of(tags.constBegin(), tags.constEnd(), std::bind(std::mem_fn(&Serializer::isContext), this, _1)); } bool Serializer::hasAkonadiTags(Item item) const { using namespace std::placeholders; Tag::List tags = item.tags(); return std::any_of(tags.constBegin(), tags.constEnd(), std::bind(std::mem_fn(&Serializer::isAkonadiTag), this, _1)); } bool Serializer::isContext(const Akonadi::Tag &tag) const { return (tag.type() == Akonadi::SerializerInterface::contextTagType()); } bool Serializer::isAkonadiTag(const Tag &tag) const { return tag.type() == Akonadi::Tag::PLAIN || tag.type() == Akonadi::Tag::GENERIC; } bool Serializer::isContextTag(const Domain::Context::Ptr &context, const Akonadi::Tag &tag) const { return (context->property("tagId").value() == tag.id()); } bool Serializer::isContextChild(Domain::Context::Ptr context, Item item) const { if (!context->property("tagId").isValid()) return false; auto tagId = context->property("tagId").value(); Akonadi::Tag tag(tagId); return item.hasTag(tag); } Domain::Tag::Ptr Serializer::createTagFromAkonadiTag(Akonadi::Tag akonadiTag) { if (!isAkonadiTag(akonadiTag)) return Domain::Tag::Ptr(); auto tag = Domain::Tag::Ptr::create(); updateTagFromAkonadiTag(tag, akonadiTag); return tag; } void Serializer::updateTagFromAkonadiTag(Domain::Tag::Ptr tag, Akonadi::Tag akonadiTag) { if (!isAkonadiTag(akonadiTag)) return; tag->setProperty("tagId", akonadiTag.id()); tag->setProperty("uid", akonadiTag.gid()); tag->setName(akonadiTag.name()); } Akonadi::Tag Serializer::createAkonadiTagFromTag(Domain::Tag::Ptr tag) { auto akonadiTag = Akonadi::Tag::genericTag(tag->name()); const auto tagUidProperty = tag->property("uid"); if (tagUidProperty.isValid()) { akonadiTag.setGid(tagUidProperty.toByteArray()); } const auto tagProperty = tag->property("tagId"); if (tagProperty.isValid()) akonadiTag.setId(tagProperty.value()); return akonadiTag; } bool Serializer::isTagChild(Domain::Tag::Ptr tag, Akonadi::Item item) { if (!tag->property("tagId").isValid()) return false; auto tagId = tag->property("tagId").value(); Akonadi::Tag akonadiTag(tagId); return item.hasTag(akonadiTag); } Akonadi::Item Serializer::createItemFromArtifact(Domain::Artifact::Ptr artifact) { if (auto task = artifact.objectCast()) { return createItemFromTask(task); } if (auto note = artifact.objectCast()) { return createItemFromNote(note); } return Akonadi::Item(); } Domain::Relation::Ptr Serializer::createRelationFromAkonadiRelation(const QPair &akonadiRelation) { auto relation = Domain::Relation::Ptr::create(); relation->setUrl(akonadiRelation.first.url()); if (akonadiRelation.first.hasPayload()) { QString subject; auto header = akonadiRelation.first.payload()->subject(); if (header) { subject = header->asUnicodeString(); } else { subject = "Unknown mail"; } relation->setName(subject); } relation->setProperty("akonadiRelation", QVariant::fromValue(akonadiRelation.second)); return relation; } bool Serializer::representsAkonadiRelation(Domain::Relation::Ptr relation, const QPair &akonadiRelation) const { return relation->url() == akonadiRelation.first.url(); } Akonadi::Relation Serializer::createAkonadiRelationFromRelation(Domain::Relation::Ptr relation) { return relation->property("akonadiRelation").value(); }