File indexing completed on 2025-02-16 04:50:13

0001 /*
0002     SPDX-FileCopyrightText: 2011-2013 Daniel Vrátil <dvratil@redhat.com>
0003     SPDX-FileCopyrightText: 2020 Igor Pobiko <igor.poboiko@gmail.com>
0004     SPDX-FileCopyrightText: 2022-2023 Claudio Cambra <claudio.cambra@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-3.0-or-later
0007 */
0008 
0009 #include "personhandler.h"
0010 #include "peopleconversionjob.h"
0011 #include "googleresource.h"
0012 #include "googlesettings.h"
0013 
0014 #include "googlepeople_debug.h"
0015 
0016 #include <Akonadi/CollectionModifyJob>
0017 #include <Akonadi/EntityDisplayAttribute>
0018 #include <Akonadi/ItemFetchJob>
0019 #include <Akonadi/ItemFetchScope>
0020 #include <Akonadi/ItemModifyJob>
0021 #include <Akonadi/LinkJob>
0022 #include <Akonadi/VectorHelper>
0023 
0024 #include <KContacts/Addressee>
0025 #include <KContacts/Picture>
0026 
0027 #include <kgapicore_export.h>
0028 
0029 #include <KGAPI/Account>
0030 #include <KGAPI/People/ContactGroup>
0031 #include <KGAPI/People/ContactGroupCreateJob>
0032 #include <KGAPI/People/ContactGroupDeleteJob>
0033 #include <KGAPI/People/ContactGroupFetchJob>
0034 #include <KGAPI/People/ContactGroupMembership>
0035 #include <KGAPI/People/ContactGroupModifyJob>
0036 #include <KGAPI/People/Membership>
0037 #include <KGAPI/People/PersonCreateJob>
0038 #include <KGAPI/People/PersonDeleteJob>
0039 #include <KGAPI/People/PersonFetchJob>
0040 #include <KGAPI/People/PersonMetadata>
0041 #include <KGAPI/People/PersonModifyJob>
0042 #include <KGAPI/People/PersonPhotoDeleteJob>
0043 #include <KGAPI/People/PersonPhotoUpdateJob>
0044 #include <KGAPI/People/Photo>
0045 
0046 using namespace KGAPI2;
0047 using namespace Akonadi;
0048 
0049 namespace {
0050     constexpr auto myContactsResourceName = "contactGroups/myContacts";
0051     constexpr auto otherContactsResourceName = "contactGroups/otherContacts";
0052 }
0053 
0054 QString PersonHandler::mimeType()
0055 {
0056     return addresseeMimeType();
0057 }
0058 
0059 QString PersonHandler::addresseeMimeType()
0060 {
0061     return KContacts::Addressee::mimeType();
0062 }
0063 
0064 bool PersonHandler::canPerformTask(const Item &item)
0065 {
0066     return GenericHandler::canPerformTask<KContacts::Addressee>(item);
0067 }
0068 
0069 bool PersonHandler::canPerformTask(const Item::List &items)
0070 {
0071     return GenericHandler::canPerformTask<KContacts::Addressee>(items);
0072 }
0073 
0074 Collection PersonHandler::collectionFromContactGroup(const People::ContactGroupPtr &group)
0075 {
0076     Collection collection;
0077     collection.setContentMimeTypes({addresseeMimeType()});
0078     collection.setName(group->name());
0079     collection.setRemoteId(group->resourceName());
0080     collection.setRemoteRevision(group->etag());
0081 
0082     const auto isSystemGroup = group->groupType() == People::ContactGroup::GroupType::SYSTEM_CONTACT_GROUP;
0083     auto realName = group->formattedName();
0084     if (isSystemGroup) {
0085         if (realName.contains(QLatin1StringView("Coworkers"))) {
0086             realName = i18nc("Name of a group of contacts", "Coworkers");
0087         } else if (realName.contains(QLatin1StringView("Friends"))) {
0088             realName = i18nc("Name of a group of contacts", "Friends");
0089         } else if (realName.contains(QLatin1StringView("Family"))) {
0090             realName = i18nc("Name of a group of contacts", "Family");
0091         } else if (realName.contains(QLatin1StringView("My Contacts"))) {
0092             realName = i18nc("Name of a group of contacts", "My Contacts");
0093         }
0094     }
0095 
0096     // "My Contacts" is the only one not virtual
0097     if (group->resourceName() == QString::fromUtf8(myContactsResourceName)) {
0098         collection.setRights(Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem);
0099     } else {
0100         collection.setRights(Collection::CanLinkItem | Collection::CanUnlinkItem | Collection::CanChangeItem);
0101         collection.setVirtual(true);
0102         if (!isSystemGroup) {
0103             collection.setRights(collection.rights() | Collection::CanChangeCollection | Collection::CanDeleteCollection);
0104         }
0105     }
0106 
0107     auto attr = collection.attribute<EntityDisplayAttribute>(Collection::AddIfMissing);
0108     attr->setDisplayName(realName);
0109     attr->setIconName(QStringLiteral("view-pim-contacts"));
0110 
0111     return collection;
0112 }
0113 
0114 void PersonHandler::retrieveCollections(const Collection &rootCollection)
0115 {
0116     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Retrieving contacts groups"));
0117     qCDebug(GOOGLE_PEOPLE_LOG) << "Retrieving contacts groups...";
0118     m_collections.clear();
0119 
0120     // Set up Google's special "Other contacts" contacts group
0121     Collection otherCollection;
0122     otherCollection.setContentMimeTypes({mimeType()});
0123     otherCollection.setName(i18n("Other Contacts"));
0124     otherCollection.setParentCollection(rootCollection);
0125     otherCollection.setRights(Collection::CanCreateItem | Collection::CanChangeItem | Collection::CanDeleteItem);
0126     otherCollection.setRemoteId(QString::fromUtf8(otherContactsResourceName));
0127 
0128     auto attr = otherCollection.attribute<EntityDisplayAttribute>(Collection::AddIfMissing);
0129     attr->setDisplayName(i18n("Other Contacts"));
0130     attr->setIconName(QStringLiteral("view-pim-contacts"));
0131 
0132     m_iface->collectionsRetrieved({otherCollection});
0133     m_collections[QString::fromUtf8(otherContactsResourceName)] = otherCollection;
0134 
0135     auto job = new People::ContactGroupFetchJob(m_settings->accountPtr(), this);
0136     connect(job, &People::ContactGroupFetchJob::finished, this, [this, rootCollection](KGAPI2::Job *job) {
0137         if (!m_iface->handleError(job)) {
0138             return;
0139         }
0140 
0141         const auto objects = qobject_cast<People::ContactGroupFetchJob *>(job)->items();
0142         qCDebug(GOOGLE_PEOPLE_LOG) << objects.count() << "contact groups retrieved";
0143 
0144         Collection::List collections;
0145         collections.reserve(objects.count());
0146 
0147         std::transform(objects.cbegin(), objects.cend(), std::back_inserter(collections), [this, &rootCollection](const ObjectPtr &object) {
0148             const auto group = object.dynamicCast<People::ContactGroup>();
0149             qCDebug(GOOGLE_PEOPLE_LOG) << " -" << group->formattedName() << "(" << group->resourceName() << ")";
0150 
0151             auto collection = collectionFromContactGroup(group);
0152             collection.setParentCollection(rootCollection);
0153 
0154             m_collections[collection.remoteId()] = collection;
0155             return collection;
0156         });
0157         m_iface->collectionsRetrievedFromHandler(collections);
0158     });
0159 }
0160 
0161 void PersonHandler::retrieveItems(const Collection &collection)
0162 {
0163     // Contacts are stored inside "My Contacts" and "Other Contacts" only
0164     if ((collection.remoteId() != QString::fromUtf8(otherContactsResourceName)) &&
0165         (collection.remoteId() != QString::fromUtf8(myContactsResourceName))) {
0166         m_iface->itemsRetrievalDone();
0167         return;
0168     }
0169 
0170     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Retrieving contacts for group '%1'", collection.displayName()));
0171     qCDebug(GOOGLE_PEOPLE_LOG) << "Retrieving contacts for group" << collection.remoteId() << "...";
0172 
0173     const auto job = new People::PersonFetchJob(m_settings->accountPtr(), this);
0174 
0175     if (!collection.remoteRevision().isEmpty()) {
0176         job->setSyncToken(collection.remoteRevision());
0177     }
0178 
0179     connect(job, &People::PersonFetchJob::finished, this, &PersonHandler::slotItemsRetrieved);
0180 }
0181 
0182 void PersonHandler::slotItemsRetrieved(KGAPI2::Job *job)
0183 {
0184     if (!m_iface->handleError(job)) {
0185         return;
0186     }
0187 
0188     auto collection = m_iface->currentCollection();
0189 
0190     Item::List changedItems, removedItems;
0191     QHash<QString, Item::List> groupsMap;
0192 
0193     auto fetchJob = qobject_cast<People::PersonFetchJob *>(job);
0194     const auto isIncremental = !fetchJob->syncToken().isEmpty();
0195     const auto objects = fetchJob->items();
0196     qCDebug(GOOGLE_PEOPLE_LOG) << "Retrieved" << objects.count() << "contacts";
0197 
0198     for (const ObjectPtr &object : objects) {
0199         const auto person = object.dynamicCast<People::Person>();
0200         const auto addressee = person->toKContactsAddressee();
0201 
0202         Item item;
0203         item.setMimeType(mimeType());
0204         item.setParentCollection(collection);
0205         item.setRemoteId(person->resourceName());
0206         item.setRemoteRevision(person->etag());
0207         item.setPayload<KContacts::Addressee>(addressee);
0208 
0209         if (person->metadata().deleted() ||
0210             (collection.remoteId() == QString::fromUtf8(otherContactsResourceName) && !person->memberships().isEmpty()) ||
0211             (collection.remoteId() == QString::fromUtf8(myContactsResourceName) && person->memberships().isEmpty())) {
0212             qCDebug(GOOGLE_PEOPLE_LOG) << " - removed" << person->resourceName();
0213             removedItems << item;
0214         } else {
0215             qCDebug(GOOGLE_PEOPLE_LOG) << " - changed" << person->resourceName();
0216             changedItems << item;
0217         }
0218 
0219         const auto memberships = person->memberships();
0220         for (const auto &membership : memberships) {
0221             // We don't link contacts to "My Contacts"
0222             const auto contactGroupResourceName = membership.contactGroupMembership().contactGroupResourceName();
0223             if (contactGroupResourceName != QString::fromUtf8(myContactsResourceName)) {
0224                 groupsMap[contactGroupResourceName] << item;
0225             }
0226         }
0227     }
0228 
0229     if (isIncremental) {
0230         m_iface->itemsRetrievedIncremental(changedItems, removedItems);
0231     } else {
0232         m_iface->itemsRetrieved(changedItems);
0233     }
0234 
0235     for (auto iter = groupsMap.constBegin(); iter != groupsMap.constEnd(); ++iter) {
0236         new LinkJob(m_collections[iter.key()], iter.value(), this);
0237     }
0238     // TODO: unlink if the group was removed!
0239 
0240     collection.setRemoteRevision(fetchJob->receivedSyncToken());
0241     new CollectionModifyJob(collection, this);
0242 
0243     emitReadyStatus();
0244 }
0245 
0246 void PersonHandler::slotPersonCreateJobFinished(KGAPI2::Job *job)
0247 {
0248     if (!m_iface->handleError(job)) {
0249         return;
0250     }
0251 
0252     const auto personCreateJob = qobject_cast<People::PersonCreateJob *>(job);
0253     const auto createdPeople = personCreateJob->items();
0254 
0255     processUpdatedPeople(job, createdPeople);
0256 }
0257 
0258 void PersonHandler::slotKGAPIModifyJobFinished(KGAPI2::Job *job)
0259 {
0260     if (!m_iface->handleError(job)) {
0261         return;
0262     }
0263 
0264     const auto personModifyJob = qobject_cast<ModifyJob *>(job);
0265     const auto modifiedPeople = personModifyJob->items();
0266 
0267     processUpdatedPeople(job, modifiedPeople);
0268 }
0269 
0270 void PersonHandler::processUpdatedPeople(KGAPI2::Job *job, const ObjectsList &updatedPeople)
0271 {
0272     Q_ASSERT(job);
0273     if (updatedPeople.isEmpty()) {
0274         return;
0275     }
0276 
0277     if (job->property(ITEM_PROPERTY).canConvert<Akonadi::Item>()) {
0278         const auto originalItem = job->property(ITEM_PROPERTY).value<Akonadi::Item>();
0279         if (!originalItem.isValid()) {
0280             qCWarning(GOOGLE_PEOPLE_LOG) << "No valid item in received KGAPI job, can't update";
0281             return;
0282         }
0283 
0284         const auto person = updatedPeople.first().dynamicCast<People::Person>();
0285         updatePersonItem(originalItem, person);
0286         emitReadyStatus();
0287 
0288     } else if (job->property(ITEMS_PROPERTY).canConvert<Akonadi::Item::List>()) {
0289         // Be careful not to send an item list or a multi person create job -- only modify jobs.
0290         // At point of creation we do not yet have resource names for the Akonadi items for newly created people.
0291         // This means we will not know which Akonadi items correspond to which person.
0292         const auto originalItems = job->property(ITEMS_PROPERTY).value<Akonadi::Item::List>();
0293         if (originalItems.isEmpty()) {
0294             qCWarning(GOOGLE_PEOPLE_LOG) << "No items in items vector in received KGAPI job, can't update";
0295             return;
0296         }
0297 
0298         for (const auto &personObject : updatedPeople) {
0299             const auto person = personObject.dynamicCast<People::Person>();
0300             const auto matchingItemIt = std::find_if(originalItems.cbegin(), originalItems.cend(), [&person](const Akonadi::Item &item) {
0301                 return item.remoteId() == person->resourceName();
0302             });
0303 
0304             if (matchingItemIt == originalItems.cend()) {
0305                 qCWarning(GOOGLE_PEOPLE_LOG) << "Could not find matching item for person:" << person->resourceName()
0306                                              << "cannot update them properly right now.";
0307                 continue;
0308             }
0309 
0310             const auto matchingItem = *matchingItemIt;
0311             updatePersonItem(matchingItem, person);
0312         }
0313         emitReadyStatus();
0314 
0315     } else {
0316         qCWarning(GOOGLE_PEOPLE_LOG) << "Finished job not carrying actionable item property, cannot update.";
0317     }
0318 }
0319 
0320 void PersonHandler::updatePersonItem(const Akonadi::Item &originalItem, const People::PersonPtr &person)
0321 {
0322     if (person.isNull()) {
0323         qCWarning(GOOGLE_PEOPLE_LOG) << "Received null person ptr, can't update";
0324         return;
0325     } else if (!originalItem.isValid() || !originalItem.hasPayload<KContacts::Addressee>()) {
0326         qCWarning(GOOGLE_PEOPLE_LOG) << "No valid item in received KGAPI job, can't update";
0327         return;
0328     }
0329 
0330     const auto personResourceName = person->resourceName();
0331     if (personResourceName.isEmpty()) {
0332         qCWarning(GOOGLE_PEOPLE_LOG) << "Received person with no resource name, can't update";
0333         return;
0334     }
0335 
0336     if (m_pendingPeoplePhotoUpdate.contains(personResourceName)) {
0337         qCDebug(GOOGLE_PEOPLE_LOG) << "Received updated person response from photo update."
0338                                      << personResourceName;
0339         m_pendingPeoplePhotoUpdate.remove(personResourceName);
0340     } else if (const auto originalAddressee = originalItem.payload<KContacts::Addressee>();
0341                !originalAddressee.photo().isEmpty() && !originalAddressee.photo().rawData().isEmpty()) {
0342         qCDebug(GOOGLE_PEOPLE_LOG) << "Person to update requires a photo update. Sending off photo update job."
0343                                      << personResourceName;
0344 
0345         m_pendingPeoplePhotoUpdate.insert(personResourceName);
0346         const auto addresseePicture = originalAddressee.photo();
0347         const auto pictureRawData = originalAddressee.photo().rawData();
0348         auto job = new People::PersonPhotoUpdateJob(personResourceName, pictureRawData, m_settings->accountPtr(), this);
0349         job->setProperty(ITEM_PROPERTY, QVariant::fromValue(originalItem));
0350         connect(job, &People::PersonPhotoUpdateJob::finished, this, &PersonHandler::slotKGAPIModifyJobFinished);
0351     } else if (originalAddressee.photo().isEmpty() && !person->photos().isEmpty()) {
0352         qCDebug(GOOGLE_PEOPLE_LOG) << "Person to update needs photo deleted. Sending off photo delete job."
0353                                      << personResourceName;
0354 
0355         m_pendingPeoplePhotoUpdate.insert(personResourceName);
0356         auto job = new People::PersonPhotoDeleteJob(personResourceName, m_settings->accountPtr(), this);
0357         job->setProperty(ITEM_PROPERTY, QVariant::fromValue(originalItem));
0358         connect(job, &People::PersonPhotoUpdateJob::finished, this, &PersonHandler::slotKGAPIModifyJobFinished);
0359     }
0360 
0361     Item newItem = originalItem;
0362     qCDebug(GOOGLE_PEOPLE_LOG) << "Person" << personResourceName << "updated";
0363 
0364     newItem.setRemoteId(personResourceName);
0365     newItem.setRemoteRevision(person->etag());
0366     m_iface->itemChangeCommitted(newItem);
0367 
0368     newItem.setPayload<KContacts::Addressee>(person->toKContactsAddressee());
0369     new ItemModifyJob(newItem, this);
0370 }
0371 
0372 void PersonHandler::itemAdded(const Item &item, const Collection &collection)
0373 {
0374     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Adding contact to group '%1'", collection.displayName()));
0375     const auto addressee = item.payload<KContacts::Addressee>();
0376     const auto person = People::Person::fromKContactsAddressee(addressee);
0377 
0378     qCDebug(GOOGLE_PEOPLE_LOG) << "Creating people";
0379 
0380     People::ContactGroupMembership contactGroupMembership;
0381     contactGroupMembership.setContactGroupResourceName(QString::fromUtf8(myContactsResourceName));
0382 
0383     People::Membership membership;
0384     membership.setContactGroupMembership(contactGroupMembership);
0385     person->setMemberships({membership});
0386 
0387     auto job = new People::PersonCreateJob(person, m_settings->accountPtr(), this);
0388     job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item));
0389     connect(job, &People::PersonCreateJob::finished, this, &PersonHandler::slotPersonCreateJobFinished);
0390 }
0391 
0392 void PersonHandler::sendModifyJob(const Akonadi::Item::List &items, const People::PersonList &people)
0393 {
0394     auto job = new People::PersonModifyJob(people, m_settings->accountPtr(), this);
0395     job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items));
0396     connect(job, &People::PersonModifyJob::finished, this, &PersonHandler::slotKGAPIModifyJobFinished);
0397 }
0398 
0399 void PersonHandler::sendModifyJob(const Akonadi::Item &item, const People::PersonPtr &person)
0400 {
0401     auto job = new People::PersonModifyJob(person, m_settings->accountPtr(), this);
0402     job->setProperty(ITEM_PROPERTY, QVariant::fromValue(item));
0403     connect(job, &People::PersonModifyJob::finished, this, &PersonHandler::slotKGAPIModifyJobFinished);
0404 }
0405 
0406 void PersonHandler::itemChanged(const Item &item, const QSet<QByteArray> & /*partIdentifiers*/)
0407 {
0408     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Changing contact"));
0409     qCDebug(GOOGLE_PEOPLE_LOG) << "Changing person" << item.remoteId();
0410 
0411     const auto job = new PeopleConversionJob({item}, this);
0412     connect(job, &PeopleConversionJob::finished, this, [this, item, job] {
0413         const auto person = job->people().first();
0414         sendModifyJob(item, person);
0415         job->deleteLater();
0416     });
0417     job->start();
0418 }
0419 
0420 void PersonHandler::itemsRemoved(const Item::List &items)
0421 {
0422     m_iface->emitStatus(AgentBase::Running, i18ncp("@info:status", "Removing %1 contact", "Removing %1 contacts", items.count()));
0423     QStringList peopleResourceNames;
0424     peopleResourceNames.reserve(items.count());
0425     std::transform(items.cbegin(), items.cend(), std::back_inserter(peopleResourceNames), [](const Item &item) {
0426         return item.remoteId();
0427     });
0428     qCInfo(GOOGLE_PEOPLE_LOG) << "Removing people" << peopleResourceNames;
0429     auto job = new People::PersonDeleteJob(peopleResourceNames, m_settings->accountPtr(), this);
0430     job->setProperty(ITEMS_PROPERTY, QVariant::fromValue(items));
0431     connect(job, &People::PersonDeleteJob::finished, this, &PersonHandler::slotGenericJobFinished);
0432 }
0433 
0434 void PersonHandler::itemsMoved(const Item::List &items, const Collection &collectionSource, const Collection &collectionDestination)
0435 {
0436     const auto sourceRemoteId = collectionSource.remoteId();
0437     const auto destinationRemoteId = collectionDestination.remoteId();
0438     qCDebug(GOOGLE_PEOPLE_LOG) << "Moving people from" << sourceRemoteId << "to" << destinationRemoteId;
0439 
0440     if (!(((sourceRemoteId == QString::fromUtf8(myContactsResourceName)) && (destinationRemoteId == QString::fromUtf8(otherContactsResourceName))) ||
0441           ((sourceRemoteId == QString::fromUtf8(otherContactsResourceName)) && (destinationRemoteId == QString::fromUtf8(myContactsResourceName))))) {
0442         m_iface->cancelTask(i18n("Invalid source or destination collection"));
0443         return;
0444     }
0445 
0446     m_iface->emitStatus(AgentBase::Running,
0447                         i18ncp("@info:status",
0448                                "Moving %1 contact from group '%2' to '%3'",
0449                                "Moving %1 contacts from group '%2' to '%3'",
0450                                items.count(),
0451                                sourceRemoteId,
0452                                destinationRemoteId));
0453 
0454     const auto job = new PeopleConversionJob(items, this);
0455     job->setReparentCollectionRemoteId(destinationRemoteId);
0456     connect(job, &PeopleConversionJob::finished, this, [this, items, job] {
0457         sendModifyJob(items, job->people());
0458         job->deleteLater();
0459     });
0460     job->start();
0461 }
0462 
0463 void PersonHandler::itemsLinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
0464 {
0465     m_iface->emitStatus(AgentBase::Running, i18ncp("@info:status", "Linking %1 contact", "Linking %1 contacts", items.count()));
0466     qCDebug(GOOGLE_PEOPLE_LOG) << "Linking" << items.count() << "contacts to group" << collection.remoteId();
0467 
0468     const auto job = new PeopleConversionJob(items, this);
0469     job->setNewLinkedCollectionRemoteId(collection.remoteId());
0470     connect(job, &PeopleConversionJob::finished, this, [this, items, job] {
0471         sendModifyJob(items, job->people());
0472         job->deleteLater();
0473     });
0474     job->start();
0475 }
0476 
0477 void PersonHandler::itemsUnlinked(const Akonadi::Item::List &items, const Akonadi::Collection &collection)
0478 {
0479     m_iface->emitStatus(AgentBase::Running, i18ncp("@info:status", "Unlinking %1 contact", "Unlinking %1 contacts", items.count()));
0480     qCDebug(GOOGLE_PEOPLE_LOG) << "Unlinking" << items.count() << "contacts to group" << collection.remoteId();
0481 
0482     const auto job = new PeopleConversionJob(items, this);
0483     job->setLinkedCollectionToRemoveRemoteId(collection.remoteId());
0484     connect(job, &PeopleConversionJob::finished, this, [this, items, job] {
0485         sendModifyJob(items, job->people());
0486         job->deleteLater();
0487     });
0488     job->start();
0489 }
0490 
0491 void PersonHandler::updateContactGroupCollection(const Collection &collection, const People::ContactGroupPtr &contactGroup)
0492 {
0493     if (contactGroup.isNull()) {
0494         qCWarning(GOOGLE_PEOPLE_LOG) << "Received null contact group ptr, can't update";
0495         return;
0496     } else if (!collection.isValid()) {
0497         qCWarning(GOOGLE_PEOPLE_LOG) << "No valid collection in received KGAPI job, can't update";
0498         return;
0499     }
0500 
0501     auto newCollection = collectionFromContactGroup(contactGroup);
0502     newCollection.setId(collection.id());
0503 
0504     qCDebug(GOOGLE_PEOPLE_LOG) << "Contact group updated:" << contactGroup->resourceName() << contactGroup->name();
0505 
0506     m_collections[newCollection.remoteId()] = newCollection;
0507     m_iface->collectionChangeCommitted(newCollection);
0508 }
0509 
0510 void PersonHandler::collectionAdded(const Collection &collection, const Collection & /*parent*/)
0511 {
0512     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Creating new contact group '%1'", collection.displayName()));
0513     qCDebug(GOOGLE_PEOPLE_LOG) << "Adding contact group" << collection.displayName();
0514     People::ContactGroupPtr group(new People::ContactGroup);
0515     group->setName(collection.name());
0516 
0517     auto job = new People::ContactGroupCreateJob(group, m_settings->accountPtr(), this);
0518     connect(job, &People::ContactGroupCreateJob::finished, this, [this, collection](KGAPI2::Job *job) {
0519         if (!m_iface->handleError(job)) {
0520             return;
0521         }
0522 
0523         const auto createJob = qobject_cast<People::ContactGroupCreateJob *>(job);
0524         const auto group = createJob->items().first().dynamicCast<People::ContactGroup>();
0525 
0526         updateContactGroupCollection(collection, group);
0527         emitReadyStatus();
0528     });
0529 }
0530 
0531 void PersonHandler::collectionChanged(const Collection &collection)
0532 {
0533     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Changing contact group '%1'", collection.displayName()));
0534     qCDebug(GOOGLE_PEOPLE_LOG) << "Changing contact group" << collection.remoteId();
0535 
0536     People::ContactGroupPtr group(new People::ContactGroup());
0537     group->setResourceName(collection.remoteId());
0538     group->setEtag(collection.remoteRevision());
0539     group->setName(collection.displayName());
0540 
0541     auto job = new People::ContactGroupModifyJob(group, m_settings->accountPtr(), this);
0542     job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection));
0543     connect(job, &People::ContactGroupModifyJob::finished, this, [this, collection](KGAPI2::Job *job) {
0544         if (!m_iface->handleError(job)) {
0545             return;
0546         }
0547 
0548         const auto modifyJob = qobject_cast<People::ContactGroupModifyJob *>(job);
0549         const auto group = modifyJob->items().first().dynamicCast<People::ContactGroup>();
0550 
0551         updateContactGroupCollection(collection, group);
0552         emitReadyStatus();
0553     });
0554 }
0555 
0556 void PersonHandler::collectionRemoved(const Collection &collection)
0557 {
0558     m_iface->emitStatus(AgentBase::Running, i18nc("@info:status", "Removing contact group '%1'", collection.displayName()));
0559     qCDebug(GOOGLE_PEOPLE_LOG) << "Removing contact group" << collection.remoteId();
0560     auto job = new People::ContactGroupDeleteJob(collection.remoteId(), m_settings->accountPtr(), this);
0561     job->setProperty(COLLECTION_PROPERTY, QVariant::fromValue(collection));
0562     connect(job, &People::ContactGroupDeleteJob::finished, this, &PersonHandler::slotGenericJobFinished);
0563 }
0564 
0565 #include "moc_personhandler.cpp"