[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 13:44:56 UTC 2015
More in-line comments (only as far as line 2000 at the moment), mostly small typos but also a couple of questions in there too.
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-19 21:39:03 +0000
> @@ -0,0 +1,560 @@
> +/*
> + * This file is part of buteo-gcontact-plugins 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 is 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.
> + // TODO QContactManager populates indices from the qContactList, but we populate keys, is this OK?
> + for (int i = 0; i < aContactList.size(); i++)
> + {
> + UContactsStatus status;
> + status.id = i;
> + if (!errorMap.contains(i)) {
> + status.errorCode = QContactManager::NoError;
> + } else {
> + qDebug() << "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);
> +
> + // int this case we should use the guid stored on contact
> + 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;
> +
> + qDebug() << "WILL REMOVE CONTACTS:" << aContactIDList;
> + 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.
> + // TODO QContactManager populates indices from the qContactList, but we populate keys, is this OK?
(Same as before, verify this is okay)
> + 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 enterred contacts, i.e. all other contacts that are not sourcing
enterred -> entered
> + // 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/UContactsBackend.h'
> --- buteo-contact-client/UContactsBackend.h 1970-01-01 00:00:00 +0000
> +++ buteo-contact-client/UContactsBackend.h 2015-07-19 21:39:03 +0000
> @@ -0,0 +1,250 @@
> +/*
> + * This file is part of buteo-gcontact-plugins 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 UCONTACTSBACKEND_H_
> +#define UCONTACTSBACKEND_H_
> +
> +#include <QContact>
> +#include <QContactId>
> +#include <QContactFetchRequest>
> +#include <QContactExtendedDetail>
> +#include <QContactChangeLogFilter>
> +#include <QContactManager>
> +
> +#include <QStringList>
> +
> +QTCONTACTS_USE_NAMESPACE
> +
> +struct UContactsStatus
> +{
> + int id;
> + QContactManager::Error errorCode;
> +};
> +
> +typedef QMultiMap<QString, QContactId> RemoteToLocalIdMap;
> +
> +//! \brief Harmattan Contact storage plugin backend interface class
> +///
> +/// This class interfaces with the backend implementation of contact manager on harmattan
> +/// device
This comment should probably be updated to give details about the real storage backend being used, since we're not on MeeGo/Harmattan.
> +class UContactsBackend : public QObject
> +{
> +
> +public:
> + explicit UContactsBackend(const QString &managerName = "", QObject* parent = 0);
> +
> + /*!
> + * \brief Destructor
> + */
> + ~UContactsBackend();
> +
> + /*!
> + * \brief Initialize the backend, must be called before any other function
> + * \param syncTarget The name of the collection used to store contacts
> + * \return Returns true if initialized with sucess false otherwise
> + */
> + bool init(uint syncAccount, const QString &syncTarget);
> +
> + /*!
> + * \brief releases the resources held.
> + * @returnReturns true if sucess false otherwise
> + */
> + bool uninit();
> +
> + /*!
> + * \brief Return ids of all contacts stored locally
> + * @return List of contact IDs
> + */
> + QList<QContactId> getAllContactIds();
> +
> +
> + /*!
> + * \brief Return all new contacts ids in a QList of QStrings
> + * @param aTimeStamp Timestamp of the oldest contact ID to be returned
> + * @return List of contact IDs
> + */
> + RemoteToLocalIdMap getAllNewContactIds(const QDateTime& aTimeStamp);
> +
> + /*!
> + * \brief Return all modified contact ids in a QList of QStrings
> + * @param aTimeStamp Timestamp of the oldest contact ID to be returned
> + * @return List of contact IDs
> + */
> + RemoteToLocalIdMap getAllModifiedContactIds(const QDateTime& aTimeStamp);
> +
> +
> + /*!
> + * \brief Return all deleted contacts ids in a QList of QStrings
> + * @param aTimeStamp Timestamp of the oldest contact ID to be returned
> + * @return List of contact IDs
> + */
> + RemoteToLocalIdMap getAllDeletedContactIds(const QDateTime& aTimeStamp);
> +
> + /*!
> + * \brief Get contact data for a given gontact ID as a QContact object
gontact -> contact
> + * @param aContactId The ID of the contact
> + * @return The data of the contact
> + */
> + QContact getContact(const QContactId& aContactId);
> +
> + /*!
> + * \brief Returns a contact for the specified remoteId
> + * @param remoteId The remote id of the contact to be returned
> + * @return The data of the contact
> + */
> + QContact getContact(const QString& remoteId);
> +
> + /*!
> + * \brief Batch addition of contacts
> + * @param aContactDataList Contact data
> + * @param aStatusMap Returned status data
> + * @return Errors
> + */
> + bool addContacts(QList<QContact>& aContactList,
> + QMap<int, UContactsStatus> *aStatusMap );
> +
> + // Functions for modifying contacts
> + /*!
> + * \brief Batch modification
> + * @param aContactDataList Contact data
> + * @param aContactsIdList Contact IDs
> + * @return Errors
> + */
> + QMap<int, UContactsStatus> modifyContacts(QList<QtContacts::QContact> *aContactList);
> +
> + /*!
> + * \brief Batch deletion of contacts
> + * @param aContactIDList Contact IDs
> + * @return Errors
> + */
> + QMap<int, UContactsStatus> deleteContacts(const QStringList &aContactIDList);
> +
> + /*!
> + * \brief Batch deletion of contacts
> + * @param aContactIDList Contact IDs
> + * @return Errors
> + */
> + QMap<int, UContactsStatus> deleteContacts(const QList<QContactId> &aContactIDList);
> +
> + /*!
> + * \brief Check if a contact exists
> + * \param remoteId The remoteId of the contact
> + * \return The localId of the contact
> + */
> + QContactId entryExists(const QString remoteId);
> +
> + /*!
> + * \brief Retrieve the current address book used by the backend
> + * \return The address book id
> + */
> + QString syncTargetId() const;
> +
> + /*!
> + * \brief Retrieve the local id of a list of remote ids
> + * \param remoteIds A list with remote ids
> + * \return A list with local ids
> + */
> + const QStringList localIds(const QStringList remoteIds);
> +
> + /*!
> + * \brief Return the value of the remote id field in a contact
> + * \param contact The contact object
> + * \return A string with the remoteId
> + */
> + static QString getRemoteId(const QContact &contact);
> +
> + /*!
> + * \brief Update the value of the remote id field in a contact
> + * \param contact The contact object
> + * \param remoteId A string with the remoteId
> + */
> + static void setRemoteId(QContact &contact, const QString &remoteId);
> +
> + /*!
> + * \brief Return the valueof the local id field in a contact
> + * \param contact The contact object
> + * \return A string with the localId
> + */
> + static QString getLocalId(const QContact &contact);
> +
> + /*!
> + * \brief Update the value of the local id field in a contact
> + * \param contact The contact object
> + * \param remoteId A string with the localId
> + */
> + static void setLocalId(QContact &contact, const QString &localId);
> +
> + /*!
> + * \brief Check if the contact is marked as deleted
> + * \param contact The contact object
> + * \return Returns true if the contact is marked as deleted, otherwise returns false
> + */
> + static bool deleted(const QContact &contact);
> +
> + /*!
> + * \brief Purge all deleted contacts from the server
> + */
> + void purgecontacts();
> +
> + QContactManager *manager() const;
> +
> +private: // functions
> +
> + /*!
> + * \brief Returns contact IDs specified by event type and timestamp
> + * @param aEventType Added/changed/removed contacts
> + * @param aTimeStamp Contacts older than aTimeStamp are filtered out
> + * @param aIdList Returned contact IDs
> + */
> + void getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType,
> + const QDateTime &aTimeStamp,
> + RemoteToLocalIdMap *aIdList);
> +
> + /*!
> + * \brief Constructs and returns the filter for accessing only contacts allowed to be synchronized
> + * Contacts not allowed to be synchronized are Instant messaging contacts and contacts with origin from other sync backends;
> + * those contacts have QContactSyncTarget::SyncTarget value different from address book or buteo sync clients.
> + * It is designed that buteo sync clients don't restrict access to contacts among themselves
> + * - value for QContactSyncTarget::SyncTarget written by this backend is "buteo".
> + */
> + QContactFilter getSyncTargetFilter() const;
> +
> + QContactFilter getRemoteIdFilter(const QString &remoteId) const;
> +
> +private: // data
> +
> + // if there is more than one Manager we need to have a list of Managers
> + QContactManager *iMgr; ///< A pointer to contact manager
> + QString mSyncTargetId;
> +
> +
> + void createSourceForAccount(uint accountId, const QString &label);
> +};
> +
> +#endif /* CONTACTSBACKEND_H_ */
> +
> +
> +
>
> === 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-19 21:39:03 +0000
> @@ -0,0 +1,949 @@
> +/*
> + * This file is part of buteo-gcontact-plugins 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;
> + }
> + /*
> + 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
> + */
> +
Where does this list of actions actually get implemented? I see that GContactsClient inherits from UContactsClient but it doesn't appear to reimplement this function.
> + 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("\"Loal URI\" is missing on configuration file");
Is "Loal" a typo for something else here? If not, could you add a clarification of what a "Loal URI" is?
> + 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);
> +
> + // Remote source will be create after authentication since it needs some information
create -> created
> + // 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();
> +
> + qDebug() << "load all contacts since" << sinceDate << sinceDate.isValid();
> + // load changed contact since the last sync date or all contacts if not
not -> 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:
> + // Not required
> + break;
> + case Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE:
> + // Not required
> + break;
> + case Buteo::SyncProfile::SYNC_DIRECTION_UNDEFINED:
> + // Not required
Is it correct for this not to break like the others? (i.e. should this throw a configuration error by continuing into the default case?)
> + 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)
> +{
> + Q_UNUSED(updatedContacts)
updatedContacts is used (it gets added to changedContacts)
> + Q_UNUSED(removedContacts)
> + FUNCTION_CALL_TRACE;
> + Q_D(UContactsClient);
> +
> + qDebug() << "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 fir 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);
> + qDebug() << "REMOTE CONTACTS" << contacts.size();
> +
> + 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);
> +
> + qDebug() << "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
> + // 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
> + 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
> + 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
> + 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
> +
> + //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);
> + }
> + }
> +}
--
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