[Merge] lp:~renatofilho/buteo-sync-plugins-contacts/new-code into lp:buteo-sync-plugins-contacts
Michael Sheldon
michael.sheldon at canonical.com
Mon Jul 20 16:23:34 UTC 2015
Up to line 3000 now, still mostly just small corrections and a few questions.
Diff comments:
>
> === added file 'buteo-contact-client/UContactsBackend.cpp'
> --- buteo-contact-client/UContactsBackend.cpp 1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsBackend.cpp 2015-07-20 14:20:21 +0000
> @@ -0,0 +1,557 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + * 2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + * Mani Chandrasekar <maninc at gmail.com>
> + * Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include "config.h"
> +#include "UContactsBackend.h"
> +#include "UContactsCustomDetail.h"
> +
> +#include <LogMacros.h>
> +
> +#include <QContactTimestamp>
> +#include <QContactIdFilter>
> +#include <QContactIntersectionFilter>
> +#include <QContactSyncTarget>
> +#include <QContactDetailFilter>
> +#include <QContactGuid>
> +#include <QContactDisplayLabel>
> +#include <QContactExtendedDetail>
> +#include <QContactSyncTarget>
> +
> +#include <QBuffer>
> +#include <QSet>
> +#include <QHash>
> +
> +#include <QDBusInterface>
> +#include <QDBusReply>
> +
> +static const QString CPIM_SERVICE_NAME ("com.canonical.pim");
> +static const QString CPIM_ADDRESSBOOK_OBJECT_PATH ("/com/canonical/pim/AddressBook");
> +static const QString CPIM_ADDRESSBOOK_IFACE_NAME ("com.canonical.pim.AddressBook");
> +
> +UContactsBackend::UContactsBackend(const QString &managerName, QObject* parent)
> + : QObject (parent),
> + iMgr(new QContactManager(managerName))
> +{
> + FUNCTION_CALL_TRACE;
> +}
> +
> +UContactsBackend::~UContactsBackend()
> +{
> + FUNCTION_CALL_TRACE;
> +
> + delete iMgr;
> + iMgr = NULL;
> +}
> +
> +bool
> +UContactsBackend::init(uint syncAccount, const QString &syncTarget)
> +{
> + FUNCTION_CALL_TRACE;
> +
> + // create address book it it does not exists
> + // check if the source already exists
> + QContactDetailFilter filter;
> + filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType);
> + filter.setValue(QContactType::TypeGroup);
> +
> + QList<QContact> sources = iMgr->contacts(filter);
> + Q_FOREACH(const QContact &contact, sources) {
> + QContactExtendedDetail exd = UContactsCustomDetail::getCustomField(contact,
> + "ACCOUNT-ID");
> + if (!exd.isEmpty() && (exd.data().toUInt() == syncAccount)) {
> + mSyncTargetId = contact.detail<QContactGuid>().guid();
> + return true;
> + }
> + }
> +
> + // memory/mock manager does not support syncTarget
> + if (iMgr->managerName() != "mock") {
> + // create a new source if necessary
> + QContact contact;
> + contact.setType(QContactType::TypeGroup);
> +
> + QContactDisplayLabel label;
> + label.setLabel(syncTarget);
> + contact.saveDetail(&label);
> +
> + // set the new source as default
> + QContactExtendedDetail isDefault;
> + isDefault.setName("IS-PRIMARY");
> + isDefault.setData(true);
> + contact.saveDetail(&isDefault);
> +
> + // Link source with account
> + QContactExtendedDetail accountId;
> + accountId.setName("ACCOUNT-ID");
> + accountId.setData(syncAccount);
> + contact.saveDetail(&accountId);
> +
> + if (!iMgr->saveContact(&contact)) {
> + qWarning() << "Fail to create contact source:" << syncTarget;
> + return false;
> + }
> +
> + mSyncTargetId = contact.detail<QContactGuid>().guid();
> + }
> +
> + return true;
> +}
> +
> +bool
> +UContactsBackend::uninit()
> +{
> + FUNCTION_CALL_TRACE;
> +
> + return true;
> +}
> +
> +QList<QContactId>
> +UContactsBackend::getAllContactIds()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_ASSERT (iMgr);
> + return iMgr->contactIds(getSyncTargetFilter());
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllNewContactIds(const QDateTime &aTimeStamp)
> +{
> + FUNCTION_CALL_TRACE;
> + LOG_DEBUG("Retrieve New Contacts Since " << aTimeStamp);
> +
> + RemoteToLocalIdMap idList;
> + const QContactChangeLogFilter::EventType eventType =
> + QContactChangeLogFilter::EventAdded;
> +
> + getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> + return idList;
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllModifiedContactIds(const QDateTime &aTimeStamp)
> +{
> +
> + FUNCTION_CALL_TRACE;
> +
> + LOG_DEBUG("Retrieve Modified Contacts Since " << aTimeStamp);
> +
> + RemoteToLocalIdMap idList;
> + const QContactChangeLogFilter::EventType eventType =
> + QContactChangeLogFilter::EventChanged;
> +
> + getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> + return idList;
> +}
> +
> +RemoteToLocalIdMap
> +UContactsBackend::getAllDeletedContactIds(const QDateTime &aTimeStamp)
> +{
> + FUNCTION_CALL_TRACE;
> + LOG_DEBUG("Retrieve Deleted Contacts Since " << aTimeStamp);
> +
> + RemoteToLocalIdMap idList;
> + const QContactChangeLogFilter::EventType eventType =
> + QContactChangeLogFilter::EventRemoved;
> +
> + getSpecifiedContactIds(eventType, aTimeStamp, &idList);
> +
> + return idList;
> +}
> +
> +bool
> +UContactsBackend::addContacts(QList<QContact>& aContactList,
> + QMap<int, UContactsStatus> *aStatusMap)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_ASSERT(iMgr);
> + Q_ASSERT(aStatusMap);
> +
> + QMap<int, QContactManager::Error> errorMap;
> +
> + // Check if contact already exists if it exists set the contact id
> + // to cause an update instead of create a new one
> + for(int i=0; i < aContactList.size(); i++) {
> + QContact &c = aContactList[i];
> + QString remoteId = getRemoteId(c);
> + QContactId id = entryExists(remoteId);
> + if (!id.isNull()) {
> + c.setId(id);
> + } else {
> + // make sure that all contacts retrieved are saved on the correct sync target
> + QContactSyncTarget syncTarget = c.detail<QContactSyncTarget>();
> + syncTarget.setSyncTarget(syncTargetId());
> + c.saveDetail(&syncTarget);
> + }
> +
> + // remove guid field if it exists
> + QContactGuid guid = c.detail<QContactGuid>();
> + if (!guid.isEmpty()) {
> + c.removeDetail(&guid);
> + }
> + }
> +
> + bool retVal = iMgr->saveContacts(&aContactList, &errorMap);
> + if (!retVal) {
> + LOG_WARNING( "Errors reported while saving contacts:" << iMgr->error() );
> + }
> +
> + // QContactManager will populate errorMap only for errors, but we use this as a status map,
> + // so populate NoError if there's no error.
> + for (int i = 0; i < aContactList.size(); i++)
> + {
> + UContactsStatus status;
> + status.id = i;
> + if (!errorMap.contains(i)) {
> + status.errorCode = QContactManager::NoError;
> + } else {
> + LOG_WARNING("Contact with id " << aContactList.at(i).id() << " and index " << i <<" is in error");
> + status.errorCode = errorMap.value(i);
> + }
> + aStatusMap->insert(i, status);
> + }
> +
> + return retVal;
> +}
> +
> +QMap<int,UContactsStatus>
> +UContactsBackend::modifyContacts(QList<QContact> *aContactList)
> +{
> + FUNCTION_CALL_TRACE;
> +
> + Q_ASSERT (iMgr);
> + UContactsStatus status;
> +
> + QMap<int,QContactManager::Error> errors;
> + QMap<int,UContactsStatus> statusMap;
> +
> + // WORKAROUND: Our backend uses GUid as contact id due problems with contact id serialization
> + // we can not use this field
> + for (int i = 0; i < aContactList->size(); i++) {
> + QContact &newContact = (*aContactList)[i];
> + QString remoteId = getRemoteId(newContact);
> +
> + // if the contact was created the remoteId will not exists on local database
> + QContactId localId = entryExists(remoteId);
> +
> + // nt this case we should use the guid stored on contact
nt -> in
> + QContactGuid guid = newContact.detail<QContactGuid>();
> +
> + if (localId.isNull() && !guid.isEmpty()) {
> + // try the guid (should contains the local id) field
> + localId = QContactId::fromString(guid.guid());
> + }
> + newContact.setId(localId);
> + newContact.removeDetail(&guid);
> + }
> +
> + if(iMgr->saveContacts(aContactList , &errors)) {
> + LOG_DEBUG("Batch Modification of Contacts Succeeded");
> + } else {
> + LOG_DEBUG("Batch Modification of Contacts Failed");
> + }
> +
> + // QContactManager will populate errorMap only for errors, but we use this as a status map,
> + // so populate NoError if there's no error.
> + // TODO QContactManager populates indices from the aContactList, but we populate keys, is this OK?
> + for (int i = 0; i < aContactList->size(); i++) {
> + QContactId contactId = aContactList->at(i).id();
> + if( !errors.contains(i) ) {
> + LOG_DEBUG("No error for contact with id " << contactId << " and index " << i);
> + status.errorCode = QContactManager::NoError;
> + statusMap.insert(i, status);
> + } else {
> + LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error");
> + QContactManager::Error errorCode = errors.value(i);
> + status.errorCode = errorCode;
> + statusMap.insert(i, status);
> + }
> + }
> + return statusMap;
> +}
> +
> +QMap<int, UContactsStatus>
> +UContactsBackend::deleteContacts(const QStringList &aContactIDList)
> +{
> + FUNCTION_CALL_TRACE;
> +
> + QList<QContactId> qContactIdList;
> + foreach (QString id, aContactIDList) {
> + qContactIdList.append(QContactId::fromString(id));
> + }
> +
> + return deleteContacts(qContactIdList);
> +}
> +
> +QMap<int, UContactsStatus>
> +UContactsBackend::deleteContacts(const QList<QContactId> &aContactIDList) {
> + FUNCTION_CALL_TRACE;
> +
> + Q_ASSERT (iMgr);
> + UContactsStatus status;
> + QMap<int, QContactManager::Error> errors;
> + QMap<int, UContactsStatus> statusMap;
> +
> + if(iMgr->removeContacts(aContactIDList , &errors)) {
> + LOG_DEBUG("Successfully Removed all contacts ");
> + }
> + else {
> + LOG_WARNING("Failed Removing Contacts");
> + }
> +
> + // QContactManager will populate errorMap only for errors, but we use this as a status map,
> + // so populate NoError if there's no error.
> + for (int i = 0; i < aContactIDList.size(); i++) {
> + QContactId contactId = aContactIDList.value(i);
> + if( !errors.contains(i) )
> + {
> + LOG_DEBUG("No error for contact with id " << contactId << " and index " << i);
> + status.errorCode = QContactManager::NoError;
> + statusMap.insert(i, status);
> + }
> + else
> + {
> + LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error");
> + QContactManager::Error errorCode = errors.value(i);
> + status.errorCode = errorCode;
> + statusMap.insert(i, status);
> + }
> + }
> +
> + return statusMap;
> +}
> +
> +
> +void
> +UContactsBackend::getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType,
> + const QDateTime& aTimeStamp,
> + RemoteToLocalIdMap *aIdList)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_ASSERT(aIdList);
> +
> + QList<QContactId> localIdList;
> + QContactChangeLogFilter filter(aEventType);
> + filter.setSince(aTimeStamp);
> +
> + localIdList = iMgr->contactIds(filter & getSyncTargetFilter());
> + LOG_DEBUG("Local ID added = " << localIdList.size() << " Datetime from when this " << aTimeStamp.toString());
> + // Filter out ids for items that were added after the specified time.
> + if (aEventType != QContactChangeLogFilter::EventAdded)
> + {
> + filter.setEventType(QContactChangeLogFilter::EventAdded);
> + QList<QContactId> addedList = iMgr->contactIds(filter & getSyncTargetFilter());
> + foreach (const QContactId &id, addedList)
> + {
> + localIdList.removeAll(id);
> + }
> + }
> +
> + // This is a defensive procedure to prevent duplicate items being sent.
> + // QSet does not allow duplicates, thus transforming QList to QSet and back
> + // again will remove any duplicate items in the original QList.
> + int originalIdCount = localIdList.size();
> + QSet<QContactId> idSet = localIdList.toSet();
> + int idCountAfterDupRemoval = idSet.size();
> +
> + LOG_DEBUG("Item IDs found (returned / incl. duplicates): " << idCountAfterDupRemoval << "/" << originalIdCount);
> + if (originalIdCount != idCountAfterDupRemoval) {
> + LOG_WARNING("Contacts backend returned duplicate items for requested list");
> + LOG_WARNING("Duplicate item IDs have been removed");
> + } // no else
> +
> + localIdList = idSet.toList();
> +
> + QContactFetchHint remoteIdHint;
> + QList <QContactDetail::DetailType> detailTypes;
> + detailTypes << QContactExtendedDetail::Type;
> + remoteIdHint.setDetailTypesHint(detailTypes);
> +
> + QList<QContact> contacts = iMgr->contacts(localIdList, remoteIdHint);
> + foreach (const QContact &contact, contacts) {
> + QString rid = getRemoteId(contact);
> + aIdList->insertMulti(rid, contact.id());
> + }
> +}
> +
> +/*!
> + \fn GContactsBackend::getContact(QContactId aContactId)
> + */
> +QContact
> +UContactsBackend::getContact(const QContactId& aContactId)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_ASSERT (iMgr);
> + QList<QContact> returnedContacts;
> +
> + LOG_DEBUG("Contact ID to be retreived = " << aContactId.toString());
> + returnedContacts = iMgr->contacts(QList<QContactId>() << aContactId);
> +
> + LOG_DEBUG("Contacts retreived from Contact manager = " << returnedContacts.count());
> + return returnedContacts.value(0, QContact());
> +}
> +
> +QContact
> +UContactsBackend::getContact(const QString& remoteId)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_ASSERT (iMgr);
> + LOG_DEBUG("Remote id to be searched for = " << remoteId);
> +
> + //FIXME: use guid field when supported by address-book-service
> + QContactIntersectionFilter remoteIdFilter = getRemoteIdFilter(remoteId);
> + QList<QContact> contactList = iMgr->contacts(remoteIdFilter & getSyncTargetFilter());
> + if (contactList.size() > 0) {
> + return contactList.at(0);
> + }
> + return QContact();
> +}
> +
> +QContactId
> +UContactsBackend::entryExists(const QString remoteId)
> +{
> + if (remoteId.isEmpty()) {
> + return QContactId();
> + }
> +
> + QContactFilter ridFilter = getRemoteIdFilter(remoteId);
> + QList<QContactId> idList = iMgr->contactIds(ridFilter & getSyncTargetFilter());
> + if (idList.size () > 0)
> + return idList.first ();
> + else
> + return QContactId();
> +}
> +
> +QString
> +UContactsBackend::syncTargetId() const
> +{
> + return mSyncTargetId;
> +}
> +
> +const QStringList
> +UContactsBackend::localIds(const QStringList remoteIds)
> +{
> + QStringList localIdList;
> + foreach (QString guid , remoteIds) {
> + QString localId = entryExists(guid).toString();
> + if (!localId.isEmpty()) {
> + localIdList << localId;
> + }
> + }
> + Q_ASSERT(localIdList.count() == remoteIds.count());
> + return localIdList;
> +}
> +
> +QContactFilter
> +UContactsBackend::getRemoteIdFilter(const QString &remoteId) const
> +{
> + QContactIntersectionFilter remoteFilter;
> +
> + QContactDetailFilter xDetailNameFilter;
> + xDetailNameFilter.setDetailType(QContactExtendedDetail::Type,
> + QContactExtendedDetail::FieldName);
> + xDetailNameFilter.setValue(UContactsCustomDetail::FieldRemoteId);
> +
> + QContactDetailFilter xDetailValueFilter;
> + xDetailValueFilter.setDetailType(QContactExtendedDetail::Type,
> + QContactExtendedDetail::FieldData);
> + xDetailValueFilter.setValue(remoteId);
> +
> + remoteFilter << xDetailNameFilter
> + << xDetailValueFilter;
> + return remoteFilter;
> +}
> +
> +QString
> +UContactsBackend::getRemoteId(const QContact &contact)
> +{
> + return UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldRemoteId).data().toString();
> +}
> +
> +void UContactsBackend::setRemoteId(QContact &contact, const QString &remoteId)
> +{
> + UContactsCustomDetail::setCustomField(contact, UContactsCustomDetail::FieldRemoteId, QVariant(remoteId));
> +}
> +
> +QString UContactsBackend::getLocalId(const QContact &contact)
> +{
> + QContactGuid guid = contact.detail<QContactGuid>();
> + return guid.guid();
> +}
> +
> +void UContactsBackend::setLocalId(QContact &contact, const QString &localId)
> +{
> + QContactGuid guid = contact.detail<QContactGuid>();
> + guid.setGuid(localId);
> + contact.saveDetail(&guid);
> +}
> +
> +bool UContactsBackend::deleted(const QContact &contact)
> +{
> + QString deletedAt = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldDeletedAt).data().toString();
> + return !deletedAt.isEmpty();
> +}
> +
> +void
> +UContactsBackend::purgecontacts()
> +{
> + QDBusInterface iface(CPIM_SERVICE_NAME,
> + CPIM_ADDRESSBOOK_OBJECT_PATH,
> + CPIM_ADDRESSBOOK_IFACE_NAME);
> + QDBusReply<void> reply = iface.call("purgeContacts", QString(""), mSyncTargetId);
> + if (reply.error().isValid()) {
> + LOG_WARNING("Fail to purge contacts" << reply.error());
> + } else {
> + LOG_DEBUG("Purged backend contacts");
> + }
> +}
> +
> +QContactFilter
> +UContactsBackend::getSyncTargetFilter() const
> +{
> + // user entered contacts, i.e. all other contacts that are not sourcing
> + // from restricted backends or instant messaging service
> + static QContactDetailFilter detailFilterDefaultSyncTarget;
> +
> + if (!mSyncTargetId.isEmpty() &&
> + detailFilterDefaultSyncTarget.value().isNull()) {
> + detailFilterDefaultSyncTarget.setDetailType(QContactSyncTarget::Type,
> + QContactSyncTarget::FieldSyncTarget + 1);
> + detailFilterDefaultSyncTarget.setValue(mSyncTargetId);
> + } else if (mSyncTargetId.isEmpty()) {
> + return QContactFilter();
> + }
> +
> + // return the union
> + return detailFilterDefaultSyncTarget;
> +}
> +
> +
> +QtContacts::QContactManager *UContactsBackend::manager() const
> +{
> + return iMgr;
> +}
>
> === added file 'buteo-contact-client/UContactsClient.cpp'
> --- buteo-contact-client/UContactsClient.cpp 1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsClient.cpp 2015-07-20 14:20:21 +0000
> @@ -0,0 +1,946 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + * 2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + * Mani Chandrasekar <maninc at gmail.com>
> + * Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#include "UContactsClient.h"
> +#include "UContactsBackend.h"
> +#include "UAbstractRemoteSource.h"
> +#include "UAuth.h"
> +#include "config.h"
> +
> +//Buteo
> +#include <LogMacros.h>
> +#include <ProfileEngineDefs.h>
> +#include <ProfileManager.h>
> +
> +#include <QLibrary>
> +#include <QtNetwork>
> +#include <QDateTime>
> +#include <QContactGuid>
> +#include <QContactDetailFilter>
> +#include <QContactAvatar>
> +
> +class UContactsClientPrivate
> +{
> +public:
> + UContactsClientPrivate(const QString &serviceName)
> + : mAuth(0),
> + mContactBackend(0),
> + mRemoteSource(0),
> + mServiceName(serviceName)
> + {
> + }
> +
> + UAuth* mAuth;
> + UContactsBackend* mContactBackend;
> + UAbstractRemoteSource* mRemoteSource;
> + bool mSlowSync;
> + QString mServiceName;
> + // local database information
> + QSet<QContactId> mAllLocalContactIds;
> + RemoteToLocalIdMap mAddedContactIds;
> + RemoteToLocalIdMap mModifiedContactIds;
> + RemoteToLocalIdMap mDeletedContactIds;
> + // sync report
> + QMap<QString, Buteo::DatabaseResults> mItemResults;
> + Buteo::SyncResults mResults;
> + // sync profile
> + QString mSyncTarget;
> + qint32 mAccountId;
> + Buteo::SyncProfile::SyncDirection mSyncDirection;
> + Buteo::SyncProfile::ConflictResolutionPolicy mConflictResPolicy;
> +};
> +
> +UContactsClient::UContactsClient(const QString& aPluginName,
> + const Buteo::SyncProfile& aProfile,
> + Buteo::PluginCbInterface *aCbInterface, const QString &serviceName)
> + : ClientPlugin(aPluginName, aProfile, aCbInterface),
> + d_ptr(new UContactsClientPrivate(serviceName))
> +{
> + FUNCTION_CALL_TRACE;
> +}
> +
> +UContactsClient::~UContactsClient()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + delete d->mAuth;
> + delete d->mRemoteSource;
> +}
> +
> +bool
> +UContactsClient::init()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + if (lastSyncTime().isNull()) {
> + d->mSlowSync = true;
> + } else {
> + d->mSlowSync = false;
> + }
> +
> + LOG_DEBUG ("Last sync date:" << lastSyncTime() << "Using slow sync?" << d->mSlowSync);
> + if (!initConfig()) {
> + LOG_CRITICAL("Fail to init configuration");
> + return false;
> + }
> +
> + d->mAuth = crateAuthenticator(this);
> + if (!d->mAuth || !d->mAuth->init(d->mAccountId, d->mServiceName)) {
> + LOG_CRITICAL("Fail to create auth object");
> + goto init_fail;
> + }
> +
> + d->mContactBackend = createContactsBackend(this);
> + if (!d->mContactBackend || !d->mContactBackend->init(d->mAccountId,
> + d->mAuth->accountDisplayName())) {
> + LOG_CRITICAL("Fail to create contact backend");
> + goto init_fail;
> + }
> +
> +
> + // remote source must be initialized after mAuth because its uses the account name property
> + d->mRemoteSource = createRemoteSource(this);
> + if (!d->mRemoteSource) {
> + LOG_CRITICAL("Fail to create remote contact backend");
> + goto init_fail;
> + }
> +
> + // sign in.
> + connect(d->mAuth, SIGNAL(success()), SLOT(start()));
> + connect(d->mAuth, SIGNAL(failed()), SLOT(onAuthenticationError()));
> +
> + // syncStateChanged to signal changes from CONNECTING, RECEIVING
> + // SENDING, DISCONNECTING, CLOSED
> + connect(this,
> + SIGNAL(stateChanged(Sync::SyncProgressDetail)),
> + SLOT(onStateChanged(Sync::SyncProgressDetail)));
> +
> + // Take necessary action when sync is finished
> + connect(this,
> + SIGNAL(syncFinished(Sync::SyncStatus)),
> + SLOT(onSyncFinished(Sync::SyncStatus)));
> +
> + return true;
> +
> +init_fail:
> +
> + delete d->mRemoteSource;
> + delete d->mContactBackend;
> + delete d->mAuth;
> + d->mRemoteSource = 0;
> + d->mContactBackend = 0;
> + d->mAuth = 0;
> + return false;
> +}
> +
> +bool
> +UContactsClient::uninit()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + delete d->mRemoteSource;
> + delete d->mContactBackend;
> + delete d->mAuth;
> + d->mRemoteSource = 0;
> + d->mContactBackend = 0;
> + d->mAuth = 0;
> +
> + return true;
> +}
> +
> +bool
> +UContactsClient::isReadyToSync() const
> +{
> + const Q_D(UContactsClient);
> + return (d->mContactBackend && d->mRemoteSource && d->mAuth);
> +}
> +
> +UContactsBackend *UContactsClient::createContactsBackend(QObject *parent) const
> +{
> + return new UContactsBackend(QCONTACTS_BACKEND_NAME, parent);
> +}
> +
> +UAuth *UContactsClient::crateAuthenticator(QObject *parent) const
> +{
> + return new UAuth(parent);
> +}
> +
> +bool
> +UContactsClient::startSync()
> +{
> + FUNCTION_CALL_TRACE;
> +
> + if (!isReadyToSync()) {
> + LOG_WARNING ("Ubuntu plugin is not ready to sync.");
> + return false;
> + }
> +
> + Q_D(UContactsClient);
> + LOG_DEBUG ("Init done. Continuing with sync");
> +
> + return d->mAuth->authenticate();
> +}
> +
> +void
> +UContactsClient::abortSync(Sync::SyncStatus aStatus)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + d->mRemoteSource->abort();
> + emit syncFinished(Sync::SYNC_ABORTED);
> +}
> +
> +bool
> +UContactsClient::initConfig()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + //TODO: support multiple remote databases "scopes"
> + QStringList accountList = iProfile.keyValues(Buteo::KEY_ACCOUNT_ID);
> + if (!accountList.isEmpty()) {
> + QString aId = accountList.first();
> + if (aId != NULL) {
> + d->mAccountId = aId.toInt();
> + }
> + } else {
> + d->mAccountId = 0;
> + LOG_WARNING("Account id not found in config profile");
> + return false;
> + }
> +
> + QStringList databaseName = iProfile.keyValues(Buteo::KEY_DISPLAY_NAME);
> + if (databaseName.isEmpty()) {
> + LOG_WARNING("\"displayname\" is missing on configuration file");
> + return false;
> + }
> + d->mSyncTarget = databaseName.first();
> + d->mSyncDirection = iProfile.syncDirection();
> + d->mConflictResPolicy = iProfile.conflictResolutionPolicy();
> + d->mItemResults.insert(d->mSyncTarget, Buteo::DatabaseResults());
> +
> + return true;
> +}
> +
> +void
> +UContactsClient::onAuthenticationError()
> +{
> + LOG_WARNING("Fail to authenticate with account");
> + emit syncFinished (Sync::SYNC_AUTHENTICATION_FAILURE);
> +}
> +
> +bool
> +UContactsClient::start()
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + /*
> + 1. If no previous sync, go for slow-sync. Fetch all contacts
> + from server
> + 2. Check if previous sync happened (from SyncLog). If yes,
> + fetch the time of last sync
> + 3. Using the last sync time, retrieve all contacts from server
> + that were added/modified/deleted
> + 4. Fetch all added/modified/deleted items from device
> + 5. Check for conflicts. Take the default policy as "server-wins"
> + 6. Save the list from the server to device
> + 7. Push "client changes" - "conflicting items" to the server
> + 8. Save the sync log
> + */
> +
> + // Remote source will be create after authentication since it needs some information
> + // about the authentication (auth-token, etc..)
> +
> + if (!d->mRemoteSource->init(remoteSourceProperties())) {
> + LOG_WARNING("Fail to init remote source");
> + return false;
> + }
> +
> + switch (d->mSyncDirection)
> + {
> + case Buteo::SyncProfile::SYNC_DIRECTION_TWO_WAY:
> + {
> + QDateTime sinceDate = d->mSlowSync ? QDateTime() : lastSyncTime();
> +
> + LOG_DEBUG("load all contacts since" << sinceDate << sinceDate.isValid());
> + // load changed contact since the last sync date or all contacts if no
> + // sync was done before
> + loadLocalContacts(sinceDate);
> +
> + // load remote contacts
> + if (d->mSlowSync) {
> + connect(d->mRemoteSource,
> + SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus)),
> + SLOT(onRemoteContactsFetchedForSlowSync(QList<QtContacts::QContact>,Sync::SyncStatus)));
> + } else {
> + connect(d->mRemoteSource,
> + SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus)),
> + SLOT(onRemoteContactsFetchedForFastSync(QList<QtContacts::QContact>,Sync::SyncStatus)));
> + }
> + d->mRemoteSource->fetchContacts(sinceDate, !d->mSlowSync, true);
> + break;
> + }
> + case Buteo::SyncProfile::SYNC_DIRECTION_FROM_REMOTE:
> + LOG_WARNING("SYNC_DIRECTION_FROM_REMOTE: not implemented");
> + return false;
> + case Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE:
> + LOG_WARNING("SYNC_DIRECTION_TO_REMOTE: not implemented");
> + return false;
> + case Buteo::SyncProfile::SYNC_DIRECTION_UNDEFINED:
> + // Not required
> + default:
> + // throw configuration error
> + return false;
> + break;
> + };
> +
> + return true;
> +}
> +
> +QList<QContact>
> +UContactsClient::prepareContactsToUpload(UContactsBackend *backend,
> + const QSet<QContactId> &ids)
> +{
> + QList<QContact> toUpdate;
> +
> + foreach(const QContactId &id, ids) {
> + QContact contact = backend->getContact(id);
> + if (!contact.isEmpty()) {
> + toUpdate << contact;
> + } else {
> + LOG_CRITICAL("Fail to find local contact with id:" << id);
> + return QList<QContact>();
> + }
> + }
> +
> + return toUpdate;
> +}
> +
> +void
> +UContactsClient::onRemoteContactsFetchedForSlowSync(const QList<QContact> contacts,
> + Sync::SyncStatus status)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> + disconnect(d->mRemoteSource);
> + }
> +
> + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> + // save remote contacts locally
> + storeToLocalForSlowSync(contacts);
> +
> + if (status == Sync::SYNC_DONE) {
> + QList<QContact> toUpload = prepareContactsToUpload(d->mContactBackend, d->mAllLocalContactIds);
> + connect(d->mRemoteSource,
> + SIGNAL(transactionCommited(QList<QtContacts::QContact>,
> + QList<QtContacts::QContact>,
> + QStringList,Sync::SyncStatus)),
> + SLOT(onContactsSavedForSlowSync(QList<QtContacts::QContact>,
> + QList<QtContacts::QContact>,
> + QStringList,Sync::SyncStatus)));
> +
> + d->mRemoteSource->transaction();
> + d->mRemoteSource->saveContacts(toUpload);
> + d->mRemoteSource->commit();
> + }
> + } else {
> + emit syncFinished(status);
> + }
> +}
> +
> +void
> +UContactsClient::onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts,
> + const QList<QtContacts::QContact> &updatedContacts,
> + const QStringList &removedContacts,
> + Sync::SyncStatus status)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + LOG_DEBUG("AFTER UPLOAD(Slow sync):"
> + << "\n\tCreated on remote:" << createdContacts.size()
> + << "\n\tUpdated on remote:" << updatedContacts.size()
> + << "\n\tRemoved from remote:" << removedContacts.size());
> +
> + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> + disconnect(d->mRemoteSource);
> + }
> +
> + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> + QList<QContact> changedContacts;
> +
> + changedContacts += createdContacts;
> + changedContacts += updatedContacts;
> + updateIdsToLocal(changedContacts);
> +
> + // sync report
> + addProcessedItem(Sync::ITEM_ADDED,
> + Sync::REMOTE_DATABASE,
> + syncTargetId(),
> + createdContacts.size());
> +
> + if (status == Sync::SYNC_PROGRESS) {
> + // sync still in progress
> + return;
> + } else {
> + // WORKARDOUND: 'galera' contacts service take a while to fire contacts
> + // changed singal, this can cause a new sync due the storage change plugin
> + // lets wait 2 secs before fire sync finished signal
> + QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully()));
> + return;
> + }
> + }
> +
> + emit syncFinished(status);
> +}
> +
> +void UContactsClient::onRemoteContactsFetchedForFastSync(const QList<QContact> contacts,
> + Sync::SyncStatus status)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> + disconnect(d->mRemoteSource);
> + }
> +
> + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> + // save remote contacts locally
> + storeToLocalForFastSync(contacts);
> +
> + if (status == Sync::SYNC_DONE) {
> + QList<QContact> contactsToUpload;
> + QList<QContact> contactsToRemove;
> +
> + // Contacts created locally
> + LOG_DEBUG("Total number of Contacts ADDED : " << d->mAddedContactIds.count());
> + contactsToUpload = prepareContactsToUpload(d->mContactBackend,
> + d->mAddedContactIds.values().toSet());
> +
> + // Contacts modified locally
> + LOG_DEBUG("Total number of Contacts MODIFIED : " << d->mModifiedContactIds.count());
> + contactsToUpload += prepareContactsToUpload(d->mContactBackend,
> + d->mModifiedContactIds.values().toSet());
> +
> + // Contacts deleted locally
> + LOG_DEBUG("Total number of Contacts DELETED : " << d->mDeletedContactIds.count());
> + contactsToRemove = prepareContactsToUpload(d->mContactBackend,
> + d->mDeletedContactIds.values().toSet());
> +
> + connect(d->mRemoteSource,
> + SIGNAL(transactionCommited(QList<QtContacts::QContact>,
> + QList<QtContacts::QContact>,
> + QStringList,Sync::SyncStatus)),
> + SLOT(onContactsSavedForFastSync(QList<QtContacts::QContact>,
> + QList<QtContacts::QContact>,
> + QStringList,Sync::SyncStatus)));
> +
> + d->mRemoteSource->transaction();
> + d->mRemoteSource->saveContacts(contactsToUpload);
> + d->mRemoteSource->removeContacts(contactsToRemove);
> + d->mRemoteSource->commit();
> + }
> + } else {
> + emit syncFinished(status);
> + }
> +}
> +
> +void
> +UContactsClient::onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts,
> + const QList<QtContacts::QContact> &updatedContacts,
> + const QStringList &removedContacts,
> + Sync::SyncStatus status)
> +{
> + Q_UNUSED(updatedContacts)
> + Q_UNUSED(removedContacts)
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + LOG_DEBUG("AFTER UPLOAD(Fast sync):" << status
> + << "\n\tCreated on remote:" << createdContacts.size()
> + << "\n\tUpdated on remote:" << updatedContacts.size()
> + << "\n\tRemoved from remote:" << removedContacts.size());
> +
> + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) {
> + disconnect(d->mRemoteSource);
> + }
> +
> + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) {
> + QList<QContact> changedContacts;
> +
> + changedContacts += createdContacts;
> + changedContacts += updatedContacts;
> +
> + updateIdsToLocal(changedContacts);
> +
> + // sync report
> + addProcessedItem(Sync::ITEM_ADDED,
> + Sync::REMOTE_DATABASE,
> + syncTargetId(),
> + createdContacts.size());
> + addProcessedItem(Sync::ITEM_MODIFIED,
> + Sync::REMOTE_DATABASE,
> + syncTargetId(),
> + updatedContacts.size());
> + addProcessedItem(Sync::ITEM_DELETED,
> + Sync::REMOTE_DATABASE,
> + syncTargetId(),
> + removedContacts.size());
> +
> + if (status == Sync::SYNC_PROGRESS) {
> + // sync still in progress
> + return;
> + } else {
> + // WORKARDOUND: 'galera' contacts service take a while to fir contacts
to fir -> for
> + // changed singal, this can cause a new sync due the storage change plugin
> + // lets wait 2 secs before fire sync finished signal
> + QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully()));
> + return;
> + }
> + }
> +
> + emit syncFinished(status);
> +}
> +
> +bool
> +UContactsClient::storeToLocalForSlowSync(const QList<QContact> &remoteContacts)
> +{
> + FUNCTION_CALL_TRACE;
> +
> + Q_D(UContactsClient);
> + Q_ASSERT(d->mSlowSync);
> +
> + bool syncSuccess = false;
> +
> + LOG_DEBUG ("@@@storeToLocal#SLOW SYNC");
> + // Since we request for all the deleted contacts, if
> + // slow sync is performed many times, even deleted contacts
> + // will appear in *remoteContacts. Filter them out while
> + // saving them to device
> + LOG_DEBUG ("TOTAL REMOTE CONTACTS:" << remoteContacts.size());
> +
> + if (!remoteContacts.isEmpty()) {
> + QMap<int, UContactsStatus> statusMap;
> + QList<QContact> cpyContacts(remoteContacts);
> + if (d->mContactBackend->addContacts(cpyContacts, &statusMap)) {
> + // TODO: Saving succeeded. Update sync results
Should this TODO be addressed?
> + syncSuccess = true;
> +
> + // sync report
> + addProcessedItem(Sync::ITEM_ADDED,
> + Sync::LOCAL_DATABASE,
> + syncTargetId(),
> + cpyContacts.count());
> + } else {
> + // TODO: Saving failed. Update sync results and probably stop sync
Same as above
> + syncSuccess = false;
> + }
> + }
> +
> + return syncSuccess;
> +}
> +
> +bool
> +UContactsClient::storeToLocalForFastSync(const QList<QContact> &remoteContacts)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> + Q_ASSERT(!d->mSlowSync);
> +
> + bool syncSuccess = false;
> + LOG_DEBUG ("@@@storeToLocal#FAST SYNC");
> + QList<QContact> remoteAddedContacts, remoteModifiedContacts, remoteDeletedContacts;
> + filterRemoteAddedModifiedDeletedContacts(remoteContacts,
> + remoteAddedContacts,
> + remoteModifiedContacts,
> + remoteDeletedContacts);
> +
> + resolveConflicts(remoteModifiedContacts, remoteDeletedContacts);
> +
> + if (!remoteAddedContacts.isEmpty()) {
> + LOG_DEBUG ("***Adding " << remoteAddedContacts.size() << " contacts");
> + QMap<int, UContactsStatus> addedStatusMap;
> + syncSuccess = d->mContactBackend->addContacts(remoteAddedContacts, &addedStatusMap);
> +
> + if (syncSuccess) {
> + // sync report
> + addProcessedItem(Sync::ITEM_ADDED,
> + Sync::LOCAL_DATABASE,
> + syncTargetId(),
> + remoteAddedContacts.count());
> + }
> + }
> +
> + if (!remoteModifiedContacts.isEmpty()) {
> + LOG_DEBUG ("***Modifying " << remoteModifiedContacts.size() << " contacts");
> + QMap<int, UContactsStatus> modifiedStatusMap =
> + d->mContactBackend->modifyContacts(&remoteModifiedContacts);
> +
> + syncSuccess = (modifiedStatusMap.size() > 0);
> +
> + if (syncSuccess) {
> + // sync report
> + addProcessedItem(Sync::ITEM_MODIFIED,
> + Sync::LOCAL_DATABASE,
> + syncTargetId(),
> + modifiedStatusMap.size());
> + }
> + }
> +
> + if (!remoteDeletedContacts.isEmpty()) {
> + LOG_DEBUG ("***Deleting " << remoteDeletedContacts.size() << " contacts");
> + QStringList guidList;
> + for (int i=0; i<remoteDeletedContacts.size(); i++) {
> + guidList << UContactsBackend::getRemoteId(remoteDeletedContacts.at(i));
> + }
> +
> + QStringList localIdList = d->mContactBackend->localIds(guidList);
> + QMap<int, UContactsStatus> deletedStatusMap =
> + d->mContactBackend->deleteContacts(localIdList);
> +
> + syncSuccess = (deletedStatusMap.size() > 0);
> + if (syncSuccess) {
> + // sync report
> + addProcessedItem(Sync::ITEM_DELETED,
> + Sync::LOCAL_DATABASE,
> + syncTargetId(),
> + localIdList.size());
> + }
> + }
> +
> + return syncSuccess;
> +}
> +
> +bool
> +UContactsClient::cleanUp()
> +{
> + FUNCTION_CALL_TRACE;
> + //TODO
What isn't being cleaned up at the moment?
> + return true;
> +}
> +
> +void UContactsClient::connectivityStateChanged(Sync::ConnectivityType aType, bool aState)
> +{
> + FUNCTION_CALL_TRACE;
> + LOG_DEBUG("Received connectivity change event:" << aType << " changed to " << aState);
> +}
> +
> +void
> +UContactsClient::loadLocalContacts(const QDateTime &since)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + if (!since.isValid()) {
> + d->mAllLocalContactIds = d->mContactBackend->getAllContactIds().toSet();
> +
> + LOG_DEBUG ("Number of contacts:" << d->mAllLocalContactIds.size ());
> + } else {
> + d->mAddedContactIds = d->mContactBackend->getAllNewContactIds(since);
> + d->mModifiedContactIds = d->mContactBackend->getAllModifiedContactIds(since);
> + d->mDeletedContactIds = d->mContactBackend->getAllDeletedContactIds(since);
> +
> + LOG_DEBUG ("Number of local added contacts:" << d->mAddedContactIds.size());
> + LOG_DEBUG ("Number of local modified contacts:" << d->mModifiedContactIds.size());
> + LOG_DEBUG ("Number of local removed contacts:" << d->mDeletedContactIds.size());
> + }
> +}
> +
> +void
> +UContactsClient::onStateChanged(Sync::SyncProgressDetail aState)
> +{
> + FUNCTION_CALL_TRACE;
> +
> + switch(aState) {
> + case Sync::SYNC_PROGRESS_SENDING_ITEMS: {
> + emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_SENDING_ITEMS);
> + break;
> + }
> + case Sync::SYNC_PROGRESS_RECEIVING_ITEMS: {
> + emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_RECEIVING_ITEMS);
> + break;
> + }
> + case Sync::SYNC_PROGRESS_FINALISING: {
> + emit syncProgressDetail(getProfileName(), Sync::SYNC_PROGRESS_FINALISING);
> + break;
> + }
> + default:
> + //do nothing
> + break;
> + };
> +}
> +
> +void
> +UContactsClient::onSyncFinished(Sync::SyncStatus aState)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + switch(aState)
> + {
> + case Sync::SYNC_ERROR:
> + case Sync::SYNC_AUTHENTICATION_FAILURE:
> + case Sync::SYNC_DATABASE_FAILURE:
> + case Sync::SYNC_CONNECTION_ERROR:
> + case Sync::SYNC_NOTPOSSIBLE:
> + {
> + generateResults(false);
> + emit error(getProfileName(), "", aState);
> + break;
> + }
> + case Sync::SYNC_DONE:
> + // purge all deleted contacts
> + d->mContactBackend->purgecontacts();
> + case Sync::SYNC_ABORTED:
> + {
> + generateResults(true);
> + emit success(getProfileName(), QString::number(aState));
> + break;
> + }
> + case Sync::SYNC_QUEUED:
> + case Sync::SYNC_STARTED:
> + case Sync::SYNC_PROGRESS:
> + default:
> + {
> + generateResults(false);
> + emit error(getProfileName(), "", aState);
> + break;
> + }
> + }
> +}
> +
> +void UContactsClient::fireSyncFinishedSucessfully()
> +{
> + emit syncFinished(Sync::SYNC_DONE);
> +}
> +
> +
> +Buteo::SyncResults
> +UContactsClient::getSyncResults() const
> +{
> + return d_ptr->mResults;
> +}
> +
> +QString
> +UContactsClient::authToken() const
> +{
> + return d_ptr->mAuth->token();
> +}
> +
> +QString
> +UContactsClient::syncTargetId() const
> +{
> + return d_ptr->mContactBackend->syncTargetId();
> +}
> +
> +QString UContactsClient::accountName() const
> +{
> + if (d_ptr->mAuth) {
> + return d_ptr->mAuth->accountDisplayName();
> + }
> + return QString();
> +}
> +
> +const QDateTime
> +UContactsClient::lastSyncTime() const
> +{
> + FUNCTION_CALL_TRACE;
> +
> + Buteo::ProfileManager pm;
> + Buteo::SyncProfile* sp = pm.syncProfile (iProfile.name ());
> + QDateTime lastTime = sp->lastSuccessfulSyncTime();
> + if (!lastTime.isNull()) {
> + // return UTC time used by google
> + return lastTime.addSecs(6).toUTC();
> + } else {
> + return lastTime;
> + }
> +}
> +
> +
> +void
> +UContactsClient::filterRemoteAddedModifiedDeletedContacts(const QList<QContact> remoteContacts,
> + QList<QContact> &remoteAddedContacts,
> + QList<QContact> &remoteModifiedContacts,
> + QList<QContact> &remoteDeletedContacts)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + foreach (const QContact &contact, remoteContacts) {
> + if (UContactsBackend::deleted(contact)) {
> + remoteDeletedContacts.append(contact);
> + continue;
> + }
> +
> + QString remoteId = UContactsBackend::getRemoteId(contact);
> + QContactId localId = d->mContactBackend->entryExists(remoteId);
> + if (localId.isNull()) {
> + remoteAddedContacts.append(contact);
> + } else {
> + remoteModifiedContacts.append(contact);
> + }
> + }
> +}
> +
> +void
> +UContactsClient::resolveConflicts(QList<QContact> &modifiedRemoteContacts,
> + QList<QContact> &deletedRemoteContacts)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + // TODO: Handle conflicts. The steps:
> + // o Compare the list of local modified/deleted contacts with
> + // the list of remote modified/deleted contacts
> + // o Create a new list (a map maybe) that has the contacts to
> + // be modified/deleted using the conflict resolution policy
> + // (server-wins, client-wins, add-new)
> + // o Return the list
Is this still really a TODO?
> +
> + //QListIterator<GContactEntry*> iter (modifiedRemoteContacts);
> + QList<QContact>::iterator iter;
> + for (iter = modifiedRemoteContacts.begin (); iter != modifiedRemoteContacts.end (); ++iter) {
> + QContact contact = *iter;
> + QString remoteId = UContactsBackend::getRemoteId(contact);
> +
> + if (d->mModifiedContactIds.contains(remoteId)) {
> + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> + modifiedRemoteContacts.erase(iter);
> + } else {
> + d->mModifiedContactIds.remove(remoteId);
> + }
> + }
> +
> + if (d->mDeletedContactIds.contains(remoteId)) {
> + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> + modifiedRemoteContacts.erase(iter);
> + } else {
> + d->mDeletedContactIds.remove(remoteId);
> + }
> + }
> + }
> +
> + for (iter = deletedRemoteContacts.begin (); iter != deletedRemoteContacts.end (); ++iter) {
> + QContact contact = *iter;
> + QString remoteId = UContactsBackend::getRemoteId(contact);
> +
> + if (d->mModifiedContactIds.contains(remoteId)) {
> + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) {
> + deletedRemoteContacts.erase(iter);
> + } else {
> + d->mModifiedContactIds.remove(remoteId);
> + }
> + }
> +
> + if (d->mDeletedContactIds.contains(remoteId)) {
> + // If the entry is deleted both at the server and
> + // locally, then just remove it from the lists
> + // so that no further action need to be taken
> + deletedRemoteContacts.erase(iter);
> + d->mDeletedContactIds.remove(remoteId);
> + }
> + }
> +}
> +
> +void
> +UContactsClient::updateIdsToLocal(const QList<QContact> &contacts)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> + QList<QContact> newList(contacts);
> + d->mContactBackend->modifyContacts(&newList);
> +}
> +
> +void
> +UContactsClient::addProcessedItem(Sync::TransferType modificationType,
> + Sync::TransferDatabase database,
> + const QString &modifiedDatabase,
> + int count)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + Buteo::DatabaseResults& results = d->mItemResults[modifiedDatabase];
> + if (database == Sync::LOCAL_DATABASE) {
> + if (modificationType == Sync::ITEM_ADDED) {
> + results.iLocalItemsAdded += count;
> + } else if (modificationType == Sync::ITEM_MODIFIED) {
> + results.iLocalItemsModified += count;
> + } else if (modificationType == Sync::ITEM_DELETED) {
> + results.iLocalItemsDeleted += count;
> + }
> + } else if (database == Sync::REMOTE_DATABASE) {
> + if( modificationType == Sync::ITEM_ADDED) {
> + results.iRemoteItemsAdded += count;
> + } else if (modificationType == Sync::ITEM_MODIFIED) {
> + results.iRemoteItemsModified += count;
> + } else if (modificationType == Sync::ITEM_DELETED) {
> + results.iRemoteItemsDeleted += count;
> + }
> + }
> +}
> +
> +void
> +UContactsClient::generateResults(bool aSuccessful)
> +{
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + d->mResults.setMajorCode(aSuccessful ? Buteo::SyncResults::SYNC_RESULT_SUCCESS :
> + Buteo::SyncResults::SYNC_RESULT_FAILED );
> + d->mResults.setTargetId(iProfile.name());
> + if (d->mItemResults.isEmpty()) {
> + LOG_DEBUG("No items transferred");
> + } else {
> + QMapIterator<QString, Buteo::DatabaseResults> i(d->mItemResults);
> + while (i.hasNext())
> + {
> + i.next();
> + const Buteo::DatabaseResults &r = i.value();
> + Buteo::TargetResults targetResults(i.key(), // Target name
> + Buteo::ItemCounts(r.iLocalItemsAdded,
> + r.iLocalItemsDeleted,
> + r.iLocalItemsModified),
> + Buteo::ItemCounts(r.iRemoteItemsAdded,
> + r.iRemoteItemsDeleted,
> + r.iRemoteItemsModified));
> + d->mResults.addTargetResults(targetResults);
> + LOG_DEBUG("Items for" << targetResults.targetName() << ":");
> + LOG_DEBUG("LA:" << targetResults.localItems().added <<
> + "LD:" << targetResults.localItems().deleted <<
> + "LM:" << targetResults.localItems().modified <<
> + "RA:" << targetResults.remoteItems().added <<
> + "RD:" << targetResults.remoteItems().deleted <<
> + "RM:" << targetResults.remoteItems().modified);
> + }
> + }
> +}
>
> === added file 'buteo-contact-client/UContactsClient.h'
> --- buteo-contact-client/UContactsClient.h 1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsClient.h 2015-07-20 14:20:21 +0000
> @@ -0,0 +1,164 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + * 2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + * Mani Chandrasekar <maninc at gmail.com>
> + * Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#ifndef UCONTACSTCLIENT_H
UCONTACSTCLIENT_H -> UCONTACTSCLIENT_H
> +#define UCONTACSTCLIENT_H
UCONTACSTCLIENT_H -> UCONTACTSCLIENT_H
> +
> +#include <QNetworkReply>
> +#include <QContact>
> +#include <QList>
> +#include <QPair>
> +
> +#include <ClientPlugin.h>
> +
> +class UContactsClientPrivate;
> +class UAuth;
> +class UAbstractRemoteSource;
> +class UContactsBackend;
> +
> +class UContactsClient : public Buteo::ClientPlugin
> +{
> + Q_OBJECT
> + Q_DECLARE_PRIVATE(UContactsClient)
> +
> +public:
> +
> + /*! \brief Constructor
> + *
> + * @param aPluginName Name of this client plugin
> + * @param aProfile Sync profile
> + * @param aCbInterface Pointer to the callback interface
> + * @param authenticator a instance of UAuth class to be used during the authentication
> + */
> + UContactsClient(const QString& aPluginName,
> + const Buteo::SyncProfile &aProfile,
> + Buteo::PluginCbInterface *aCbInterface,
> + const QString& serviceName);
> +
> + /*! \brief Destructor
> + *
> + * Call uninit before destroying the object.
> + */
> + virtual ~UContactsClient();
> +
> + //! @see SyncPluginBase::init
> + virtual bool init();
> +
> + //! @see SyncPluginBase::uninit
> + virtual bool uninit();
> +
> + //! @see ClientPlugin::startSync
> + virtual bool startSync();
> +
> + //! @see SyncPluginBase::abortSync
> + virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED);
> +
> + //! @see SyncPluginBase::getSyncResults
> + virtual Buteo::SyncResults getSyncResults() const;
> +
> + //! @see SyncPluginBase::cleanUp
> + virtual bool cleanUp();
> +
> +public slots:
> + //! @see SyncPluginBase::connectivityStateChanged
> + virtual void connectivityStateChanged( Sync::ConnectivityType aType,
> + bool aState );
> +
> +protected:
> + QString authToken() const;
> + QString syncTargetId() const;
> + QString accountName() const;
> +
> + // Must be implemented by the plugins
> + virtual UAbstractRemoteSource* createRemoteSource(QObject *parent) const = 0;
> + virtual QVariantMap remoteSourceProperties() const = 0;
> +
> + virtual UContactsBackend* createContactsBackend(QObject *parent) const;
> + virtual UAuth* crateAuthenticator(QObject *parent) const;
> +
> + virtual bool isReadyToSync() const;
> + virtual const QDateTime lastSyncTime() const;
> +
> +signals:
> + void stateChanged(Sync::SyncProgressDetail progress);
> + void itemProcessed(Sync::TransferType type,
> + Sync::TransferDatabase db,
> + int committedItems);
> + void syncFinished(Sync::SyncStatus);
> +
> +
> +private:
> + QScopedPointer<UContactsClientPrivate> d_ptr;
> +
> + void loadLocalContacts(const QDateTime &since);
> +
> + bool initConfig();
> + void generateResults(bool aSuccessful);
> + void updateIdsToLocal(const QList<QtContacts::QContact> &contacts);
> + void filterRemoteAddedModifiedDeletedContacts(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> remoteContacts,
> + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteAddedContacts,
> + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteModifiedContacts,
> + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteDeletedContacts);
> + void resolveConflicts(QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &modifiedRemoteContacts,
> + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &deletedRemoteContacts);
> + void addProcessedItem(Sync::TransferType modificationType,
> + Sync::TransferDatabase database,
> + const QString &modifiedDatabase,
> + int count = 1);
> + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> prepareContactsToUpload(UContactsBackend *backend,
> + const QSet<QTCONTACTS_PREPEND_NAMESPACE(QContactId)> &ids);
> +
> + /* slow sync */
> + bool storeToLocalForSlowSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts);
> +
> + /* fast sync */
> + bool storeToLocalForFastSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts);
> +
> +private slots:
> + bool start();
> + void onAuthenticationError();
> + void onStateChanged(Sync::SyncProgressDetail progress);
> + void onSyncFinished(Sync::SyncStatus status);
> + void fireSyncFinishedSucessfully();
> +
> + /* slow sync */
> + void onRemoteContactsFetchedForSlowSync(const QList<QtContacts::QContact> contacts,
> + Sync::SyncStatus status);
> + void onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts,
> + const QList<QtContacts::QContact> &updatedContacts,
> + const QStringList &removedContacts,
> + Sync::SyncStatus status);
> + /* fast sync */
> + void onRemoteContactsFetchedForFastSync(const QList<QtContacts::QContact> contacts,
> + Sync::SyncStatus status);
> + void onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts,
> + const QList<QtContacts::QContact> &updatedContacts,
> + const QStringList &removedContacts,
> + Sync::SyncStatus status);
> +
> +};
> +
> +#endif // UCONTACTCLIENT_H
UCONTACTCLIENT_H -> UCONTACTSCLIENT_H
>
> === added file 'buteo-contact-client/UContactsCustomDetail.h'
> --- buteo-contact-client/UContactsCustomDetail.h 1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsCustomDetail.h 2015-07-20 14:20:21 +0000
> @@ -0,0 +1,52 @@
> +/*
> + * This file is part of buteo-sync-plugins-goole package
> + *
> + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies).
> + * 2015 Canonical Ltd
> + *
> + * Contributors: Sateesh Kavuri <sateesh.kavuri at gmail.com>
> + * Mani Chandrasekar <maninc at gmail.com>
> + * Renato Araujo Oliveira Filho <renato.filho at canonical.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public License
> + * version 2.1 as published by the Free Software Foundation.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + *
> + */
> +
> +#ifndef UCONTACTSCUSTOMDETAIL_H
> +#define UCONTACTSCUSTOMDETAIL_H
> +
> +#include <QString>
> +
> +#include <QContact>
> +#include <QContactExtendedDetail>
> +
> +QTCONTACTS_USE_NAMESPACE
> +
> +class UContactsCustomDetail
> +{
> +public:
> + // Ubuntu fields
> + static const QString FieldContactETag;
> + static const QString FieldRemoteId;
> + static const QString FieldGroupMembershipInfo;
> + static const QString FieldDeletedAt;
> + static const QString FieldCreatedAt;
> + static const QString FieldContactAvatarETag;
> +
> + static QContactExtendedDetail getCustomField(const QContact &contact, const QString &name);
> + static void setCustomField(QContact &contact, const QString &name, const QVariant &value);
> +};
> +
> +#endif // GCONTACTCUSTOMDETAIL_H
GCONTACTCUSTOMDETAIL_H -> UCONTACTSCUSTOMDETAIL_H
>
> === added file 'config.h.in'
> --- config.h.in 1970-01-01 00:00:00 +0000
> +++ config.h.in 2015-07-20 14:20:21 +0000
> @@ -0,0 +1,11 @@
> +#ifndef __CONFIG_H__
> +#define __CONFIG_H__
> +
> +#include <QString>
> +
> +const QString GOOGLE_CONTACTS_CLIENT_ID ("759250720802-4sii0me9963n9fdqdmi7cepn6ub8luoh.apps.googleusercontent.com");
> +const QString GOOGLE_CONTACTS_CLIENT_SECRET ("juFngKUcuhB7IRQqHtSLavqJ");
Is it okay for the secret to be kept in source control?
> +const QString GOOGLE_ETAG_DETAIL ("X-GOOGLE-ETAG");
> +const QString QCONTACTS_BACKEND_NAME ("galera");
> +
> +#endif
--
https://code.launchpad.net/~renatofilho/buteo-sync-plugins-contacts/new-code/+merge/264335
Your team Ubuntu Phablet Team is subscribed to branch lp:buteo-sync-plugins-contacts.
More information about the Ubuntu-reviews
mailing list