diff --git a/resources/google-new/CMakeLists.txt b/resources/google-new/CMakeLists.txt index 71676f446..eaa8ed31c 100644 --- a/resources/google-new/CMakeLists.txt +++ b/resources/google-new/CMakeLists.txt @@ -1,76 +1,75 @@ add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_google_resource\") set(googleresource_SRCS googleresource.cpp - settings.cpp settingsdialog.cpp googleaccountmanager.cpp googlesettings.cpp googlesettingsdialog.cpp kgapiversionattribute.cpp defaultreminderattribute.cpp generichandler.cpp calendarhandler.cpp taskhandler.cpp contacthandler.cpp ${accounts_SRCS}) if (ECM_VERSION VERSION_LESS "5.68.0") ecm_qt_declare_logging_category(googleresource_SRCS HEADER googleresource_debug.h IDENTIFIER GOOGLE_LOG CATEGORY_NAME org.kde.pim.google) else() ecm_qt_declare_logging_category(googleresource_SRCS HEADER googleresource_debug.h IDENTIFIER GOOGLE_LOG CATEGORY_NAME org.kde.pim.google DESCRIPTION "resource google (kdepim-runtime)" EXPORT KDEPIMRUNTIME ) endif() kconfig_add_kcfg_files(googleresource_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfgc) kcfg_generate_dbus_interface( ${CMAKE_CURRENT_SOURCE_DIR}/settingsbase.kcfg org.kde.Akonadi.Google.Settings ) qt5_add_dbus_adaptor(googleresource_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.Google.Settings.xml - ${CMAKE_CURRENT_SOURCE_DIR}/settings.h Settings + ${CMAKE_CURRENT_SOURCE_DIR}/googlesettings.h GoogleSettings ) add_executable(akonadi_google_resource ${googleresource_SRCS}) if( APPLE ) set_target_properties(akonadi_google_resource PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../../Info.plist.template ) set_target_properties(akonadi_google_resource PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.google" ) set_target_properties(akonadi_google_resource PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Google Resource" ) endif() target_link_libraries(akonadi_google_resource KF5::AkonadiCalendar KF5::AkonadiCore KF5::AkonadiAgentBase KF5::CalendarCore KF5::Contacts KF5::Wallet KF5::I18n KF5::WindowSystem KF5::Completion KF5::TextWidgets KPim::GAPICalendar KPim::GAPIContacts KPim::GAPICore KPim::GAPITasks ) install(TARGETS akonadi_google_resource ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES googleresource.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) diff --git a/resources/google-new/calendarhandler.cpp b/resources/google-new/calendarhandler.cpp index a9e52d49f..522a93ffb 100644 --- a/resources/google-new/calendarhandler.cpp +++ b/resources/google-new/calendarhandler.cpp @@ -1,377 +1,377 @@ /* 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 "defaultreminderattribute.h" #include "calendarhandler.h" +#include "defaultreminderattribute.h" #include "googleresource.h" +#include "googlesettings.h" #include "kgapiversionattribute.h" -#include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googleresource_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 Akonadi::Item &item) { return m_resource->canPerformTask(item, mimetype()); } void CalendarHandler::retrieveCollections() { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving calendars")); qCDebug(GOOGLE_LOG) << "Retrieving calendars..."; auto job = new CalendarFetchJob(m_resource->account(), this); connect(job, &KGAPI2::Job::finished, this, &CalendarHandler::slotCollectionsRetrieved); } void CalendarHandler::slotCollectionsRetrieved(KGAPI2::Job* job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Calendars retrieved"; const ObjectsList calendars = qobject_cast(job)->items(); Collection::List collections; - const QStringList activeCalendars = Settings::self()->calendars(); + const QStringList activeCalendars = GoogleSettings::self()->calendars(); for (const auto &object : calendars) { const CalendarPtr &calendar = object.dynamicCast(); qCDebug(GOOGLE_LOG) << "Retrieved calendar:" << calendar->title() << "(" << calendar->uid() << ")"; if (!activeCalendars.contains(calendar->uid())) { continue; } Collection collection; collection.setContentMimeTypes(QStringList() << mimetype()); collection.setName(calendar->uid()); collection.setParentCollection(m_resource->rootCollection()); collection.setRemoteId(calendar->uid()); if (calendar->editable()) { collection.setRights(Collection::CanChangeCollection |Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); } else { collection.setRights(Collection::ReadOnly); } EntityDisplayAttribute *attr = collection.attribute(Collection::AddIfMissing); attr->setDisplayName(calendar->title()); attr->setIconName(QStringLiteral("view-calendar")); auto colorAttr = collection.attribute(Collection::AddIfMissing); colorAttr->setColor(calendar->backgroundColor()); DefaultReminderAttribute *reminderAttr = collection.attribute(Collection::AddIfMissing); reminderAttr->setReminders(calendar->defaultReminders()); // Block email reminders, since Google sends them for us BlockAlarmsAttribute *blockAlarms = collection.attribute(Collection::AddIfMissing); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Audio, false); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Display, false); blockAlarms->blockAlarmType(KCalendarCore::Alarm::Procedure, false); collections << collection; } Q_EMIT collectionsRetrieved(collections); } void CalendarHandler::retrieveItems(const Collection &collection) { qCDebug(GOOGLE_LOG) << "Retrieving events for calendar" << 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(); } if (!collection.hasAttribute() || collection.attribute()->version() != KGAPIEventVersion) { lastSyncDelta = -1; } auto job = new EventFetchJob(collection.remoteId(), m_resource->account(), this); if (lastSyncDelta > -1 && lastSyncDelta < 25 * 24 * 3600) { job->setFetchOnlyUpdated(collection.remoteRevision().toULongLong()); } - if (!Settings::self()->eventsSince().isEmpty()) { - const QDate date = QDate::fromString(Settings::self()->eventsSince(), Qt::ISODate); + if (!GoogleSettings::self()->eventsSince().isEmpty()) { + const QDate date = QDate::fromString(GoogleSettings::self()->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, &KGAPI2::Job::finished, this, &CalendarHandler::slotItemsRetrieved); Q_EMIT 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->fetchOnlyUpdated() > 0); qCDebug(GOOGLE_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_LOG) << " - removed" << event->uid(); removedItems << item; } else { qCDebug(GOOGLE_LOG) << " - changed" << event->uid(); changedItems << item; } } if (!isIncremental) { collection.attribute(Collection::AddIfMissing)->setVersion(KGAPIEventVersion); m_resource->itemsRetrieved(changedItems); } else { m_resource->itemsRetrievedIncremental(changedItems, removedItems); } const QDateTime local(QDateTime::currentDateTime()); const QDateTime UTC(local.toUTC()); collection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); new CollectionModifyJob(collection, this); emitReadyStatus(); } void CalendarHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.name())); qCDebug(GOOGLE_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_resource->account(), this); job->setSendUpdates(SendUpdatesPolicy::None); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &KGAPI2::Job::finished, this, &CalendarHandler::slotCreateJobFinished); } void CalendarHandler::slotCreateJobFinished(KGAPI2::Job* job) { if (!m_resource->handleError(job)) { return; } Item item = job->property(ITEM_PROPERTY).value(); ObjectsList objects = qobject_cast(job)->items(); Q_ASSERT(objects.count() > 0); EventPtr event = objects.first().dynamicCast(); qCDebug(GOOGLE_LOG) << "Event added"; item.setRemoteId(event->id()); item.setRemoteRevision(event->etag()); item.setGid(event->uid()); m_resource->changeCommitted(item); item.setPayload(event.dynamicCast()); new ItemModifyJob(item, this); emitReadyStatus(); } void CalendarHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing event in calendar '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_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_resource->account(), this); job->setSendUpdates(SendUpdatesPolicy::None); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &EventModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::itemRemoved(const Item &item) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing event from calendar '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_LOG) << "Removing event" << item.remoteId(); KGAPI2::Job *job = new EventDeleteJob(item.remoteId(), item.parentCollection().remoteId(), m_resource->account(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &EventDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::itemMoved(const Item &item, const Collection &collectionSource, const Collection &collectionDestination) { qCDebug(GOOGLE_LOG) << "Moving" << item.remoteId() << "from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); KGAPI2::Job *job = new EventMoveJob(item.remoteId(), collectionSource.remoteId(), collectionDestination.remoteId(), m_resource->account(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &EventMoveJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Moving event from calendar '%1' to calendar '%2'", collectionSource.displayName(), collectionDestination.displayName())); } void CalendarHandler::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(parent); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Creating calendar '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Adding calendar" << collection.displayName(); CalendarPtr calendar(new Calendar()); calendar->setTitle(collection.displayName()); calendar->setEditable(true); auto job = new CalendarCreateJob(calendar, m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::collectionChanged(const Akonadi::Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing calendar '%1'", collection.displayName())); qCDebug(GOOGLE_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_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void CalendarHandler::collectionRemoved(const Akonadi::Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing calendar '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Removing calendar" << collection.remoteId(); auto job = new CalendarDeleteJob(collection.remoteId(), m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::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_resource->account(), const_cast(this)); connect(job, &KGAPI2::Job::finished, [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_resource->account(), this); connect(job, &KGAPI2::Job::finished, [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(m_resource->account()->accountName()); fb->addAttendee(KCalendarCore::Attendee(QString(), queryJob->id())); // FIXME: is it really sort? fb->setDateTime(QDateTime::currentDateTimeUtc(), KCalendarCore::IncidenceBase::RoleSort); for (const auto range : queryJob->busy()) { 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-new/contacthandler.cpp b/resources/google-new/contacthandler.cpp index 3a5016203..b67b12b28 100644 --- a/resources/google-new/contacthandler.cpp +++ b/resources/google-new/contacthandler.cpp @@ -1,455 +1,454 @@ /* 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 "kgapiversionattribute.h" -#include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googleresource_debug.h" #define ALLCONTACTS_REMOTEID QStringLiteral("AllContacts") #define OTHERCONTACTS_REMOTEID QStringLiteral("OtherContacts") #define MODIFIED_PROPERTY "modifiedItems" 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() { return QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/6").arg(QString::fromLatin1(QUrl::toPercentEncoding(m_resource->account()->accountName()))); } void ContactHandler::retrieveCollections() { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts groups")); qCDebug(GOOGLE_LOG) << "Retrieving contacts groups..."; m_allCollection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); m_allCollection.setName(i18n("All Contacts")); m_allCollection.setParentCollection(m_resource->rootCollection()); m_allCollection.setRemoteId(ALLCONTACTS_REMOTEID); m_allCollection.attribute(Collection::AddIfMissing); m_allCollection.setRights(Collection::ReadOnly); m_collections[ ALLCONTACTS_REMOTEID ] = m_allCollection; 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); otherCollection.setVirtual(true); auto attr = otherCollection.attribute(Collection::AddIfMissing); attr->setDisplayName(i18n("Other Contacts")); attr->setIconName(QStringLiteral("view-pim-contacts")); m_collections[ OTHERCONTACTS_REMOTEID ] = otherCollection; auto job = new ContactsGroupFetchJob(m_resource->account(), this); connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotCollectionsRetrieved); } void ContactHandler::slotCollectionsRetrieved(KGAPI2::Job* job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Contacts groups retrieved"; const ObjectsList objects = qobject_cast(job)->items(); for (const auto &object : objects) { const ContactsGroupPtr group = object.dynamicCast(); qCDebug(GOOGLE_LOG) << "Retrieved contact group:" << group->id() << "(" << group->title() << ")"; 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"); } } Collection collection; collection.setContentMimeTypes({ KContacts::Addressee::mimeType() }); collection.setName(group->id()); collection.setParentCollection(m_resource->rootCollection()); collection.setRights(Collection::CanLinkItem |Collection::CanUnlinkItem |Collection::CanChangeItem); if (!group->isSystemGroup()) { collection.setRights(collection.rights() |Collection::CanChangeCollection |Collection::CanDeleteCollection); } if (group->id() == myContactsRemoteId()) { collection.setRights(collection.rights() |Collection::CanCreateItem |Collection::CanChangeItem |Collection::CanDeleteItem); } collection.setRemoteId(group->id()); collection.setVirtual(true); auto attr = collection.attribute(Collection::AddIfMissing); attr->setDisplayName(realName); attr->setIconName(QStringLiteral("view-pim-contacts")); m_collections[ collection.remoteId() ] = collection; } Q_EMIT collectionsRetrieved(valuesToVector(m_collections)); emitReadyStatus(); } void ContactHandler::retrieveItems(const Collection &collection) { // All items are stored in "Everything" collection if (collection.remoteId() != ALLCONTACTS_REMOTEID) { m_resource->itemsRetrievalDone(); return; } Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts")); qCDebug(GOOGLE_LOG) << "Retreiving contacts..."; auto job = new ContactFetchJob(m_resource->account(), this); job->setFetchDeleted(true); if (!collection.remoteRevision().isEmpty()) { job->setFetchOnlyUpdated(collection.remoteRevision().toLongLong()); } connect(job, &ContactFetchJob::finished, this, &ContactHandler::slotItemsRetrieved); } void ContactHandler::slotItemsRetrieved(KGAPI2::Job *job) { if (!m_resource->handleError(job)) { return; } Item::List changedItems, removedItems; QMap groupsMap; QList changedPhotos; auto fetchJob = qobject_cast(job); bool isIncremental = (fetchJob->fetchOnlyUpdated() > 0); const ObjectsList objects = fetchJob->items(); qCDebug(GOOGLE_LOG) << "Retrieved" << objects.count() << "contacts"; for (const ObjectPtr &object : objects) { const ContactPtr contact = object.dynamicCast(); Item item; item.setMimeType(KContacts::Addressee::mimeType()); item.setParentCollection(m_allCollection); item.setRemoteId(contact->uid()); item.setRemoteRevision(contact->etag()); item.setPayload(*contact.dynamicCast()); if (contact->deleted()) { qCDebug(GOOGLE_LOG) << " - removed" << contact->uid(); removedItems << item; } else { qCDebug(GOOGLE_LOG) << " - changed" << contact->uid(); changedItems << item; changedPhotos << contact->uid(); } const QStringList groups = contact->groups(); for (const QString &group : groups) { groupsMap[group] << item; } if (contact->groups().isEmpty()) { groupsMap[OTHERCONTACTS_REMOTEID] << 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); } if (!changedPhotos.isEmpty()) { qCDebug(GOOGLE_LOG) << "Requesting photos for" << changedPhotos << "contacts..."; m_resource->scheduleCustomTask(this, "retrieveContactsPhotos", QVariant::fromValue(changedPhotos)); } const QDateTime local(QDateTime::currentDateTime()); const QDateTime UTC(local.toUTC()); m_allCollection.setRemoteRevision(QString::number(UTC.toSecsSinceEpoch())); new CollectionModifyJob(m_allCollection, this); emitReadyStatus(); } void ContactHandler::retrieveContactsPhotos(const QVariant &argument) { if (!m_resource->canPerformTask()) { return; } Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving contacts photos")); qCDebug(GOOGLE_LOG) << "Retrieve contacts photos" << argument << "called"; auto job = new ItemFetchJob(m_allCollection, this); job->setProperty(MODIFIED_PROPERTY, argument); job->fetchScope().fetchFullPayload(true); connect(job, &ItemFetchJob::finished, this, &ContactHandler::slotUpdatePhotosItemsRetrieved); } void ContactHandler::slotUpdatePhotosItemsRetrieved(KJob *job) { auto fetchJob = qobject_cast(job); const auto modifiedItems = fetchJob->property(MODIFIED_PROPERTY).value(); ContactsList contacts; const Item::List items = fetchJob->items(); qCDebug(GOOGLE_LOG) << "Fetched" << items.count() << "contacts for photo update"; for (const Item &item : items) { if (modifiedItems.contains(item.remoteId())) { const KContacts::Addressee addressee = item.payload(); const ContactPtr contact(new Contact(addressee)); contacts << contact; qCDebug(GOOGLE_LOG) << " -" << contact->uid(); } } // Make sure account is still valid if (!m_resource->canPerformTask()) { return; } qCDebug(GOOGLE_LOG) << "Starting fetching photos..."; auto photoJob = new ContactFetchPhotoJob(contacts, m_resource->account(), this); photoJob->setProperty("processedItems", 0); connect(photoJob, &ContactFetchPhotoJob::photoFetched, [this, &items](KGAPI2::Job *job, const ContactPtr &contact){ qCDebug(GOOGLE_LOG) << " - fetched photo for contact" << contact->uid(); int processedItems = job->property("processedItems").toInt(); processedItems++; job->setProperty("processedItems", processedItems); Q_EMIT percent((float)processedItems / items.count()); for (const Item item : items) { if (item.remoteId() == contact->uid()) { Item newItem = item; newItem.setPayload(*contact.dynamicCast()); new ItemModifyJob(newItem, this); return; } } }); connect(photoJob, &ContactFetchPhotoJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Adding contact to group '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Contact added to group" << collection.remoteId(); auto addressee = item.payload< KContacts::Addressee >(); ContactPtr contact(new Contact(addressee)); if (collection.remoteId() == myContactsRemoteId()) { /* Adding contact to "My Contacts" system group explicitly */ contact->addGroup(myContactsRemoteId()); } auto job = new ContactCreateJob(contact, m_resource->account(), this); connect(job, &ContactCreateJob::finished, [this, item](KGAPI2::Job* job){ if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Contact created!"; ContactPtr contact = qobject_cast(job)->items().first().dynamicCast(); Item newItem = item; newItem.setRemoteId(contact->uid()); newItem.setRemoteRevision(contact->etag()); m_resource->changeCommitted(newItem); emitReadyStatus(); }); } void ContactHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing contact")); qCDebug(GOOGLE_LOG) << "Changing contact" << item.remoteId(); KContacts::Addressee addressee = item.payload< KContacts::Addressee >(); ContactPtr contact(new Contact(addressee)); ContactModifyJob *modifyJob = new ContactModifyJob(contact, m_resource->account(), this); modifyJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(modifyJob, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemRemoved(const Item &item) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing contact")); qCDebug(GOOGLE_LOG) << "Removing contact" << item.remoteId(); auto job = new ContactDeleteJob(item.remoteId(), m_resource->account(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &ContactDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemMoved(const Item &item, const Collection &collectionSource, const Collection &collectionDestination) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Moving contact")); qCDebug(GOOGLE_LOG) << "Moving contact" << item.remoteId() << "from" << collectionSource.remoteId() << "to" << collectionDestination.remoteId(); KContacts::Addressee addressee = item.payload< KContacts::Addressee >(); ContactPtr contact(new Contact(addressee)); /* // MyContacts -> OtherContacts if (collectionSource.remoteId() == MYCONTACTS_REMOTEID && collectionDestination.remoteId() == OTHERCONTACTS_REMOTEID) { contact->removeGroup(QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/6").arg(QString::fromLatin1(QUrl::toPercentEncoding(m_resource->account()->accountName())))); // OtherContacts -> MyContacts } else if (collectionSource.remoteId() == OTHERCONTACTS_REMOTEID && collectionDestination.remoteId() == MYCONTACTS_REMOTEID) { contact->addGroup(QStringLiteral("http://www.google.com/m8/feeds/groups/%1/base/6").arg(QString::fromLatin1(QUrl::toPercentEncoding(m_resource->account()->accountName())))); } else {*/ m_resource->cancelTask(i18n("Invalid source or destination collection")); return; // } auto job = new ContactModifyJob(contact, m_resource->account(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemLinked(const Item &item, const Collection &collection) { KContacts::Addressee addressee = item.payload(); ContactPtr contact(new Contact(addressee)); contact->addGroup(collection.remoteId()); ContactModifyJob *modifyJob = new ContactModifyJob(contact, m_resource->account(), this); modifyJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(modifyJob, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::itemUnlinked(const Item &item, const Collection &collection) { KContacts::Addressee addressee = item.payload(); ContactPtr contact(new Contact(addressee)); contact->removeGroup(collection.remoteId()); ContactModifyJob *modifyJob = new ContactModifyJob(contact, m_resource->account(), this); modifyJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(modifyJob, &ContactModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::collectionAdded(const Collection &collection, const Collection &parent) { Q_UNUSED(parent); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Creating new contact group '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Adding contact group" << collection.displayName(); ContactsGroupPtr group(new ContactsGroup); group->setTitle(collection.name()); group->setIsSystemGroup(false); auto job = new ContactsGroupCreateJob(group, m_resource->account(), this); connect(job, &ContactsGroupCreateJob::finished, [this, &collection](KGAPI2::Job* job){ if (!m_resource->handleError(job)) { return; } ContactsGroupPtr group = qobject_cast(job)->items().first().dynamicCast(); qCDebug(GOOGLE_LOG) << "Contact group created:" << group->id(); Collection newCollection = collection; newCollection.setRemoteId(group->id()); newCollection.setContentMimeTypes(QStringList() << KContacts::Addressee::mimeType()); auto attr = newCollection.attribute(Collection::AddIfMissing); attr->setDisplayName(group->title()); attr->setIconName(QStringLiteral("view-pim-contacts")); m_collections[ newCollection.remoteId() ] = newCollection; m_resource->changeCommitted(newCollection); emitReadyStatus(); }); } void ContactHandler::collectionChanged(const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing contact group '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Changing contact group" << collection.remoteId(); ContactsGroupPtr group(new ContactsGroup()); group->setId(collection.remoteId()); group->setTitle(collection.displayName()); auto job = new ContactsGroupModifyJob(group, m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &ContactsGroupModifyJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void ContactHandler::collectionRemoved(const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing contact group '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Removing contact group" << collection.remoteId(); auto job = new ContactsGroupDeleteJob(collection.remoteId(), m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &ContactsGroupDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } diff --git a/resources/google-new/googleresource.cpp b/resources/google-new/googleresource.cpp index b93c3c487..e1a4875a0 100644 --- a/resources/google-new/googleresource.cpp +++ b/resources/google-new/googleresource.cpp @@ -1,597 +1,601 @@ /* 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 "googleresource.h" #include "googlesettings.h" #include "calendarhandler.h" #include "contacthandler.h" #include "taskhandler.h" #include "defaultreminderattribute.h" #include "googleresource_debug.h" #include "kgapiversionattribute.h" -#include "settings.h" +#include "settingsadaptor.h" #include "settingsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACCESS_TOKEN_PROPERTY "AccessToken" #define CALENDARS_PROPERTY "_KGAPI2CalendarPtr" #define ROOT_COLLECTION_REMOTEID QStringLiteral("RootCollection") Q_DECLARE_METATYPE(KGAPI2::Job *) using namespace KGAPI2; using namespace Akonadi; GoogleResource::GoogleResource(const QString &id) : ResourceBase(id) , AgentBase::ObserverV2() , m_isConfiguring(false) { AttributeFactory::registerAttribute< DefaultReminderAttribute >(); AttributeFactory::registerAttribute(); connect(this, &GoogleResource::abortRequested, this, [this](){ cancelTask(i18n("Aborted")); }); connect(this, &GoogleResource::reloadConfiguration, this, &GoogleResource::reloadConfig); setNeedsNetwork(true); changeRecorder()->itemFetchScope().fetchFullPayload(true); changeRecorder()->itemFetchScope().setAncestorRetrieval(ItemFetchScope::All); changeRecorder()->fetchCollection(true); changeRecorder()->collectionFetchScope().setAncestorRetrieval(CollectionFetchScope::All); m_accountMgr = new GoogleAccountManager(this); connect(m_accountMgr, &GoogleAccountManager::accountChanged, this, [this](const AccountPtr &account){ if (accountId() > 0) { return; } m_account = account; }); connect(m_accountMgr, &GoogleAccountManager::accountRemoved, this, [this](const QString &accountName){ if (accountId() > 0) { return; } if (m_account && m_account->accountName() != accountName) { return; } Q_EMIT status(NotConfigured, i18n("Configured account has been removed")); m_account.clear(); - Settings::self()->setAccount(QString()); + GoogleSettings::self()->setAccount(QString()); }); connect(m_accountMgr, &GoogleAccountManager::managerReady, this, [this](bool ready){ if (accountId() > 0) { return; } if (!ready) { Q_EMIT status(Broken, i18n("Can't access KWallet")); return; } - const QString accountName = Settings::self()->account(); + const QString accountName = GoogleSettings::self()->account(); if (accountName.isEmpty()) { Q_EMIT status(NotConfigured); return; } m_account = m_accountMgr->findAccount(accountName); if (m_account.isNull()) { Q_EMIT status(NotConfigured, i18n("Configured account does not exist")); return; } Q_EMIT status(Idle, i18nc("@info:status", "Ready")); synchronize(); }); Q_EMIT status(NotConfigured, i18n("Waiting for KWallet...")); updateResourceName(); m_freeBusyHandler.reset(new CalendarHandler(this)); m_handlers << m_freeBusyHandler; m_handlers << GenericHandler::Ptr(new ContactHandler(this)); m_handlers << GenericHandler::Ptr(new TaskHandler(this)); for (auto handler : m_handlers) { connect(handler.data(), &GenericHandler::status, [this](int code, QString message){ Q_EMIT status(code, message); }); connect(handler.data(), &GenericHandler::percent, [this](int value){ Q_EMIT percent(value); }); connect(handler.data(), &GenericHandler::collectionsRetrieved, this, &GoogleResource::collectionsPartiallyRetrieved); } + + new SettingsAdaptor(GoogleSettings::self()); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), + GoogleSettings::self(), QDBusConnection::ExportAdaptors); } GoogleResource::~GoogleResource() { } void GoogleResource::cleanup() { - accountManager()->cleanup(Settings::self()->account()); + accountManager()->cleanup(GoogleSettings::self()->account()); ResourceBase::cleanup(); } AccountPtr GoogleResource::account() const { return m_account; } GoogleAccountManager *GoogleResource::accountManager() const { return m_accountMgr; } Akonadi::Collection GoogleResource::rootCollection() const { return m_rootCollection; } void GoogleResource::configure(WId windowId) { if (!m_accountMgr->isReady() || m_isConfiguring) { Q_EMIT configurationDialogAccepted(); return; } m_isConfiguring = true; QScopedPointer settingsDialog(new SettingsDialog(accountManager(), windowId, this)); settingsDialog->setWindowIcon(QIcon::fromTheme(QStringLiteral("im-google"))); if (settingsDialog->exec() == QDialog::Accepted) { updateResourceName(); Q_EMIT configurationDialogAccepted(); - m_account = accountManager()->findAccount(Settings::self()->account()); + m_account = accountManager()->findAccount(GoogleSettings::self()->account()); if (m_account.isNull()) { Q_EMIT status(NotConfigured, i18n("Configured account does not exist")); m_isConfiguring = false; return; } Q_EMIT status(Idle, i18nc("@info:status", "Ready")); synchronize(); } else { updateResourceName(); Q_EMIT configurationDialogRejected(); } m_isConfiguring = false; } QList GoogleResource::scopes() const { // TODO: determine it based on what user wants? const QList< QUrl > scopes = {Account::accountInfoScopeUrl(), Account::calendarScopeUrl(), Account::contactsScopeUrl(), Account::tasksScopeUrl()}; return scopes; } void GoogleResource::updateResourceName() { - const QString accountName = Settings::self()->account(); + const QString accountName = GoogleSettings::self()->account(); setName(i18nc("%1 is account name (user@gmail.com)", "Google Groupware (%1)", accountName.isEmpty() ? i18n("not configured") : accountName)); } void GoogleResource::updateAccountToken(const AccountPtr &account, KGAPI2::Job *restartJob) { - if (!Settings::self()->account().isEmpty()) { - AuthJob *authJob = new AuthJob(account, Settings::self()->clientId(), Settings::self()->clientSecret(), this); + if (!GoogleSettings::self()->account().isEmpty()) { + AuthJob *authJob = new AuthJob(account, GoogleSettings::self()->clientId(), GoogleSettings::self()->clientSecret(), this); authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(restartJob)); connect(authJob, &AuthJob::finished, this, &GoogleResource::slotAuthJobFinished); } } void GoogleResource::reloadConfig() { - const QString accountName = Settings::self()->account(); + const QString accountName = GoogleSettings::self()->account(); if (!accountName.isEmpty()) { m_account = m_accountMgr->findAccount(accountName); if (m_account.isNull()) { Q_EMIT status(NotConfigured, i18n("Configured account does not exist")); return; } } else { Q_EMIT status(NotConfigured); return; } Q_EMIT status(Idle, i18nc("@info:status", "Ready")); } bool GoogleResource::handleError(KGAPI2::Job *job, bool _cancelTask) { if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) { return true; } qCDebug(GOOGLE_LOG) << job << job->errorString(); if (job->error() == KGAPI2::Unauthorized) { const QList resourceScopes = scopes(); for (const QUrl &scope : resourceScopes) { if (!m_account->scopes().contains(scope)) { m_account->addScope(scope); } } updateAccountToken(m_account, job); return false; } if (_cancelTask) { cancelTask(job->errorString()); } return false; } bool GoogleResource::canPerformTask() { if (!m_account && accountId() == 0) { cancelTask(i18nc("@info:status", "Resource is not configured")); Q_EMIT status(NotConfigured, i18nc("@info:status", "Resource is not configured")); return false; } return true; } void GoogleResource::slotAuthJobFinished(KGAPI2::Job *job) { if (job->error() != KGAPI2::NoError) { cancelTask(i18n("Failed to refresh tokens")); return; } AuthJob *authJob = qobject_cast(job); m_account = authJob->account(); if (!m_accountMgr->storeAccount(m_account)) { qWarning() << "Failed to store account in KWallet"; } KGAPI2::Job *otherJob = job->property(JOB_PROPERTY).value(); if (otherJob) { otherJob->setAccount(m_account); otherJob->restart(); } job->deleteLater(); } void GoogleResource::slotGenericJobFinished(KGAPI2::Job *job) { if (!handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Job finished"; const Item item = job->property(ITEM_PROPERTY).value(); const Collection collection = job->property(COLLECTION_PROPERTY).value(); if (item.isValid()) { changeCommitted(item); } else if (collection.isValid()) { changeCommitted(collection); } else { taskDone(); } Q_EMIT status(Idle, i18nc("@info:status", "Ready")); } int GoogleResource::accountId() const { return 0; } QDateTime GoogleResource::lastCacheUpdate() const { if (m_freeBusyHandler) { return m_freeBusyHandler->lastCacheUpdate(); } return QDateTime(); } void GoogleResource::canHandleFreeBusy(const QString &email) const { if (m_freeBusyHandler) { m_freeBusyHandler->canHandleFreeBusy(email); } else { handlesFreeBusy(email, false); } } void GoogleResource::retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) { if (m_freeBusyHandler) { m_freeBusyHandler->retrieveFreeBusy(email, start, end); } else { freeBusyRetrieved(email, QString(), false, QString()); } } /* * Collection handling */ void GoogleResource::retrieveCollections() { qCDebug(GOOGLE_LOG) << "Retrieve Collections"; if (!canPerformTask()) { return; } CachePolicy cachePolicy; - if (Settings::self()->enableIntervalCheck()) { + if (GoogleSettings::self()->enableIntervalCheck()) { cachePolicy.setInheritFromParent(false); - cachePolicy.setIntervalCheckTime(Settings::self()->intervalCheckTime()); + cachePolicy.setIntervalCheckTime(GoogleSettings::self()->intervalCheckTime()); } // Setting up root collection m_rootCollection = Collection(); m_rootCollection.setContentMimeTypes({ Collection::mimeType(), Collection::virtualMimeType() }); m_rootCollection.setRemoteId(ROOT_COLLECTION_REMOTEID); m_rootCollection.setName(account()->accountName()); m_rootCollection.setParentCollection(Collection::root()); m_rootCollection.setRights(Collection::CanCreateCollection); m_rootCollection.setCachePolicy(cachePolicy); EntityDisplayAttribute *attr = m_rootCollection.attribute(Collection::AddIfMissing); attr->setDisplayName(account()->accountName()); attr->setIconName(QStringLiteral("im-google")); m_collections = { m_rootCollection }; m_jobs = 0; for (auto handler : m_handlers) { handler->retrieveCollections(); m_jobs++; } } void GoogleResource::collectionsPartiallyRetrieved(const Collection::List& collections) { m_jobs--; m_collections << collections; if (m_jobs == 0) { qCDebug(GOOGLE_LOG) << "Collections retrieved!"; collectionsRetrieved(m_collections); } } void GoogleResource::retrieveItems(const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (collection.contentMimeTypes().contains(handler->mimetype())) { handler->retrieveItems(collection); found = true; break; } } if (!found) { qCWarning(GOOGLE_LOG) << "Unknown collection" << collection.name(); itemsRetrieved({}); } } void GoogleResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } if (collection.parentCollection() == Akonadi::Collection::root()) { cancelTask(i18n("The top-level collection cannot contain anything")); return; } bool found = false; for (auto handler : m_handlers) { if (collection.contentMimeTypes().contains(handler->mimetype()) && handler->canPerformTask(item)) { handler->itemAdded(item, collection); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::itemChanged(const Akonadi::Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (handler->canPerformTask(item)) { handler->itemChanged(item, partIdentifiers); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::itemRemoved(const Akonadi::Item &item) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (item.mimeType() == handler->mimetype()) { handler->itemRemoved(item); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::itemMoved(const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) { if (!canPerformTask()) { return; } if (collectionDestination.parentCollection() == Akonadi::Collection::root()) { cancelTask(i18n("The top-level collection cannot contain anything")); return; } bool found = false; for (auto handler : m_handlers) { if (handler->canPerformTask(item)) { handler->itemMoved(item, collectionSource, collectionDestination); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::itemLinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (handler->canPerformTask(item)) { handler->itemLinked(item, collection); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::itemUnlinked(const Akonadi::Item &item, const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (handler->canPerformTask(item)) { handler->itemUnlinked(item, collection); found = true; break; } } if (!found) { cancelTask(i18n("Invalid payload type")); } } void GoogleResource::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (collection.contentMimeTypes().contains(handler->mimetype())) { handler->retrieveItems(collection); found = true; break; } } if (!found) { cancelTask(i18n("Unknown collection mimetype")); } } void GoogleResource::collectionChanged(const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (collection.contentMimeTypes().contains(handler->mimetype())) { handler->retrieveItems(collection); found = true; break; } } if (!found) { cancelTask(i18n("Unknown collection mimetype")); } } void GoogleResource::collectionRemoved(const Akonadi::Collection &collection) { if (!canPerformTask()) { return; } bool found = false; for (auto handler : m_handlers) { if (collection.contentMimeTypes().contains(handler->mimetype())) { handler->retrieveItems(collection); found = true; break; } } if (!found) { cancelTask(i18n("Unknown collection mimetype")); } } AKONADI_RESOURCE_MAIN(GoogleResource) diff --git a/resources/google-new/googlesettings.cpp b/resources/google-new/googlesettings.cpp index e8339f72a..7018074ea 100644 --- a/resources/google-new/googlesettings.cpp +++ b/resources/google-new/googlesettings.cpp @@ -1,54 +1,66 @@ /* Copyright (C) 2011-2013 Dan Vratil 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 "settingsadaptor.h" #include "settingsbase.h" +#include + +Q_GLOBAL_STATIC(GoogleSettings, s_globalSettings); + GoogleSettings::GoogleSettings() : m_winId(0) { + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, + QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); } QString GoogleSettings::clientId() const { return QStringLiteral("554041944266.apps.googleusercontent.com"); } QString GoogleSettings::clientSecret() const { return QStringLiteral("mdT1DjzohxN3npUUzkENT0gO"); } void GoogleSettings::setWindowId(WId id) { m_winId = id; } void GoogleSettings::setResourceId(const QString &resourceIdentificator) { m_resourceId = resourceIdentificator; } QString GoogleSettings::account() const { return SettingsBase::account(); } void GoogleSettings::setAccount(const QString &account) { SettingsBase::setAccount(account); } + +GoogleSettings* GoogleSettings::self() +{ + return s_globalSettings(); +} diff --git a/resources/google-new/googlesettings.h b/resources/google-new/googlesettings.h index 9c62ac52b..7388d7dff 100644 --- a/resources/google-new/googlesettings.h +++ b/resources/google-new/googlesettings.h @@ -1,54 +1,56 @@ /* Copyright (C) 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 . */ #ifndef GOOGLESETTINGS_H #define GOOGLESETTINGS_H #include "settingsbase.h" #include /** * @brief Settings object * * Provides read-only access to application clientId and * clientSecret and read-write access to accessToken and * refreshToken. */ 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; virtual QString account() const; virtual void setAccount(const QString &account); + static GoogleSettings *self(); private: WId m_winId; QString m_resourceId; }; #endif // GOOGLESETTINGS_H diff --git a/resources/google-new/googlesettingsdialog.cpp b/resources/google-new/googlesettingsdialog.cpp index 9d284fd5f..17b473df8 100644 --- a/resources/google-new/googlesettingsdialog.cpp +++ b/resources/google-new/googlesettingsdialog.cpp @@ -1,266 +1,265 @@ /* Copyright (C) 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 "googlesettingsdialog.h" #include "googleaccountmanager.h" #include "googlesettings.h" #include "googleresource.h" -#include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(KGAPI2::Job *) using namespace KGAPI2; GoogleSettingsDialog::GoogleSettingsDialog(GoogleAccountManager *accountManager, WId wId, GoogleResource *parent) : QDialog() , m_parentResource(parent) , m_accountManager(accountManager) { setAttribute(Qt::WA_NativeWindow, true); KWindowSystem::setMainWindow(windowHandle(), wId); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QVBoxLayout *topLayout = new QVBoxLayout(this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &GoogleSettingsDialog::slotSaveSettings); connect(buttonBox, &QDialogButtonBox::rejected, this, &GoogleSettingsDialog::reject); QWidget *widget = new QWidget(this); topLayout->addWidget(widget); topLayout->addWidget(buttonBox); QVBoxLayout *mainLayout = new QVBoxLayout(widget); mainLayout->setContentsMargins(0, 0, 0, 0); m_mainLayout = mainLayout; m_accGroupBox = new QGroupBox(i18n("Accounts"), this); mainLayout->addWidget(m_accGroupBox); QHBoxLayout *accLayout = new QHBoxLayout(m_accGroupBox); m_accComboBox = new QComboBox(m_accGroupBox); accLayout->addWidget(m_accComboBox, 1); connect(m_accComboBox, QOverload::of(&QComboBox::currentTextChanged), this, &GoogleSettingsDialog::currentAccountChanged); m_addAccButton = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add-user")), i18n("&Add"), m_accGroupBox); accLayout->addWidget(m_addAccButton); connect(m_addAccButton, &QPushButton::clicked, this, &GoogleSettingsDialog::slotAddAccountClicked); m_removeAccButton = new QPushButton(QIcon::fromTheme(QStringLiteral("list-remove-user")), i18n("&Remove"), m_accGroupBox); accLayout->addWidget(m_removeAccButton); connect(m_removeAccButton, &QPushButton::clicked, this, &GoogleSettingsDialog::slotRemoveAccountClicked); QGroupBox *refreshBox = new QGroupBox(i18n("Refresh"), this); mainLayout->addWidget(refreshBox); QGridLayout *refreshLayout = new QGridLayout(refreshBox); m_enableRefresh = new QCheckBox(i18n("Enable interval refresh"), refreshBox); - m_enableRefresh->setChecked(Settings::self()->enableIntervalCheck()); + m_enableRefresh->setChecked(GoogleSettings::self()->enableIntervalCheck()); refreshLayout->addWidget(m_enableRefresh, 0, 0, 1, 2); QLabel *label = new QLabel(i18n("Refresh interval:")); refreshLayout->addWidget(label, 1, 0); m_refreshSpinBox = new KPluralHandlingSpinBox(this); m_refreshSpinBox->setMaximum(720); m_refreshSpinBox->setMinimum(10); m_refreshSpinBox->setSingleStep(1); m_refreshSpinBox->setValue(30); m_refreshSpinBox->setDisplayIntegerBase(10); m_refreshSpinBox->setSuffix(ki18np(" minute", " minutes")); - m_refreshSpinBox->setEnabled(Settings::self()->enableIntervalCheck()); + m_refreshSpinBox->setEnabled(GoogleSettings::self()->enableIntervalCheck()); refreshLayout->addWidget(m_refreshSpinBox, 1, 1); connect(m_enableRefresh, &QCheckBox::toggled, m_refreshSpinBox, &KPluralHandlingSpinBox::setEnabled); if (m_enableRefresh->isEnabled()) { - m_refreshSpinBox->setValue(Settings::self()->intervalCheckTime()); + m_refreshSpinBox->setValue(GoogleSettings::self()->intervalCheckTime()); } QMetaObject::invokeMethod(this, &GoogleSettingsDialog::reloadAccounts, Qt::QueuedConnection); } GoogleSettingsDialog::~GoogleSettingsDialog() { } QVBoxLayout *GoogleSettingsDialog::mainLayout() const { return m_mainLayout; } GoogleAccountManager *GoogleSettingsDialog::accountManager() const { return m_accountManager; } KGAPI2::AccountPtr GoogleSettingsDialog::currentAccount() const { return m_accountManager->findAccount(m_accComboBox->currentText()); } void GoogleSettingsDialog::reloadAccounts() { disconnect(m_accComboBox, QOverload::of(&QComboBox::currentTextChanged), this, &GoogleSettingsDialog::currentAccountChanged); m_accComboBox->clear(); const AccountsList accounts = m_accountManager->listAccounts(); for (const AccountPtr &account : accounts) { m_accComboBox->addItem(account->accountName()); } - int index = m_accComboBox->findText(Settings::self()->account(), Qt::MatchExactly); + int index = m_accComboBox->findText(GoogleSettings::self()->account(), Qt::MatchExactly); if (index > -1) { m_accComboBox->setCurrentIndex(index); } connect(m_accComboBox, QOverload::of(&QComboBox::currentTextChanged), this, &GoogleSettingsDialog::currentAccountChanged); m_removeAccButton->setEnabled(m_accComboBox->count() > 0); Q_EMIT currentAccountChanged(m_accComboBox->currentText()); } void GoogleSettingsDialog::slotAddAccountClicked() { AccountPtr account(new Account()); // FIXME: We need a proper API for this account->addScope(Account::contactsScopeUrl()); account->addScope(Account::calendarScopeUrl()); account->addScope(Account::tasksScopeUrl()); account->addScope(Account::accountInfoEmailScopeUrl()); account->addScope(Account::accountInfoScopeUrl()); AuthJob *authJob = new AuthJob(account, - Settings::self()->clientId(), - Settings::self()->clientSecret()); + GoogleSettings::self()->clientId(), + GoogleSettings::self()->clientSecret()); connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAccountAuthenticated); } void GoogleSettingsDialog::slotRemoveAccountClicked() { const AccountPtr account = currentAccount(); if (!account) { return; } if (KMessageBox::warningYesNo( this, i18n("Do you really want to revoke access to account %1?" "

This will revoke access to all resources using this account!

", account->accountName()), i18n("Revoke Access?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), QString(), KMessageBox::Dangerous) != KMessageBox::Yes) { return; } m_accountManager->removeAccount(account->accountName()); reloadAccounts(); } void GoogleSettingsDialog::slotAccountAuthenticated(Job *job) { AuthJob *authJob = qobject_cast(job); const AccountPtr account = authJob->account(); if (authJob->error() != KGAPI2::NoError) { KMessageBox::sorry(this, authJob->errorString()); return; } if (!m_accountManager->storeAccount(account)) { qWarning() << "Failed to add account to KWallet"; } reloadAccounts(); } bool GoogleSettingsDialog::handleError(Job *job) { if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) { return true; } if (job->error() == KGAPI2::Unauthorized) { qDebug() << job << job->errorString(); const AccountPtr account = currentAccount(); const QList resourceScopes = m_parentResource->scopes(); for (const QUrl &scope : resourceScopes) { if (!account->scopes().contains(scope)) { account->addScope(scope); } } - AuthJob *authJob = new AuthJob(account, Settings::self()->clientId(), - Settings::self()->clientSecret(), this); + AuthJob *authJob = new AuthJob(account, GoogleSettings::self()->clientId(), + GoogleSettings::self()->clientSecret(), this); authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job)); connect(authJob, &AuthJob::finished, this, &GoogleSettingsDialog::slotAuthJobFinished); return false; } KMessageBox::sorry(this, job->errorString()); job->deleteLater(); return false; } void GoogleSettingsDialog::slotAuthJobFinished(Job *job) { qDebug(); if (job->error() != KGAPI2::NoError) { KMessageBox::sorry(this, job->errorString()); return; } AuthJob *authJob = qobject_cast(job); const AccountPtr account = authJob->account(); if (!m_accountManager->storeAccount(account)) { qWarning() << "Failed to store account in KWallet"; } KGAPI2::Job *otherJob = job->property(JOB_PROPERTY).value(); otherJob->setAccount(account); otherJob->restart(); job->deleteLater(); } void GoogleSettingsDialog::slotSaveSettings() { - Settings::self()->setEnableIntervalCheck(m_enableRefresh->isChecked()); - Settings::self()->setIntervalCheckTime(m_refreshSpinBox->value()); + GoogleSettings::self()->setEnableIntervalCheck(m_enableRefresh->isChecked()); + GoogleSettings::self()->setIntervalCheckTime(m_refreshSpinBox->value()); saveSettings(); accept(); } diff --git a/resources/google-new/settings.cpp b/resources/google-new/settings.cpp deleted file mode 100644 index 894a7942f..000000000 --- a/resources/google-new/settings.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2011, 2012 Dan Vratil - - 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 "settings.h" -#include "settingsadaptor.h" - -#include - -class SettingsHelper -{ -public: - SettingsHelper() : q(nullptr) - { - } - - ~SettingsHelper() - { - delete q; - q = nullptr; - } - - Settings *q; -}; - -Q_GLOBAL_STATIC(SettingsHelper, s_globalSettings) - -Settings::Settings() - : GoogleSettings() -{ - Q_ASSERT(!s_globalSettings->q); - s_globalSettings->q = this; - - new SettingsAdaptor(this); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, - QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); -} - -Settings *Settings::self() -{ - if (!s_globalSettings->q) { - new Settings; - s_globalSettings->q->load(); - } - - return s_globalSettings->q; -} diff --git a/resources/google-new/settings.h b/resources/google-new/settings.h deleted file mode 100644 index 84747f079..000000000 --- a/resources/google-new/settings.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright (C) 2011, 2012 Dan Vratil - - 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 GOOGLE_CALENDAR_SETTINGS_H -#define GOOGLE_CALENDAR_SETTINGS_H - -#include "googlesettings.h" - -class Settings : public GoogleSettings -{ - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.GoogleCalendar.ExtendedSettings") -public: - Settings(); - static Settings *self(); -}; - -#endif // SETTINGS_H diff --git a/resources/google-new/settingsdialog.cpp b/resources/google-new/settingsdialog.cpp index ce67ac4f6..f745c39bc 100644 --- a/resources/google-new/settingsdialog.cpp +++ b/resources/google-new/settingsdialog.cpp @@ -1,230 +1,230 @@ /* 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 "settingsdialog.h" -#include "settings.h" +#include "googlesettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KGAPI2; SettingsDialog::SettingsDialog(GoogleAccountManager *accountManager, WId windowId, GoogleResource *parent) : GoogleSettingsDialog(accountManager, windowId, parent) { connect(this, &SettingsDialog::currentAccountChanged, this, &SettingsDialog::slotCurrentAccountChanged); m_calendarsBox = new QGroupBox(i18n("Calendars"), this); mainLayout()->addWidget(m_calendarsBox); QVBoxLayout *vbox = new QVBoxLayout(m_calendarsBox); m_calendarsList = new QListWidget(m_calendarsBox); vbox->addWidget(m_calendarsList, 1); m_reloadCalendarsBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reload"), m_calendarsBox); vbox->addWidget(m_reloadCalendarsBtn); connect(m_reloadCalendarsBtn, &QPushButton::clicked, this, &SettingsDialog::slotReloadCalendars); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); m_eventsLimitLabel = new QLabel(i18nc("Followed by a date picker widget", "&Fetch only new events since"), this); hbox->addWidget(m_eventsLimitLabel); m_eventsLimitCombo = new KDateComboBox(this); m_eventsLimitLabel->setBuddy(m_eventsLimitCombo); m_eventsLimitCombo->setMaximumDate(QDate::currentDate()); m_eventsLimitCombo->setMinimumDate(QDate::fromString(QStringLiteral("2000-01-01"), Qt::ISODate)); m_eventsLimitCombo->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate |KDateComboBox::DatePicker | KDateComboBox::WarnOnInvalid); - if (Settings::self()->eventsSince().isEmpty()) { + if (GoogleSettings::self()->eventsSince().isEmpty()) { const QString ds = QStringLiteral("%1-01-01").arg(QString::number(QDate::currentDate().year() - 3)); m_eventsLimitCombo->setDate(QDate::fromString(ds, Qt::ISODate)); } else { - m_eventsLimitCombo->setDate(QDate::fromString(Settings::self()->eventsSince(), Qt::ISODate)); + m_eventsLimitCombo->setDate(QDate::fromString(GoogleSettings::self()->eventsSince(), Qt::ISODate)); } hbox->addWidget(m_eventsLimitCombo); m_taskListsBox = new QGroupBox(i18n("Tasklists"), this); mainLayout()->addWidget(m_taskListsBox); vbox = new QVBoxLayout(m_taskListsBox); m_taskListsList = new QListWidget(m_taskListsBox); vbox->addWidget(m_taskListsList, 1); m_reloadTaskListsBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reload"), m_taskListsBox); vbox->addWidget(m_reloadTaskListsBtn); connect(m_reloadTaskListsBtn, &QPushButton::clicked, this, &SettingsDialog::slotReloadTaskLists); } SettingsDialog::~SettingsDialog() { } void SettingsDialog::saveSettings() { const AccountPtr account = currentAccount(); if (!currentAccount()) { - Settings::self()->setAccount(QString()); - Settings::self()->setCalendars(QStringList()); - Settings::self()->setTaskLists(QStringList()); - Settings::self()->setEventsSince(QString()); - Settings::self()->save(); + GoogleSettings::self()->setAccount(QString()); + GoogleSettings::self()->setCalendars(QStringList()); + GoogleSettings::self()->setTaskLists(QStringList()); + GoogleSettings::self()->setEventsSince(QString()); + GoogleSettings::self()->save(); return; } - Settings::self()->setAccount(account->accountName()); + GoogleSettings::self()->setAccount(account->accountName()); QStringList calendars; for (int i = 0; i < m_calendarsList->count(); i++) { QListWidgetItem *item = m_calendarsList->item(i); if (item->checkState() == Qt::Checked) { calendars.append(item->data(Qt::UserRole).toString()); } } - Settings::self()->setCalendars(calendars); + GoogleSettings::self()->setCalendars(calendars); if (m_eventsLimitCombo->isValid()) { - Settings::self()->setEventsSince(m_eventsLimitCombo->date().toString(Qt::ISODate)); + GoogleSettings::self()->setEventsSince(m_eventsLimitCombo->date().toString(Qt::ISODate)); } QStringList taskLists; for (int i = 0; i < m_taskListsList->count(); i++) { QListWidgetItem *item = m_taskListsList->item(i); if (item->checkState() == Qt::Checked) { taskLists.append(item->data(Qt::UserRole).toString()); } } - Settings::self()->setTaskLists(taskLists); + GoogleSettings::self()->setTaskLists(taskLists); - Settings::self()->save(); + GoogleSettings::self()->save(); } void SettingsDialog::slotCurrentAccountChanged(const QString &accountName) { if (accountName.isEmpty()) { m_taskListsBox->setDisabled(true); m_taskListsList->clear(); m_calendarsBox->setDisabled(true); m_calendarsList->clear(); return; } slotReloadCalendars(); slotReloadTaskLists(); } void SettingsDialog::slotReloadCalendars() { const AccountPtr account = currentAccount(); if (!account) { return; } CalendarFetchJob *fetchJob = new CalendarFetchJob(account, this); connect(fetchJob, &CalendarFetchJob::finished, this, &SettingsDialog::slotCalendarsRetrieved); m_calendarsBox->setDisabled(true); m_calendarsList->clear(); } void SettingsDialog::slotReloadTaskLists() { const AccountPtr account = currentAccount(); if (!account) { return; } TaskListFetchJob *fetchJob = new TaskListFetchJob(account, this); connect(fetchJob, &TaskListFetchJob::finished, this, &SettingsDialog::slotTaskListsRetrieved); m_taskListsBox->setDisabled(true); m_taskListsList->clear(); } void SettingsDialog::slotCalendarsRetrieved(Job *job) { if (!handleError(job) || !currentAccount()) { m_calendarsBox->setEnabled(true); return; } FetchJob *fetchJob = qobject_cast(job); const ObjectsList objects = fetchJob->items(); QStringList activeCalendars; - if (currentAccount()->accountName() == Settings::self()->account()) { - activeCalendars = Settings::self()->calendars(); + if (currentAccount()->accountName() == GoogleSettings::self()->account()) { + activeCalendars = GoogleSettings::self()->calendars(); } m_calendarsList->clear(); for (const ObjectPtr &object : objects) { 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_calendarsList->addItem(item); } m_calendarsBox->setEnabled(true); } void SettingsDialog::slotTaskListsRetrieved(Job *job) { if (!handleError(job) || !currentAccount()) { m_taskListsBox->setEnabled(true); return; } FetchJob *fetchJob = qobject_cast(job); const ObjectsList objects = fetchJob->items(); QStringList activeTaskLists; - if (currentAccount()->accountName() == Settings::self()->account()) { - activeTaskLists = Settings::self()->taskLists(); + if (currentAccount()->accountName() == GoogleSettings::self()->account()) { + activeTaskLists = GoogleSettings::self()->taskLists(); } m_taskListsList->clear(); for (const ObjectPtr &object : objects) { 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_taskListsList->addItem(item); } m_taskListsBox->setEnabled(true); } diff --git a/resources/google-new/taskhandler.cpp b/resources/google-new/taskhandler.cpp index 01440b0eb..87d150922 100644 --- a/resources/google-new/taskhandler.cpp +++ b/resources/google-new/taskhandler.cpp @@ -1,400 +1,400 @@ /* 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 "kgapiversionattribute.h" -#include "settings.h" +#include "googlesettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "googleresource_debug.h" #define TASK_PROPERTY "_KGAPI2::TaskPtr" using namespace KGAPI2; using namespace Akonadi; Q_DECLARE_METATYPE(KGAPI2::TaskPtr) QString TaskHandler::mimetype() { return KCalendarCore::Todo::todoMimeType(); } bool TaskHandler::canPerformTask(const Akonadi::Item &item) { return m_resource->canPerformTask(item, mimetype()); } void TaskHandler::retrieveCollections() { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving task lists")); qCDebug(GOOGLE_LOG) << "Retrieving tasks..."; auto job = new TaskListFetchJob(m_resource->account(), this); connect(job, &KGAPI2::Job::finished, this, &TaskHandler::slotCollectionsRetrieved); } void TaskHandler::slotCollectionsRetrieved(KGAPI2::Job* job) { if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Task lists retrieved"; const ObjectsList taskLists = qobject_cast(job)->items(); - const QStringList activeTaskLists = Settings::self()->taskLists(); + const QStringList activeTaskLists = GoogleSettings::self()->taskLists(); Collection::List collections; for (const ObjectPtr &object : taskLists) { const TaskListPtr &taskList = object.dynamicCast(); qCDebug(GOOGLE_LOG) << "Retrieved task list:" << taskList->uid(); if (!activeTaskLists.contains(taskList->uid())) { continue; } Collection collection; collection.setContentMimeTypes(QStringList() << KCalendarCore::Todo::todoMimeType()); 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")); collections << collection; } Q_EMIT collectionsRetrieved(collections); } void TaskHandler::retrieveItems(const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Retrieving tasks for list '%1'", collection.displayName())); qCDebug(GOOGLE_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_resource->account(), this); if (lastSyncDelta > -1 && lastSyncDelta < 25 * 25 * 3600) { job->setFetchOnlyUpdated(collection.remoteRevision().toULongLong()); } job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::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(); Collection collection = job->property(COLLECTION_PROPERTY).value(); bool isIncremental = (qobject_cast(job)->fetchOnlyUpdated() > 0); qCDebug(GOOGLE_LOG) << "Retrieved" << objects.count() << "tasks for list" << collection.remoteId(); for (const auto &object : objects) { 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_LOG) << " - removed" << task->uid(); removedItems << item; } else { qCDebug(GOOGLE_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); emitReadyStatus(); } void TaskHandler::itemAdded(const Item &item, const Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Adding event to calendar '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Task added to list" << collection.remoteId(); KCalendarCore::Todo::Ptr todo = item.payload(); TaskPtr ktodo(new Task(*todo)); ktodo->setUid(QLatin1String("")); if (!ktodo->relatedTo(KCalendarCore::Incidence::RelTypeParent).isEmpty()) { Akonadi::Item parentItem; parentItem.setRemoteId(ktodo->relatedTo(KCalendarCore::Incidence::RelTypeParent)); qCDebug(GOOGLE_LOG) << "Fetching task parent" << parentItem.remoteId(); auto job = new ItemFetchJob(parentItem, this); job->setCollection(collection); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); job->setProperty(TASK_PROPERTY, QVariant::fromValue(ktodo)); connect(job, &ItemFetchJob::finished, this, &TaskHandler::slotTaskAddedSearchFinished); return; } else { auto job = new TaskCreateJob(ktodo, collection.remoteId(), m_resource->account(), this); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &KGAPI2::Job::finished, this, &TaskHandler::slotCreateJobFinished); } } void TaskHandler::slotTaskAddedSearchFinished(KJob* job) { if (job->error()) { m_resource->cancelTask(i18n("Failed to add task: %1", job->errorString())); return; } ItemFetchJob *fetchJob = qobject_cast(job); Item item = job->property(ITEM_PROPERTY).value(); TaskPtr task = job->property(TASK_PROPERTY).value(); Item::List items = fetchJob->items(); qCDebug(GOOGLE_LOG) << "Received" << items.count() << "parents for task"; const QString tasksListId = item.parentCollection().remoteId(); // Make sure account is still valid if (!m_resource->canPerformTask()) { return; } KGAPI2::Job *newJob = nullptr; // The parent is not known, so give up and just store the item in Google // without the information about parent. // TODO: this is not necessary if (items.isEmpty()) { task->setRelatedTo(QString(), KCalendarCore::Incidence::RelTypeParent); newJob = new TaskCreateJob(task, tasksListId, m_resource->account(), this); } else { Item matchedItem = items.first(); qCDebug(GOOGLE_LOG) << "Adding task with parent" << matchedItem.remoteId(); task->setRelatedTo(matchedItem.remoteId(), KCalendarCore::Incidence::RelTypeParent); TaskCreateJob *createJob = new TaskCreateJob(task, tasksListId, m_resource->account(), this); createJob->setParentItem(matchedItem.remoteId()); newJob = createJob; } newJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(newJob, &KGAPI2::Job::finished, this, &TaskHandler::slotCreateJobFinished); } void TaskHandler::slotCreateJobFinished(KGAPI2::Job* job) { if (!m_resource->handleError(job)) { return; } Item item = job->property(ITEM_PROPERTY).value(); ObjectsList objects = qobject_cast(job)->items(); Q_ASSERT(objects.count() > 0); TaskPtr task = objects.first().dynamicCast(); item.setRemoteId(task->uid()); item.setRemoteRevision(task->etag()); item.setGid(task->uid()); m_resource->changeCommitted(item); item.setPayload(task.dynamicCast()); new ItemModifyJob(item, this); emitReadyStatus(); } void TaskHandler::itemChanged(const Item &item, const QSet< QByteArray > &partIdentifiers) { Q_UNUSED(partIdentifiers); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing task in list '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_LOG) << "Changing task" << item.remoteId(); KCalendarCore::Todo::Ptr todo = item.payload(); TaskPtr ktodo(new Task(*todo)); QString parentUid = todo->relatedTo(KCalendarCore::Incidence::RelTypeParent); auto job = new TaskMoveJob(item.remoteId(), item.parentCollection().remoteId(), parentUid, m_resource->account(), this); connect(job, &TaskMoveJob::finished, [ktodo, item, this](KGAPI2::Job* job){ if (!m_resource->handleError(job)) { return; } qCDebug(GOOGLE_LOG) << "Move task" << item.remoteId() << "finished, modifying..."; auto newJob = new TaskModifyJob(ktodo, item.parentCollection().remoteId(), job->account(), this); newJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); }); } void TaskHandler::itemRemoved(const Item &item) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing task from list '%1'", item.parentCollection().displayName())); qCDebug(GOOGLE_LOG) << "Removing task" << item.remoteId(); /* 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. */ auto job = new ItemFetchJob(item.parentCollection()); job->setAutoDelete(true); job->fetchScope().fetchFullPayload(true); job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(job, &ItemFetchJob::finished, this, &TaskHandler::slotRemoveTaskFetchJobFinished); } void TaskHandler::slotRemoveTaskFetchJobFinished(KJob* job) { if (job->error()) { m_resource->cancelTask(i18n("Failed to delete task: %1", job->errorString())); return; } qCDebug(GOOGLE_LOG) << "Item fetched, removing..."; ItemFetchJob *fetchJob = qobject_cast(job); Item removedItem = fetchJob->property(ITEM_PROPERTY).value(); const Item::List items = fetchJob->items(); Item::List detachItems; for (Item item : items) { if (!item.hasPayload()) { qCDebug(GOOGLE_LOG) << "Item " << item.remoteId() << " does not have Todo payload"; continue; } KCalendarCore::Todo::Ptr todo = item.payload(); /* If this item is child of the item we want to remove then add it to detach list */ if (todo->relatedTo(KCalendarCore::Incidence::RelTypeParent) == removedItem.remoteId()) { todo->setRelatedTo(QString(), KCalendarCore::Incidence::RelTypeParent); item.setPayload(todo); detachItems << item; } } /* If there are no items do detach, then delete the task right now */ if (detachItems.isEmpty()) { slotDoRemoveTask(job); return; } /* Send modify request to detach all the sub-tasks from the task that is about to be * removed. */ auto modifyJob = new ItemModifyJob(detachItems); modifyJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(removedItem)); modifyJob->setAutoDelete(true); connect(modifyJob, &ItemModifyJob::finished, this, &TaskHandler::slotDoRemoveTask); } void TaskHandler::slotDoRemoveTask(KJob *job) { if (job->error()) { m_resource->cancelTask(i18n("Failed to delete task: %1", job->errorString())); return; } // Make sure account is still valid if (!m_resource->canPerformTask()) { return; } Item item = job->property(ITEM_PROPERTY).value< Item >(); /* Now finally we can safely remove the task we wanted to */ auto deleteJob = new TaskDeleteJob(item.remoteId(), item.parentCollection().remoteId(), m_resource->account(), this); deleteJob->setProperty(ITEM_PROPERTY, QVariant::fromValue(item)); connect(deleteJob, &TaskDeleteJob::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void TaskHandler::itemMoved(const Item &item, const Collection &collectionSource, const Collection &collectionDestination) { m_resource->cancelTask(i18n("Moving tasks between task lists is not supported")); } void TaskHandler::collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) { Q_UNUSED(parent); Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Creating new task list '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Adding task list" << collection.displayName(); TaskListPtr taskList(new TaskList()); taskList->setTitle(collection.displayName()); auto job = new TaskListCreateJob(taskList, m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void TaskHandler::collectionChanged(const Akonadi::Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Changing task list '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Changing task list" << collection.remoteId(); TaskListPtr taskList(new TaskList()); taskList->setUid(collection.remoteId()); taskList->setTitle(collection.displayName()); auto job = new TaskListModifyJob(taskList, m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); } void TaskHandler::collectionRemoved(const Akonadi::Collection &collection) { Q_EMIT status(AgentBase::Running, i18nc("@info:status", "Removing task list '%1'", collection.displayName())); qCDebug(GOOGLE_LOG) << "Removing task list" << collection.remoteId(); auto job = new TaskListDeleteJob(collection.remoteId(), m_resource->account(), this); job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection)); connect(job, &KGAPI2::Job::finished, m_resource, &GoogleResource::slotGenericJobFinished); }