diff --git a/resources/google-groupware/calendarhandler.cpp b/resources/google-groupware/calendarhandler.cpp index b10d1ed5d..de8510dff 100644 --- a/resources/google-groupware/calendarhandler.cpp +++ b/resources/google-groupware/calendarhandler.cpp @@ -1,395 +1,395 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #include "calendarhandler.h" #include "defaultreminderattribute.h" #include "googleresource.h" #include "googlesettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googlecalendar_debug.h" using namespace KGAPI2; using namespace Akonadi; static constexpr uint32_t KGAPIEventVersion = 1; QString CalendarHandler::mimetype() { return KCalendarCore::Event::eventMimeType(); } bool CalendarHandler::canPerformTask(const Item &item) { return m_resource->canPerformTask(item, mimetype()); } void CalendarHandler::setupCollection(Collection &collection, const CalendarPtr &calendar) { collection.setContentMimeTypes({ mimetype() }); collection.setName(calendar->uid()); collection.setParentCollection(m_resource->rootCollection()); collection.setRemoteId(calendar->uid()); if (calendar->editable()) { collection.setRights(Collection::CanChangeCollection |Collection::CanDeleteCollection |Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); } else { collection.setRights(Collection::ReadOnly); } // TODO: for some reason, KOrganizer creates virtual collections //newCollection.setVirtual(false); // Setting icon auto attr = collection.attribute(Collection::AddIfMissing); attr->setDisplayName(calendar->title()); attr->setIconName(QStringLiteral("view-calendar")); // Setting color if (calendar->backgroundColor().isValid()) { auto colorAttr = collection.attribute(Collection::AddIfMissing); colorAttr->setColor(calendar->backgroundColor()); } // Setting default remoinders auto reminderAttr = collection.attribute(Collection::AddIfMissing); reminderAttr->setReminders(calendar->defaultReminders()); // Block email reminders, since Google sends them for us auto blockAlarms = collection.attribute(Collection::AddIfMissing); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Audio, false); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Display, false); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Procedure, false); } void CalendarHandler::retrieveCollections() { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving calendars")); qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieving calendars..."; auto job = new CalendarFetchJob(m_settings->accountPtr(), this); connect(job, &CalendarFetchJob::finished, this, &CalendarHandler::slotCollectionsRetrieved); } -void CalendarHandler::slotCollectionsRetrieved(KGAPI2::Job* job) +void CalendarHandler::slotCollectionsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_CALENDAR_LOG) << "Calendars retrieved"; const ObjectsList calendars = qobject_cast(job)->items(); Collection::List collections; collections.reserve(calendars.count()); const QStringList activeCalendars = m_settings->calendars(); for (const auto &object : calendars) { const CalendarPtr &calendar = object.dynamicCast(); qCDebug(GOOGLE_CALENDAR_LOG) << " -" << calendar->title() << "(" << calendar->uid() << ")"; if (!activeCalendars.contains(calendar->uid())) { qCDebug(GOOGLE_CALENDAR_LOG) << "Skipping, not subscribed"; continue; } Collection collection; setupCollection(collection, calendar); collections << collection; } m_resource->collectionsRetrievedFromHandler(collections); } void CalendarHandler::retrieveItems(const Collection &collection) { qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieving events for calendar" << collection.remoteId(); QString syncToken = collection.remoteRevision(); auto job = new EventFetchJob(collection.remoteId(), m_settings->accountPtr(), this); if (!syncToken.isEmpty()) { qCDebug(GOOGLE_CALENDAR_LOG) << "Using sync token" << syncToken; job->setSyncToken(syncToken); job->setFetchDeleted(true); } else { // No need to fetch deleted items for non-incremental update job->setFetchDeleted(false); if (!m_settings->eventsSince().isEmpty()) { const QDate date = QDate::fromString(m_settings->eventsSince(), Qt::ISODate); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) job->setTimeMin(QDateTime(date).toSecsSinceEpoch()); #else job->setTimeMin(QDateTime(date.startOfDay()).toSecsSinceEpoch()); #endif } } job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &EventFetchJob::finished, this, &CalendarHandler::slotItemsRetrieved); Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving events for calendar '%1'", collection.displayName())); } void CalendarHandler::slotItemsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } Item::List changedItems, removedItems; Collection collection = job->property(COLLECTION_PROPERTY).value(); DefaultReminderAttribute *attr = collection.attribute(); auto fetchJob = qobject_cast(job); const ObjectsList objects = fetchJob->items(); bool isIncremental = !fetchJob->syncToken().isEmpty(); qCDebug(GOOGLE_CALENDAR_LOG) << "Retrieved" << objects.count() << "events for calendar" << collection.remoteId(); for (const ObjectPtr &object : objects) { const EventPtr event = object.dynamicCast(); if (event->useDefaultReminders() && attr) { const KCalendarCore::Alarm::List alarms = attr->alarms(event.data()); for (const KCalendarCore::Alarm::Ptr &alarm : alarms) { event->addAlarm(alarm); } } Item item; item.setMimeType(KCalendarCore::Event::eventMimeType()); item.setParentCollection(collection); item.setRemoteId(event->id()); item.setRemoteRevision(event->etag()); item.setPayload(event.dynamicCast()); if (event->deleted()) { qCDebug(GOOGLE_CALENDAR_LOG) << " - removed" << event->uid(); removedItems << item; } else { qCDebug(GOOGLE_CALENDAR_LOG) << " - changed" << event->uid(); changedItems << item; } } if (!isIncremental) { m_resource->itemsRetrieved(changedItems); } else { m_resource->itemsRetrievedIncremental(changedItems, removedItems); } qCDebug(GOOGLE_CALENDAR_LOG) << "Next sync token:" << fetchJob->syncToken(); collection.setRemoteRevision(fetchJob->syncToken()); new CollectionModifyJob(collection, this); m_resource->emitReadyStatus(); } void CalendarHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.name())); qCDebug(GOOGLE_CALENDAR_LOG) << "Event added to calendar" << collection.remoteId(); KCalendarCore::Event::Ptr event = item.payload(); EventPtr kevent(new Event(*event)); auto *job = new EventCreateJob(kevent, collection.remoteId(), m_settings->accountPtr(), this); job->setSendUpdates(SendUpdatesPolicy::None); connect(job, &EventCreateJob::finished, this, [this, item](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } Item newItem(item); const EventPtr event = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_CALENDAR_LOG) << "Event added"; newItem.setRemoteId(event->id()); newItem.setRemoteRevision(event->etag()); newItem.setGid(event->uid()); m_resource->changeCommitted(newItem); newItem.setPayload(event.dynamicCast()); new ItemModifyJob(newItem, this); m_resource->emitReadyStatus(); }); } void CalendarHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing event in calendar '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_CALENDAR_LOG) << "Changing event" << item.remoteId(); KCalendarCore::Event::Ptr event = item.payload(); EventPtr kevent(new Event(*event)); auto job = new EventModifyJob(kevent, item.parentCollection().remoteId(), m_settings->accountPtr(), this); job->setSendUpdates(SendUpdatesPolicy::None); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &EventModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::itemsRemoved(const Item::List &items) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 events", "Removing %1 event", items.count())); QStringList eventIds; eventIds.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(eventIds), [](const Item &item){ return item.remoteId(); }); qCDebug(GOOGLE_CALENDAR_LOG) << "Removing events:" << eventIds; // TODO: what if events are from diferent calendars? auto job = new EventDeleteJob(eventIds, items.first().parentCollection().remoteId(), m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &EventDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Moving %1 events from calendar '%2' to calendar '%3'", "Moving %1 event from calendar '%2' to calendar '%3'", items.count(), collectionSource.displayName(), collectionDestination.displayName())); QStringList eventIds; eventIds.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(eventIds), [](const Item &item){ return item.remoteId(); }); qCDebug(GOOGLE_CALENDAR_LOG) << "Moving events" << eventIds << "from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); auto job = new EventMoveJob(eventIds, collectionSource.remoteId(), collectionDestination.remoteId(), m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &EventMoveJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::collectionAdded(const Collection &collection, const Collection &parent) { Q_UNUSED(parent); Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating calendar '%1'", collection.displayName())); qCDebug(GOOGLE_CALENDAR_LOG) << "Adding calendar" << collection.displayName(); CalendarPtr calendar(new Calendar()); calendar->setTitle(collection.displayName()); calendar->setEditable(true); auto job = new CalendarCreateJob(calendar, m_settings->accountPtr(), this); connect(job, &CalendarCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } CalendarPtr calendar = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_CALENDAR_LOG) << "Created calendar" << calendar->uid(); // Enable newly added calendar in settings, otherwise user won't see it m_settings->addCalendar(calendar->uid()); // TODO: the calendar returned by google is almost empty, i.e. it's not "editable", // does not contain the color, etc calendar->setEditable(true); // Populate remoteId & other stuff Collection newCollection(collection); setupCollection(newCollection, calendar); m_resource->changeCommitted(newCollection); m_resource->emitReadyStatus(); }); } void CalendarHandler::collectionChanged(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing calendar '%1'", collection.displayName())); qCDebug(GOOGLE_CALENDAR_LOG) << "Changing calendar" << collection.remoteId(); CalendarPtr calendar(new Calendar()); calendar->setUid(collection.remoteId()); calendar->setTitle(collection.displayName()); calendar->setEditable(true); auto job = new CalendarModifyJob(calendar, m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &CalendarModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::collectionRemoved(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing calendar '%1'", collection.displayName())); qCDebug(GOOGLE_CALENDAR_LOG) << "Removing calendar" << collection.remoteId(); auto job = new CalendarDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &CalendarDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } QDateTime CalendarHandler::lastCacheUpdate() const { return QDateTime(); } void CalendarHandler::canHandleFreeBusy(const QString &email) const { if (m_resource->canPerformTask()) { m_resource->handlesFreeBusy(email, false); return; } auto job = new FreeBusyQueryJob(email, QDateTime::currentDateTimeUtc(), QDateTime::currentDateTimeUtc().addSecs(3600), m_settings->accountPtr(), const_cast(this)); connect(job, &FreeBusyQueryJob::finished, this, [this](KGAPI2::Job *job){ auto queryJob = qobject_cast(job); if (!m_resource->handleError(job, false)) { m_resource->handlesFreeBusy(queryJob->id(), false); return; } m_resource->handlesFreeBusy(queryJob->id(), true); }); } void CalendarHandler::retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) { if (m_resource->canPerformTask()) { m_resource->freeBusyRetrieved(email, QString(), false, QString()); return; } auto job = new FreeBusyQueryJob(email, start, end, m_settings->accountPtr(), this); connect(job, &FreeBusyQueryJob::finished, this, [this](KGAPI2::Job *job) { auto queryJob = qobject_cast(job); if (!m_resource->handleError(job, false)) { m_resource->freeBusyRetrieved(queryJob->id(), QString(), false, QString()); return; } KCalendarCore::FreeBusy::Ptr fb(new KCalendarCore::FreeBusy); fb->setUid(QStringLiteral("%1%2@google.com").arg(QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMddTHHmmssZ")))); fb->setOrganizer(job->account()->accountName()); fb->addAttendee(KCalendarCore::Attendee(QString(), queryJob->id())); // FIXME: is it really sort? fb->setDateTime(QDateTime::currentDateTimeUtc(), KCalendarCore::IncidenceBase::RoleSort); const auto ranges = queryJob->busy(); for (const auto &range : ranges) { fb->addPeriod(range.busyStart, range.busyEnd); } KCalendarCore::ICalFormat format; const QString fbStr = format.createScheduleMessage(fb, KCalendarCore::iTIPRequest); m_resource->freeBusyRetrieved(queryJob->id(), fbStr, true, QString()); }); } diff --git a/resources/google-groupware/calendarhandler.h b/resources/google-groupware/calendarhandler.h index 3031ca1c3..c7adddef0 100644 --- a/resources/google-groupware/calendarhandler.h +++ b/resources/google-groupware/calendarhandler.h @@ -1,59 +1,59 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 2020 Igor Pobiko 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 3 of the License, or (at your option) any later version. 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, see . */ #ifndef CALENDARHANDLER_H #define CALENDARHANDLER_H #include "generichandler.h" #include #include class CalendarHandler : public GenericHandler { Q_OBJECT public: typedef QSharedPointer Ptr; using GenericHandler::GenericHandler; QString mimetype() override; bool canPerformTask(const Akonadi::Item &item) override; void retrieveCollections() override; void retrieveItems(const Akonadi::Collection &collection) override; void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; void itemsRemoved(const Akonadi::Item::List &items) override; void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; void collectionChanged(const Akonadi::Collection &collection) override; void collectionRemoved(const Akonadi::Collection &collection) override; // FreeBusy QDateTime lastCacheUpdate() const; void canHandleFreeBusy(const QString &email) const; void retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end); private Q_SLOTS: - void slotCollectionsRetrieved(KGAPI2::Job* job); - void slotItemsRetrieved(KGAPI2::Job* job); + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); private: - void setupCollection(Akonadi::Collection& collection, const KGAPI2::CalendarPtr& group); + void setupCollection(Akonadi::Collection &collection, const KGAPI2::CalendarPtr &group); }; #endif // CALENDARHANDLER_H diff --git a/resources/google-groupware/contacthandler.cpp b/resources/google-groupware/contacthandler.cpp index ea64fc357..a53cc38a7 100644 --- a/resources/google-groupware/contacthandler.cpp +++ b/resources/google-groupware/contacthandler.cpp @@ -1,494 +1,494 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #include "contacthandler.h" #include "googleresource.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googlecontacts_debug.h" #define OTHERCONTACTS_REMOTEID QStringLiteral("OtherContacts") using namespace KGAPI2; using namespace Akonadi; QString ContactHandler::mimetype() { return KContacts::Addressee::mimeType(); } bool ContactHandler::canPerformTask(const Item &item) { return m_resource->canPerformTask(item, mimetype()); } QString ContactHandler::myContactsRemoteId() const { return QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/6").arg(QString::fromLatin1(QUrl::toPercentEncoding(m_settings->accountPtr()->accountName()))); } void ContactHandler::setupCollection(Collection &collection, const ContactsGroupPtr &group) { collection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); collection.setName(group->id()); collection.setRemoteId(group->id()); collection.setParentCollection(m_resource->rootCollection()); QString realName = group->title(); if (group->isSystemGroup()) { if (group->title().contains(QLatin1String("Coworkers"))) { realName = i18nc("Name of a group of contacts", "Coworkers"); } else if (group->title().contains(QLatin1String("Friends"))) { realName = i18nc("Name of a group of contacts", "Friends"); } else if (group->title().contains(QLatin1String("Family"))) { realName = i18nc("Name of a group of contacts", "Family"); } else if (group->title().contains(QLatin1String("My Contacts"))) { realName = i18nc("Name of a group of contacts", "My Contacts"); } } // "My Contacts" is the only one not virtual if (group->id() == myContactsRemoteId()) { collection.setRights(Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); } else { collection.setRights(Collection::CanLinkItem |Collection::CanUnlinkItem |Collection::CanChangeItem); collection.setVirtual(true); if (!group->isSystemGroup()) { collection.setRights(collection.rights() |Collection::CanChangeCollection |Collection::CanDeleteCollection); } } auto attr = collection.attribute(Collection::AddIfMissing); attr->setDisplayName(realName); attr->setIconName(QStringLiteral("view-pim-contacts")); } void ContactHandler::retrieveCollections() { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts groups")); qCDebug(GOOGLE_CONTACTS_LOG) << "Retrieving contacts groups..."; m_collections.clear(); Collection otherCollection; otherCollection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); otherCollection.setName(i18n("Other Contacts")); otherCollection.setParentCollection(m_resource->rootCollection()); otherCollection.setRights(Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); otherCollection.setRemoteId(OTHERCONTACTS_REMOTEID); auto attr = otherCollection.attribute(Collection::AddIfMissing); attr->setDisplayName(i18n("Other Contacts")); attr->setIconName(QStringLiteral("view-pim-contacts")); m_resource->collectionsRetrieved({ otherCollection }); m_collections[ OTHERCONTACTS_REMOTEID ] = otherCollection; auto job = new ContactsGroupFetchJob(m_settings->accountPtr(), this); connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotCollectionsRetrieved); } -void ContactHandler::slotCollectionsRetrieved(KGAPI2::Job* job) +void ContactHandler::slotCollectionsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_CONTACTS_LOG) << "Contacts groups retrieved"; const ObjectsList objects = qobject_cast(job)->items(); Collection::List collections; collections.reserve(objects.count()); std::transform(objects.cbegin(), objects.cend(), std::back_inserter(collections), [this](const ObjectPtr &object){ const ContactsGroupPtr group = object.dynamicCast(); qCDebug(GOOGLE_CONTACTS_LOG) << " -" << group->title() << "(" << group->id() << ")"; Collection collection; setupCollection(collection, group); m_collections[ collection.remoteId() ] = collection; return collection; }); m_resource->collectionsRetrievedFromHandler(collections); } void ContactHandler::retrieveItems(const Collection &collection) { // Contacts are stored inside "My Contacts" and "Other Contacts" only if ((collection.remoteId() != OTHERCONTACTS_REMOTEID) && (collection.remoteId() != myContactsRemoteId())) { m_resource->itemsRetrievalDone(); return; } Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts for group '%1'", collection.displayName())); qCDebug(GOOGLE_CONTACTS_LOG) << "Retreiving contacts for group" << collection.remoteId() << "..."; auto job = new ContactFetchJob(m_settings->accountPtr(), this); if (!collection.remoteRevision().isEmpty()) { job->setFetchOnlyUpdated(collection.remoteRevision().toLongLong()); job->setFetchDeleted(true); } else { // No need to fetch deleted items for a non-incremental update job->setFetchDeleted(false); } connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotItemsRetrieved); } void ContactHandler::slotItemsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } Collection collection = m_resource->currentCollection(); Item::List changedItems, removedItems; QHash groupsMap; QStringList changedPhotos; auto fetchJob = qobject_cast(job); bool isIncremental = (fetchJob->fetchOnlyUpdated() > 0); const ObjectsList objects = fetchJob->items(); qCDebug(GOOGLE_CONTACTS_LOG) << "Retrieved" << objects.count() << "contacts"; for (const ObjectPtr &object : objects) { const ContactPtr contact = object.dynamicCast(); Item item; item.setMimeType( mimetype() ); item.setParentCollection(collection); item.setRemoteId(contact->uid()); item.setRemoteRevision(contact->etag()); item.setPayload(*contact.dynamicCast()); if (contact->deleted() || (collection.remoteId() == OTHERCONTACTS_REMOTEID && !contact->groups().isEmpty()) || (collection.remoteId() == myContactsRemoteId() && contact->groups().isEmpty())) { qCDebug(GOOGLE_CONTACTS_LOG) << " - removed" << contact->uid(); removedItems << item; } else { qCDebug(GOOGLE_CONTACTS_LOG) << " - changed" << contact->uid(); changedItems << item; changedPhotos << contact->uid(); } const QStringList groups = contact->groups(); for (const QString &group : groups) { // We don't link contacts to "My Contacts" if (group != myContactsRemoteId()) { groupsMap[group] << item; } } } if (isIncremental) { m_resource->itemsRetrievedIncremental(changedItems, removedItems); } else { m_resource->itemsRetrieved(changedItems); } for (auto iter = groupsMap.constBegin(), iterEnd = groupsMap.constEnd(); iter != iterEnd; ++iter) { new LinkJob(m_collections[iter.key()], iter.value(), this); } // TODO: unlink if the group was removed! if (!changedPhotos.isEmpty()) { QVariantMap map; map[QStringLiteral("collection")] = QVariant::fromValue(collection); map[QStringLiteral("modified")] = QVariant::fromValue(changedPhotos); m_resource->scheduleCustomTask(this, "retrieveContactsPhotos", map); } const QDateTime local(QDateTime::currentDateTime()); const QDateTime UTC(local.toUTC()); collection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); new CollectionModifyJob(collection, this); m_resource->emitReadyStatus(); } void ContactHandler::retrieveContactsPhotos(const QVariant &argument) { if (!m_resource->canPerformTask()) { return; } const auto map = argument.value(); const auto collection = map[QStringLiteral("collection")].value(); const auto changedPhotos = map[QStringLiteral("modified")].toStringList(); Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Retrieving %1 contacts photos for group '%2'", "Retrieving %1 contact photo for group '%2'", changedPhotos.count(), collection.displayName())); Item::List items; items.reserve(changedPhotos.size()); std::transform(changedPhotos.cbegin(), changedPhotos.cend(), std::back_inserter(items), [](const QString &contact){ Item item; item.setRemoteId(contact); return item; }); auto job = new ItemFetchJob(items, this); job->setCollection(collection); job->fetchScope().fetchFullPayload(true); connect(job, &ItemFetchJob::finished, this, &ContactHandler::slotUpdatePhotosItemsRetrieved); } void ContactHandler::slotUpdatePhotosItemsRetrieved(KJob *job) { // Make sure account is still valid if (!m_resource->canPerformTask()) { return; } const Item::List items = qobject_cast(job)->items(); ContactsList contacts; qCDebug(GOOGLE_CONTACTS_LOG) << "Fetched" << items.count() << "contacts for photo update"; contacts.reserve(items.size()); std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), [](const Item &item){ const KContacts::Addressee addressee = item.payload(); const ContactPtr contact(new Contact(addressee)); return contact; }); qCDebug(GOOGLE_CONTACTS_LOG) << "Starting fetching photos..."; auto photoJob = new ContactFetchPhotoJob(contacts, m_settings->accountPtr(), this); photoJob->setProperty("processedItems", 0); connect(photoJob, &ContactFetchPhotoJob::photoFetched, this, [this, items](KGAPI2::Job *job, const ContactPtr &contact){ qCDebug(GOOGLE_CONTACTS_LOG) << " - fetched photo for contact" << contact->uid(); int processedItems = job->property("processedItems").toInt(); processedItems++; job->setProperty("processedItems", processedItems); Q_EMIT m_resource->percent(100.0f*processedItems / items.count()); auto it = std::find_if(items.cbegin(), items.cend(), [&contact](const Item &item){ return item.remoteId() == contact->uid(); }); if (it != items.cend()) { Item newItem(*it); newItem.setPayload(*contact.dynamicCast()); new ItemModifyJob(newItem, this); } }); connect(photoJob, &ContactFetchPhotoJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding contact to group '%1'", collection.displayName())); auto addressee = item.payload< KContacts::Addressee >(); ContactPtr contact(new Contact(addressee)); qCDebug(GOOGLE_CONTACTS_LOG) << "Creating contact"; if (collection.remoteId() == myContactsRemoteId()) { contact->addGroup(myContactsRemoteId()); } auto job = new ContactCreateJob(contact, m_settings->accountPtr(), this); - connect(job, &ContactCreateJob::finished, this, [this, item](KGAPI2::Job* job){ + connect(job, &ContactCreateJob::finished, this, [this, item](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } ContactPtr contact = qobject_cast(job)->items().first().dynamicCast(); Item newItem = item; qCDebug(GOOGLE_CONTACTS_LOG) << "Contact" << contact->uid() << "created"; newItem.setRemoteId(contact->uid()); newItem.setRemoteRevision(contact->etag()); m_resource->changeCommitted(newItem); newItem.setPayload(*contact.dynamicCast()); new ItemModifyJob(newItem, this); m_resource->emitReadyStatus(); }); } void ContactHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing contact")); qCDebug(GOOGLE_CONTACTS_LOG) << "Changing contact" << item.remoteId(); KContacts::Addressee addressee = item.payload< KContacts::Addressee >(); ContactPtr contact(new Contact(addressee)); auto job = new ContactModifyJob(contact, m_settings->accountPtr(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemsRemoved(const Item::List &items) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 contacts", "Removing %1 contact", items.count())); QStringList contactIds; contactIds.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(contactIds), [](const Item &item){ return item.remoteId(); }); qCDebug(GOOGLE_CONTACTS_LOG) << "Removing contacts" << contactIds; auto job = new ContactDeleteJob(contactIds, m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &ContactDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination) { qCDebug(GOOGLE_CONTACTS_LOG) << "Moving contacts from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); if (!(((collectionSource.remoteId() == myContactsRemoteId()) && (collectionDestination.remoteId() == OTHERCONTACTS_REMOTEID)) || ((collectionSource.remoteId() == OTHERCONTACTS_REMOTEID) && (collectionDestination.remoteId() == myContactsRemoteId())))) { m_resource->cancelTask(i18n("Invalid source or destination collection")); } Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Moving %1 contacts from group '%2' to '%3'", "Moving %1 contact from group '%2' to '%3'", items.count(), collectionSource.remoteId(), collectionDestination.remoteId())); ContactsList contacts; contacts.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), [this, &collectionSource, &collectionDestination](const Item &item){ KContacts::Addressee addressee = item.payload(); ContactPtr contact(new Contact(addressee)); // MyContacts -> OtherContacts if (collectionSource.remoteId() == myContactsRemoteId() && collectionDestination.remoteId() == OTHERCONTACTS_REMOTEID) { contact->clearGroups(); // OtherContacts -> MyContacts } else if (collectionSource.remoteId() == OTHERCONTACTS_REMOTEID && collectionDestination.remoteId() == myContactsRemoteId()) { contact->addGroup(myContactsRemoteId()); } return contact; }); qCDebug(GOOGLE_CONTACTS_LOG) << "Moving contacts from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemsLinked(const Item::List &items, const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Linking %1 contact", "Linking %1 contacts", items.count())); qCDebug(GOOGLE_CONTACTS_LOG) << "Linking" << items.count() << "contacts to group" << collection.remoteId(); ContactsList contacts; contacts.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), [this, &collection](const Item &item){ KContacts::Addressee addressee = item.payload(); ContactPtr contact(new Contact(addressee)); contact->addGroup(collection.remoteId()); return contact; }); auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemsUnlinked(const Item::List &items, const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Unlinking %1 contact", "Unlinking %1 contacts", items.count())); qCDebug(GOOGLE_CONTACTS_LOG) << "Unlinking" << items.count() << "contacts from group" << collection.remoteId(); ContactsList contacts; contacts.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(contacts), [this, &collection](const Item &item){ KContacts::Addressee addressee = item.payload(); ContactPtr contact(new Contact(addressee)); contact->removeGroup(collection.remoteId()); return contact; }); auto job = new ContactModifyJob(contacts, m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::collectionAdded(const Collection &collection, const Collection & /*parent*/) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating new contact group '%1'", collection.displayName())); qCDebug(GOOGLE_CONTACTS_LOG) << "Adding contact group" << collection.displayName(); ContactsGroupPtr group(new ContactsGroup); group->setTitle(collection.name()); group->setIsSystemGroup(false); auto job = new ContactsGroupCreateJob(group, m_settings->accountPtr(), this); - connect(job, &ContactsGroupCreateJob::finished, this, [this, collection](KGAPI2::Job* job){ + connect(job, &ContactsGroupCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } ContactsGroupPtr group = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_CONTACTS_LOG) << "Contact group created:" << group->id(); Collection newCollection(collection); setupCollection(newCollection, group); m_collections[ newCollection.remoteId() ] = newCollection; m_resource->changeCommitted(newCollection); m_resource->emitReadyStatus(); }); } void ContactHandler::collectionChanged(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing contact group '%1'", collection.displayName())); qCDebug(GOOGLE_CONTACTS_LOG) << "Changing contact group" << collection.remoteId(); ContactsGroupPtr group(new ContactsGroup()); group->setId(collection.remoteId()); group->setTitle(collection.displayName()); auto job = new ContactsGroupModifyJob(group, m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &ContactsGroupModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::collectionRemoved(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing contact group '%1'", collection.displayName())); qCDebug(GOOGLE_CONTACTS_LOG) << "Removing contact group" << collection.remoteId(); auto job = new ContactsGroupDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &ContactsGroupDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } diff --git a/resources/google-groupware/contacthandler.h b/resources/google-groupware/contacthandler.h index 625451f77..c0e39313e 100644 --- a/resources/google-groupware/contacthandler.h +++ b/resources/google-groupware/contacthandler.h @@ -1,61 +1,61 @@ /* Copyright (C) 2011, 2012 Dan Vratil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #ifndef CONTACTHANDLER_H #define CONTACTHANDLER_H #include "generichandler.h" #include #include class ContactHandler : public GenericHandler { Q_OBJECT public: using GenericHandler::GenericHandler; QString mimetype() override; bool canPerformTask(const Akonadi::Item &item) override; void retrieveCollections() override; void retrieveItems(const Akonadi::Collection &collection) override; void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; void itemsRemoved(const Akonadi::Item::List &items) override; void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection) override; void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; void collectionChanged(const Akonadi::Collection &collection) override; void collectionRemoved(const Akonadi::Collection &collection) override; private Q_SLOTS: - void slotCollectionsRetrieved(KGAPI2::Job* job); - void slotItemsRetrieved(KGAPI2::Job* job); + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); void slotUpdatePhotosItemsRetrieved(KJob *job); void retrieveContactsPhotos(const QVariant &arguments); private: QString myContactsRemoteId() const; - void setupCollection(Akonadi::Collection& collection, const KGAPI2::ContactsGroupPtr& group); + void setupCollection(Akonadi::Collection &collection, const KGAPI2::ContactsGroupPtr &group); QMap m_collections; }; #endif // CONTACTHANDLER_H diff --git a/resources/google-groupware/defaultreminderattribute.cpp b/resources/google-groupware/defaultreminderattribute.cpp index 182b2f7d5..04e1e6617 100644 --- a/resources/google-groupware/defaultreminderattribute.cpp +++ b/resources/google-groupware/defaultreminderattribute.cpp @@ -1,115 +1,115 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 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 3 of the License, or (at your option) any later version. 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, see . */ #include "defaultreminderattribute.h" #include #include #include using namespace KGAPI2; DefaultReminderAttribute::DefaultReminderAttribute() { } Akonadi::Attribute *DefaultReminderAttribute::clone() const { DefaultReminderAttribute *attr = new DefaultReminderAttribute(); attr->setReminders(m_reminders); return attr; } void DefaultReminderAttribute::setReminders(const RemindersList &reminders) { m_reminders = reminders; } void DefaultReminderAttribute::deserialize(const QByteArray &data) { QJsonDocument json = QJsonDocument::fromJson(data); if (json.isNull()) { return; } - QVariant var = json.toVariant(); + const QVariant var = json.toVariant(); const QVariantList list = var.toList(); for (const QVariant &l : list) { QVariantMap reminder = l.toMap(); KGAPI2::ReminderPtr rem(new KGAPI2::Reminder); if (reminder[QStringLiteral("type")].toString() == QLatin1String("display")) { rem->setType(KCalendarCore::Alarm::Display); } else if (reminder[QStringLiteral("type")].toString() == QLatin1String("email")) { rem->setType(KCalendarCore::Alarm::Email); } KCalendarCore::Duration offset(reminder[QStringLiteral("time")].toInt(), KCalendarCore::Duration::Seconds); rem->setStartOffset(offset); m_reminders << rem; } } QByteArray DefaultReminderAttribute::serialized() const { QVariantList list; list.reserve(m_reminders.count()); for (const ReminderPtr &rem : qAsConst(m_reminders)) { QVariantMap reminder; if (rem->type() == KCalendarCore::Alarm::Display) { reminder[QStringLiteral("type")] = QLatin1String("display"); } else if (rem->type() == KCalendarCore::Alarm::Email) { reminder[QStringLiteral("type")] = QLatin1String("email"); } reminder[QStringLiteral("time")] = rem->startOffset().asSeconds(); list << reminder; } QJsonDocument serialized = QJsonDocument::fromVariant(list); return serialized.toJson(); } KCalendarCore::Alarm::List DefaultReminderAttribute::alarms(KCalendarCore::Incidence *incidence) const { KCalendarCore::Alarm::List alarms; alarms.reserve(m_reminders.count()); for (const ReminderPtr &reminder : qAsConst(m_reminders)) { KCalendarCore::Alarm::Ptr alarm(new KCalendarCore::Alarm(incidence)); alarm->setType(reminder->type()); alarm->setTime(incidence->dtStart()); alarm->setStartOffset(reminder->startOffset()); alarm->setEnabled(true); alarms << alarm; } return alarms; } QByteArray DefaultReminderAttribute::type() const { static const QByteArray sType("defaultReminders"); return sType; } diff --git a/resources/google-groupware/generichandler.h b/resources/google-groupware/generichandler.h index 154fe85aa..e26e81d54 100644 --- a/resources/google-groupware/generichandler.h +++ b/resources/google-groupware/generichandler.h @@ -1,66 +1,66 @@ /* Copyright (C) 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #ifndef GENERICHANDLER_H #define GENERICHANDLER_H #include #include #include #include #include namespace KGAPI2 { class Job; } class GoogleResource; class GoogleSettings; class GenericHandler : public QObject { Q_OBJECT public: typedef QSharedPointer Ptr; GenericHandler(GoogleResource *resource, GoogleSettings *settings); virtual ~GenericHandler(); virtual QString mimetype() = 0; virtual bool canPerformTask(const Akonadi::Item &item) = 0; virtual void retrieveCollections() = 0; virtual void retrieveItems(const Akonadi::Collection &collection) = 0; virtual void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) = 0; virtual void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) = 0; virtual void itemsRemoved(const Akonadi::Item::List &items) = 0; virtual void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) = 0; virtual void itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); virtual void itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection); virtual void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) = 0; virtual void collectionChanged(const Akonadi::Collection &collection) = 0; virtual void collectionRemoved(const Akonadi::Collection &collection) = 0; protected: - GoogleResource* m_resource; - GoogleSettings* m_settings; + GoogleResource *m_resource = nullptr; + GoogleSettings *m_settings = nullptr; }; #endif // GENERICHANDLER_H diff --git a/resources/google-groupware/googlesettings.cpp b/resources/google-groupware/googlesettings.cpp index b272efa5e..9a23ce8fe 100644 --- a/resources/google-groupware/googlesettings.cpp +++ b/resources/google-groupware/googlesettings.cpp @@ -1,184 +1,183 @@ /* Copyright (C) 2011-2013 Dan Vratil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #include "googlesettings.h" #include "settingsbase.h" #include "googleresource_debug.h" #include #include #include using namespace KWallet; using namespace KGAPI2; static const QString googleWalletFolder = QStringLiteral("Akonadi Google"); GoogleSettings::GoogleSettings() : m_winId(0) - , m_isReady(false) { m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId, Wallet::Asynchronous); if (m_wallet) { connect(m_wallet.data(), &Wallet::walletOpened, this, &GoogleSettings::slotWalletOpened); } else { qCWarning(GOOGLE_LOG) << "Failed to open wallet!"; } } void GoogleSettings::slotWalletOpened(bool success) { if (!success) { qCWarning(GOOGLE_LOG) << "Failed to open wallet!"; Q_EMIT accountReady(false); return; } if (!m_wallet->hasFolder(googleWalletFolder) && !m_wallet->createFolder(googleWalletFolder)) { qCWarning(GOOGLE_LOG) << "Failed to create wallet folder" << googleWalletFolder; Q_EMIT accountReady(false); return; } if (!m_wallet->setFolder(googleWalletFolder)) { qWarning() << "Failed to open wallet folder" << googleWalletFolder; Q_EMIT accountReady(false); return; } qCDebug(GOOGLE_LOG) << "Wallet opened, reading" << account(); if (!account().isEmpty()) { m_account = fetchAccountFromWallet(account()); } m_isReady = true; Q_EMIT accountReady(true); } KGAPI2::AccountPtr GoogleSettings::fetchAccountFromWallet(const QString &accountName) { if (!m_wallet->entryList().contains(accountName)) { qCDebug(GOOGLE_LOG) << "Account" << accountName << "not found in KWallet"; return AccountPtr(); } QMap map; m_wallet->readMap(accountName, map); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) const QStringList scopes = map[QStringLiteral("scopes")].split(QLatin1Char(','), QString::SkipEmptyParts); #else const QStringList scopes = map[QStringLiteral("scopes")].split(QLatin1Char(','), Qt::SkipEmptyParts); #endif QList scopeUrls; scopeUrls.reserve(scopes.count()); for (const QString &scope : scopes) { scopeUrls << QUrl(scope); } AccountPtr account(new Account(accountName, map[QStringLiteral("accessToken")], map[QStringLiteral("refreshToken")], scopeUrls)); return account; } bool GoogleSettings::storeAccount(AccountPtr account) { // Removing the old one (if present) if (m_account && (account->accountName() != m_account->accountName())) { cleanup(); } // Populating the new one m_account = account; QStringList scopes; const QList urlScopes = m_account->scopes(); scopes.reserve(urlScopes.count()); for (const QUrl &url : urlScopes) { scopes << url.toString(); } QMap map; map[QStringLiteral("accessToken")] = m_account->accessToken(); map[QStringLiteral("refreshToken")] = m_account->refreshToken(); map[QStringLiteral("scopes")] = scopes.join(QLatin1Char(',')); // Removing previous junk (if present) cleanup(); if (m_wallet->writeMap(m_account->accountName(), map) != 0) { qCWarning(GOOGLE_LOG) << "Failed to write new account entry to wallet"; return false; } SettingsBase::setAccount(m_account->accountName()); m_isReady = true; return true; } void GoogleSettings::cleanup() { if (m_account && m_wallet) { m_wallet->removeEntry(m_account->accountName()); } } -void GoogleSettings::addCalendar(const QString& calendar) +void GoogleSettings::addCalendar(const QString &calendar) { if (calendars().isEmpty() || calendars().contains(calendar)) { return; } setCalendars(calendars() << calendar); save(); } -void GoogleSettings::addTaskList(const QString& taskList) +void GoogleSettings::addTaskList(const QString &taskList) { if (calendars().isEmpty() || taskLists().contains(taskList)) { return; } setTaskLists(taskLists() << taskList); save(); } QString GoogleSettings::clientId() const { return QStringLiteral("554041944266.apps.googleusercontent.com"); } QString GoogleSettings::clientSecret() const { return QStringLiteral("mdT1DjzohxN3npUUzkENT0gO"); } bool GoogleSettings::isReady() const { return m_isReady; } AccountPtr GoogleSettings::accountPtr() { return m_account; } void GoogleSettings::setWindowId(WId id) { m_winId = id; } void GoogleSettings::setResourceId(const QString &resourceIdentificator) { m_resourceId = resourceIdentificator; } diff --git a/resources/google-groupware/googlesettings.h b/resources/google-groupware/googlesettings.h index 1c9c890c0..fa3804943 100644 --- a/resources/google-groupware/googlesettings.h +++ b/resources/google-groupware/googlesettings.h @@ -1,77 +1,77 @@ /* Copyright (C) 2013 Daniel Vrátil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #ifndef GOOGLESETTINGS_H #define GOOGLESETTINGS_H #include "settingsbase.h" #include #include #include namespace KWallet { class Wallet; } /** * @brief Settings object * * Provides read-only access to application clientId and * clientSecret and read-write access to accessToken and * refreshToken. Interacts with KWallet. */ class GoogleSettings : public SettingsBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.Google.ExtendedSettings") public: GoogleSettings(); void setWindowId(WId id); void setResourceId(const QString &resourceIdentifier); QString appId() const; QString clientId() const; QString clientSecret() const; - void addCalendar(const QString& calendar); - void addTaskList(const QString& taskList); + void addCalendar(const QString &calendar); + void addTaskList(const QString &taskList); KGAPI2::AccountPtr accountPtr(); // Wallet bool isReady() const; bool storeAccount(KGAPI2::AccountPtr account); void cleanup(); Q_SIGNALS: void accountReady(bool ready); void accountChanged(); private Q_SLOTS: void slotWalletOpened(bool success); private: WId m_winId; QString m_resourceId; - bool m_isReady; + bool m_isReady = false; KGAPI2::AccountPtr m_account; QPointer m_wallet; KGAPI2::AccountPtr fetchAccountFromWallet(const QString &accountName); }; #endif // GOOGLESETTINGS_H diff --git a/resources/google-groupware/googlesettingsdialog.cpp b/resources/google-groupware/googlesettingsdialog.cpp index 7d16c6d97..51414c9e5 100644 --- a/resources/google-groupware/googlesettingsdialog.cpp +++ b/resources/google-groupware/googlesettingsdialog.cpp @@ -1,293 +1,294 @@ /* Copyright (C) 2013 Daniel Vrátil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #include "googlesettingsdialog.h" #include "ui_googlesettingsdialog.h" #include "googlesettings.h" #include "googleresource.h" #include "googleresource_debug.h" #include #include #include #include #include #include #include #include #include using namespace KGAPI2; GoogleSettingsDialog::GoogleSettingsDialog(GoogleResource *resource, GoogleSettings *settings, WId wId) : QDialog() , m_resource(resource) , m_settings(settings) { if (wId) { setAttribute(Qt::WA_NativeWindow, true); KWindowSystem::setMainWindow(windowHandle(), wId); } QVBoxLayout *mainLayout = new QVBoxLayout(this); QWidget *mainWidget = new QWidget(this); mainLayout->addWidget(mainWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); mainLayout->addWidget(buttonBox); m_ui = new Ui::GoogleSettingsDialog; m_ui->setupUi(mainWidget); m_ui->refreshSpinBox->setSuffix(ki18np(" minute", " minutes")); m_ui->enableRefresh->setChecked(m_settings->enableIntervalCheck()); m_ui->refreshSpinBox->setEnabled(m_settings->enableIntervalCheck()); if (m_settings->enableIntervalCheck()) { m_ui->refreshSpinBox->setValue(m_settings->intervalCheckTime()); } else { m_ui->refreshSpinBox->setValue(30); } m_ui->eventsLimitCombo->setMaximumDate(QDate::currentDate()); m_ui->eventsLimitCombo->setMinimumDate(QDate::fromString(QStringLiteral("2000-01-01"), Qt::ISODate)); m_ui->eventsLimitCombo->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate |KDateComboBox::DatePicker | KDateComboBox::WarnOnInvalid); if (m_settings->eventsSince().isEmpty()) { const QString ds = QStringLiteral("%1-01-01").arg(QString::number(QDate::currentDate().year() - 3)); m_ui->eventsLimitCombo->setDate(QDate::fromString(ds, Qt::ISODate)); } else { m_ui->eventsLimitCombo->setDate(QDate::fromString(m_settings->eventsSince(), Qt::ISODate)); } connect(buttonBox, &QDialogButtonBox::accepted, this, &GoogleSettingsDialog::slotSaveSettings); connect(buttonBox, &QDialogButtonBox::rejected, this, &GoogleSettingsDialog::reject); connect(m_ui->reloadCalendarsBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotReloadCalendars); connect(m_ui->reloadTaskListsBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotReloadTaskLists); connect(m_ui->configureBtn, &QPushButton::clicked, this, &GoogleSettingsDialog::slotConfigure); if (m_settings->isReady()) { m_account = m_settings->accountPtr(); } connect(m_settings, &GoogleSettings::accountReady, this, [this](bool ready){ if (ready) { m_account = m_settings->accountPtr(); accountChanged(); } }); QMetaObject::invokeMethod(this, &GoogleSettingsDialog::accountChanged, Qt::QueuedConnection); } GoogleSettingsDialog::~GoogleSettingsDialog() { + delete m_ui; } bool GoogleSettingsDialog::handleError(Job *job) { if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) { return true; } if (job->error() == KGAPI2::Unauthorized) { qCDebug(GOOGLE_LOG) << job << job->errorString(); const QList resourceScopes = m_resource->scopes(); for (const QUrl &scope : resourceScopes) { if (!m_account->scopes().contains(scope)) { m_account->addScope(scope); } } AuthJob *authJob = new AuthJob(m_account, m_settings->clientId(), m_settings->clientSecret(), this); authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job)); connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAuthJobFinished); return false; } KMessageBox::sorry(this, job->errorString()); return false; } void GoogleSettingsDialog::accountChanged() { if (!m_account) { m_ui->accountLabel->setText(i18n("not configured")); m_ui->calendarsBox->setDisabled(true); m_ui->calendarsList->clear(); m_ui->taskListsBox->setDisabled(true); m_ui->taskListsList->clear(); return; } m_ui->accountLabel->setText(QStringLiteral("%1").arg(m_account->accountName())); slotReloadCalendars(); slotReloadTaskLists(); } void GoogleSettingsDialog::slotConfigure() { m_account = AccountPtr(new Account()); const QList resourceScopes = m_resource->scopes(); for (const QUrl &scope : resourceScopes) { if (!m_account->scopes().contains(scope)) { m_account->addScope(scope); } } AuthJob *authJob = new AuthJob(m_account, m_settings->clientId(), m_settings->clientSecret()); connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAuthJobFinished); } -void GoogleSettingsDialog::slotAuthJobFinished(Job* job) +void GoogleSettingsDialog::slotAuthJobFinished(Job *job) { auto authJob = qobject_cast(job); m_account = authJob->account(); if (authJob->error() != KGAPI2::NoError) { KMessageBox::sorry(this, authJob->errorString()); return; } accountChanged(); auto otherJob = job->property(JOB_PROPERTY).value(); if (otherJob) { otherJob->setAccount(m_account); otherJob->restart(); } } void GoogleSettingsDialog::slotSaveSettings() { if (!m_account || !m_settings->storeAccount(m_account)) { m_settings->setAccount(QString()); m_settings->setEnableIntervalCheck(m_ui->enableRefresh->isChecked()); m_settings->setIntervalCheckTime(m_ui->refreshSpinBox->value()); m_settings->setCalendars({}); m_settings->setTaskLists({}); m_settings->setEventsSince(QString()); m_settings->save(); return; } m_settings->setAccount(m_account->accountName()); m_settings->setEnableIntervalCheck(m_ui->enableRefresh->isChecked()); m_settings->setIntervalCheckTime(m_ui->refreshSpinBox->value()); QStringList calendars; for (int i = 0; i < m_ui->calendarsList->count(); i++) { QListWidgetItem *item = m_ui->calendarsList->item(i); if (item->checkState() == Qt::Checked) { calendars.append(item->data(Qt::UserRole).toString()); } } m_settings->setCalendars(calendars); if (m_ui->eventsLimitCombo->isValid()) { m_settings->setEventsSince(m_ui->eventsLimitCombo->date().toString(Qt::ISODate)); } QStringList taskLists; for (int i = 0; i < m_ui->taskListsList->count(); i++) { QListWidgetItem *item = m_ui->taskListsList->item(i); if (item->checkState() == Qt::Checked) { taskLists.append(item->data(Qt::UserRole).toString()); } } m_settings->setTaskLists(taskLists); m_settings->save(); accept(); } void GoogleSettingsDialog::slotReloadCalendars() { m_ui->calendarsBox->setDisabled(true); m_ui->calendarsList->clear(); if (!m_account) { return; } auto fetchJob = new CalendarFetchJob(m_account, this); - connect(fetchJob, &CalendarFetchJob::finished, this, [this](Job* job){ + connect(fetchJob, &CalendarFetchJob::finished, this, [this](Job *job){ if (!handleError(job) || !m_account) { m_ui->calendarsBox->setEnabled(false); return; } const ObjectsList objects = qobject_cast(job)->items(); QStringList activeCalendars; if (m_account->accountName() == m_settings->account()) { activeCalendars = m_settings->calendars(); } m_ui->calendarsList->clear(); for (const ObjectPtr &object : objects) { const CalendarPtr calendar = object.dynamicCast(); QListWidgetItem *item = new QListWidgetItem(calendar->title()); item->setData(Qt::UserRole, calendar->uid()); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState((activeCalendars.isEmpty() || activeCalendars.contains(calendar->uid())) ? Qt::Checked : Qt::Unchecked); m_ui->calendarsList->addItem(item); } m_ui->calendarsBox->setEnabled(true); }); } void GoogleSettingsDialog::slotReloadTaskLists() { if (!m_account) { return; } m_ui->taskListsBox->setDisabled(true); m_ui->taskListsList->clear(); auto job = new TaskListFetchJob(m_account, this); connect(job, &TaskListFetchJob::finished, this, [this](KGAPI2::Job *job){ if (!handleError(job) || !m_account) { m_ui->taskListsBox->setDisabled(true); return; } const ObjectsList objects = qobject_cast(job)->items(); QStringList activeTaskLists; if (m_account->accountName() == m_settings->account()) { activeTaskLists = m_settings->taskLists(); } m_ui->taskListsList->clear(); for (const ObjectPtr &object : objects) { const TaskListPtr taskList = object.dynamicCast(); QListWidgetItem *item = new QListWidgetItem(taskList->title()); item->setData(Qt::UserRole, taskList->uid()); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState((activeTaskLists.isEmpty() || activeTaskLists.contains(taskList->uid())) ? Qt::Checked : Qt::Unchecked); m_ui->taskListsList->addItem(item); } m_ui->taskListsBox->setEnabled(true); }); } diff --git a/resources/google-groupware/googlesettingsdialog.ui b/resources/google-groupware/googlesettingsdialog.ui index 265f59bdb..832a32d7c 100644 --- a/resources/google-groupware/googlesettingsdialog.ui +++ b/resources/google-groupware/googlesettingsdialog.ui @@ -1,185 +1,182 @@ GoogleSettingsDialog 0 0 584 680 - - Form - Account: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter <b>not confgured</b> Configure... Refresh false Refresh interval: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter refreshSpinBox 10 720 30 Enable interval refresh Calendars Fetch only events since Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter eventsLimitCombo Reload .. Tasklists Reload .. KPluralHandlingSpinBox QSpinBox
kpluralhandlingspinbox.h
KDateComboBox QComboBox
kdatecombobox.h
enableRefresh toggled(bool) refreshSpinBox setEnabled(bool) 182 99 312 128
diff --git a/resources/google-groupware/taskhandler.cpp b/resources/google-groupware/taskhandler.cpp index 9e91d5809..f099e3763 100644 --- a/resources/google-groupware/taskhandler.cpp +++ b/resources/google-groupware/taskhandler.cpp @@ -1,359 +1,359 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 2020 Igor Poboiko 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 3 of the License, or (at your option) any later version. 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, see . */ #include "taskhandler.h" #include "googleresource.h" #include "googlesettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googletasks_debug.h" #define TASK_PROPERTY "_KGAPI2::TaskPtr" using namespace KGAPI2; using namespace Akonadi; QString TaskHandler::mimetype() { return KCalendarCore::Todo::todoMimeType(); } bool TaskHandler::canPerformTask(const Item &item) { return m_resource->canPerformTask(item, mimetype()); } -void TaskHandler::setupCollection(Collection& collection, const TaskListPtr& taskList) +void TaskHandler::setupCollection(Collection &collection, const TaskListPtr &taskList) { collection.setContentMimeTypes({ mimetype() }); collection.setName(taskList->uid()); collection.setParentCollection(m_resource->rootCollection()); collection.setRemoteId(taskList->uid()); collection.setRights(Collection::CanChangeCollection |Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); EntityDisplayAttribute *attr = collection.attribute(Collection::AddIfMissing); attr->setDisplayName(taskList->title()); attr->setIconName(QStringLiteral("view-pim-tasks")); } void TaskHandler::retrieveCollections() { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving task lists")); qCDebug(GOOGLE_TASKS_LOG) << "Retrieving tasks..."; auto job = new TaskListFetchJob(m_settings->accountPtr(), this); connect(job, &TaskListFetchJob::finished, this, &TaskHandler::slotCollectionsRetrieved); } -void TaskHandler::slotCollectionsRetrieved(KGAPI2::Job* job) +void TaskHandler::slotCollectionsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_TASKS_LOG) << "Task lists retrieved"; const ObjectsList taskLists = qobject_cast(job)->items(); const QStringList activeTaskLists = m_settings->taskLists(); Collection::List collections; for (const ObjectPtr &object : taskLists) { const TaskListPtr &taskList = object.dynamicCast(); qCDebug(GOOGLE_TASKS_LOG) << " -" << taskList->title() << "(" << taskList->uid() << ")"; if (!activeTaskLists.contains(taskList->uid())) { qCDebug(GOOGLE_TASKS_LOG) << "Skipping, not subscribed"; continue; } Collection collection; setupCollection(collection, taskList); collections << collection; } m_resource->collectionsRetrievedFromHandler(collections); } void TaskHandler::retrieveItems(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Retrieving tasks for list '%1'", collection.displayName())); qCDebug(GOOGLE_TASKS_LOG) << "Retrieving tasks for list" << collection.remoteId(); // https://bugs.kde.org/show_bug.cgi?id=308122: we can only request changes in // max. last 25 days, otherwise we get an error. int lastSyncDelta = -1; if (!collection.remoteRevision().isEmpty()) { lastSyncDelta = QDateTime::currentDateTimeUtc().toSecsSinceEpoch() - collection.remoteRevision().toULongLong(); } auto job = new TaskFetchJob(collection.remoteId(), m_settings->accountPtr(), this); if (lastSyncDelta > -1 && lastSyncDelta < 25 * 25 * 3600) { job->setFetchOnlyUpdated(collection.remoteRevision().toULongLong()); job->setFetchDeleted(true); } else { // No need to fetch deleted items for non-incremental update job->setFetchDeleted(false); } job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &TaskFetchJob::finished, this, &TaskHandler::slotItemsRetrieved); } void TaskHandler::slotItemsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } Item::List changedItems, removedItems; - const ObjectsList& objects = qobject_cast(job)->items(); + const ObjectsList &objects = qobject_cast(job)->items(); Collection collection = job->property(COLLECTION_PROPERTY).value(); bool isIncremental = (qobject_cast(job)->fetchOnlyUpdated() > 0); qCDebug(GOOGLE_TASKS_LOG) << "Retrieved" << objects.count() << "tasks for list" << collection.remoteId(); for (const auto &object : objects) { const TaskPtr task = object.dynamicCast(); Item item; item.setMimeType(KCalendarCore::Todo::todoMimeType()); item.setParentCollection(collection); item.setRemoteId(task->uid()); item.setRemoteRevision(task->etag()); item.setPayload(task.dynamicCast()); if (task->deleted()) { qCDebug(GOOGLE_TASKS_LOG) << " - removed" << task->uid(); removedItems << item; } else { qCDebug(GOOGLE_TASKS_LOG) << " - changed" << task->uid(); changedItems << item; } } if (isIncremental) { m_resource->itemsRetrievedIncremental(changedItems, removedItems); } else { m_resource->itemsRetrieved(changedItems); } const QDateTime local(QDateTime::currentDateTime()); const QDateTime UTC(local.toUTC()); collection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); new CollectionModifyJob(collection, this); m_resource->emitReadyStatus(); } void TaskHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.displayName())); KCalendarCore::Todo::Ptr todo = item.payload(); TaskPtr task(new Task(*todo)); const QString parentRemoteId = task->relatedTo(KCalendarCore::Incidence::RelTypeParent); qCDebug(GOOGLE_TASKS_LOG) << "Task added to list" << collection.remoteId() << "with parent" << parentRemoteId; auto job = new TaskCreateJob(task, item.parentCollection().remoteId(), m_settings->accountPtr(), this); job->setParentItem(parentRemoteId); connect(job, &TaskCreateJob::finished, this, [this, item](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } Item newItem = item; const TaskPtr task = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_TASKS_LOG) << "Task added"; newItem.setRemoteId(task->uid()); newItem.setRemoteRevision(task->etag()); newItem.setGid(task->uid()); m_resource->changeCommitted(newItem); newItem.setPayload(task.dynamicCast()); new ItemModifyJob(newItem, this); m_resource->emitReadyStatus(); }); } void TaskHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing task in list '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_TASKS_LOG) << "Changing task" << item.remoteId(); KCalendarCore::Todo::Ptr todo = item.payload(); const QString parentUid = todo->relatedTo(KCalendarCore::Incidence::RelTypeParent); // First we move it to a new parent, if there is auto job = new TaskMoveJob(item.remoteId(), item.parentCollection().remoteId(), parentUid, m_settings->accountPtr(), this); - connect(job, &TaskMoveJob::finished, this, [this, todo, item](KGAPI2::Job* job){ + connect(job, &TaskMoveJob::finished, this, [this, todo, item](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } TaskPtr task(new Task(*todo)); auto newJob = new TaskModifyJob(task, item.parentCollection().remoteId(), job->account(), this); newJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(newJob, &TaskModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); }); } void TaskHandler::itemsRemoved(const Item::List &items) { Q_EMIT m_resource->status(AgentBase::Running, i18ncp("@info:status", "Removing %1 tasks", "Removing %1 task", items.count())); qCDebug(GOOGLE_TASKS_LOG) << "Removing" << items.count() << "tasks"; /* Google always automatically removes tasks with all their subtasks. In KOrganizer * by default we only remove the item we are given. For this reason we have to first * fetch all tasks, find all sub-tasks for the task being removed and detach them * from the task. Only then the task can be safely removed. */ // TODO: what if items belong to different collections? auto job = new ItemFetchJob(items.first().parentCollection()); job->fetchScope().fetchFullPayload(true); connect(job, &ItemFetchJob::finished, this, [this, items](KJob *job){ if (job->error()) { m_resource->cancelTask(i18n("Failed to delete task: %1", job->errorString())); return; } const Item::List fetchedItems = qobject_cast(job)->items(); Item::List detachItems; TasksList detachTasks; for (const Item &fetchedItem : fetchedItems) { auto todo = fetchedItem.payload(); TaskPtr task(new Task(*todo)); const QString parentId = task->relatedTo(KCalendarCore::Incidence::RelTypeParent); if (parentId.isEmpty()) { continue; } auto it = std::find_if(items.cbegin(), items.cend(), [&parentId](const Item &item){ return item.remoteId() == parentId; }); if (it != items.cend()) { Item newItem(fetchedItem); qCDebug(GOOGLE_TASKS_LOG) << "Detaching child" << newItem.remoteId() << "from" << parentId; todo->setRelatedTo(QString(), KCalendarCore::Incidence::RelTypeParent); newItem.setPayload(todo); detachItems << newItem; detachTasks << task; } } /* If there are no items do detach, then delete the task right now */ if (detachItems.isEmpty()) { doRemoveTasks(items); return; } qCDebug(GOOGLE_TASKS_LOG) << "Reparenting" << detachItems.count() << "children..."; auto moveJob = new TaskMoveJob(detachTasks, items.first().parentCollection().remoteId(), QString(), m_settings->accountPtr(), this); connect(moveJob, &TaskMoveJob::finished, this, [this, items, detachItems](KGAPI2::Job *job){ if (job->error()) { m_resource->cancelTask(i18n("Failed to reparent subtasks: %1", job->errorString())); return; } // Update items inside Akonadi DB too new ItemModifyJob(detachItems); // Perform actual removal doRemoveTasks(items); }); }); } void TaskHandler::doRemoveTasks(const Item::List &items) { // Make sure account is still valid if (!m_resource->canPerformTask()) { return; } QStringList taskIds; taskIds.reserve(items.count()); std::transform(items.cbegin(), items.cend(), std::back_inserter(taskIds), [](const Item &item){ return item.remoteId(); }); /* Now finally we can safely remove the task we wanted to */ // TODO: what if tasks are deleted from different collections? auto job = new TaskDeleteJob(taskIds, items.first().parentCollection().remoteId(), m_settings->accountPtr(), this); job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items)); connect(job, &TaskDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void TaskHandler::itemsMoved(const Item::List &/*item*/, const Collection &/*collectionSource*/, const Collection &/*collectionDestination*/) { m_resource->cancelTask(i18n("Moving tasks between task lists is not supported")); } void TaskHandler::collectionAdded(const Collection &collection, const Collection &/*parent*/) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Creating new task list '%1'", collection.displayName())); qCDebug(GOOGLE_TASKS_LOG) << "Adding task list" << collection.displayName(); TaskListPtr taskList(new TaskList()); taskList->setTitle(collection.displayName()); auto job = new TaskListCreateJob(taskList, m_settings->accountPtr(), this); - connect(job, &TaskListCreateJob::finished, this, [this, collection](KGAPI2::Job* job){ + connect(job, &TaskListCreateJob::finished, this, [this, collection](KGAPI2::Job *job){ if (!m_resource->handleError(job)) { return; } TaskListPtr taskList = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_TASKS_LOG) << "Task list created:" << taskList->uid(); // Enable newly added task list in settings m_settings->addTaskList(taskList->uid()); // Populate remoteId & other stuff Collection newCollection(collection); setupCollection(newCollection, taskList); m_resource->changeCommitted(newCollection); m_resource->emitReadyStatus(); }); } void TaskHandler::collectionChanged(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Changing task list '%1'", collection.displayName())); qCDebug(GOOGLE_TASKS_LOG) << "Changing task list" << collection.remoteId(); TaskListPtr taskList(new TaskList()); taskList->setUid(collection.remoteId()); taskList->setTitle(collection.displayName()); auto job = new TaskListModifyJob(taskList, m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &TaskListModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void TaskHandler::collectionRemoved(const Collection &collection) { Q_EMIT m_resource->status(AgentBase::Running, i18nc("@info:status", "Removing task list '%1'", collection.displayName())); qCDebug(GOOGLE_TASKS_LOG) << "Removing task list" << collection.remoteId(); auto job = new TaskListDeleteJob(collection.remoteId(), m_settings->accountPtr(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &TaskListDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } diff --git a/resources/google-groupware/taskhandler.h b/resources/google-groupware/taskhandler.h index 9a89b6926..d31aaa311 100644 --- a/resources/google-groupware/taskhandler.h +++ b/resources/google-groupware/taskhandler.h @@ -1,53 +1,53 @@ /* Copyright (C) 2011-2013 Daniel Vrátil 2020 Igor Pobiko 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 3 of the License, or (at your option) any later version. 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, see . */ #ifndef TASKHANDLER_H #define TASKHANDLER_H #include "generichandler.h" #include class TaskHandler : public GenericHandler { public: using GenericHandler::GenericHandler; QString mimetype() override; bool canPerformTask(const Akonadi::Item &item) override; void retrieveCollections() override; void retrieveItems(const Akonadi::Collection &collection) override; void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override; void itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) override; void itemsRemoved(const Akonadi::Item::List &items) override; void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) override; void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override; void collectionChanged(const Akonadi::Collection &collection) override; void collectionRemoved(const Akonadi::Collection &collection) override; private Q_SLOTS: - void slotCollectionsRetrieved(KGAPI2::Job* job); - void slotItemsRetrieved(KGAPI2::Job* job); + void slotCollectionsRetrieved(KGAPI2::Job *job); + void slotItemsRetrieved(KGAPI2::Job *job); private: void setupCollection(Akonadi::Collection &colleciton, const KGAPI2::TaskListPtr &taskList); void doRemoveTasks(const Akonadi::Item::List &items); }; #endif // TASKHANDLER_H