qtmobility/plugins/contacts/qtcontacts-tracker/qtrackercontactsaverequest.cpp
changeset 14 6fbed849b4f4
parent 11 06b8e2af4411
child 15 1f895d8a5b2b
equal deleted inserted replaced
11:06b8e2af4411 14:6fbed849b4f4
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qtrackercontactsaverequest.h"
       
    43 #include "trackerchangelistener.h"
       
    44 
       
    45 #include <QtTracker/Tracker>
       
    46 using namespace SopranoLive;
       
    47 
       
    48 #include "qtrackercontactslive.h"
       
    49 
       
    50 // TODO better error handling when saving
       
    51 QTrackerContactSaveRequest::QTrackerContactSaveRequest(QContactAbstractRequest* req, QContactManagerEngine* parent)
       
    52 : QObject(parent), QTrackerContactAsyncRequest(req), errorCount(0)
       
    53 {
       
    54     Q_ASSERT(req);
       
    55     Q_ASSERT(req->type() == QContactAbstractRequest::ContactSaveRequest);
       
    56     Q_ASSERT(parent);
       
    57 
       
    58     QContactSaveRequest* r = qobject_cast<QContactSaveRequest*>(req);
       
    59     if (!r) {
       
    60         QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::FinishedState);
       
    61         return;
       
    62     }
       
    63 
       
    64     QList<QContact> contacts = r->contacts();
       
    65 
       
    66     if(contacts.isEmpty()) {
       
    67         QMap<int, QContactManager::Error> errors; 
       
    68         errors[0] = QContactManager::BadArgumentError;
       
    69         QContactSaveRequest* saveRequest = qobject_cast<QContactSaveRequest*>(req);
       
    70         QContactManagerEngine::updateContactSaveRequest(saveRequest, contacts, QContactManager::BadArgumentError, errors, r->state());
       
    71         return;
       
    72     }
       
    73 
       
    74     QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState);
       
    75 
       
    76     TrackerChangeListener *changeListener = new TrackerChangeListener(parent, this);
       
    77     connect(changeListener, SIGNAL(contactsChanged(const QList<QContactLocalId> &)),SLOT(onTrackerSignal(const QList<QContactLocalId> &)));
       
    78     connect(changeListener, SIGNAL(contactsAdded(const QList<QContactLocalId> &)),SLOT(onTrackerSignal(const QList<QContactLocalId> &)));
       
    79 
       
    80     // Save contacts with batch size
       
    81     /// @todo where to get reasonable batch size
       
    82     int batchSize = 100;
       
    83     for (int i = 0; i < contacts.size(); i+=batchSize) {
       
    84         saveContacts(contacts.mid(i, batchSize));
       
    85     }
       
    86 }
       
    87 
       
    88 void QTrackerContactSaveRequest::onTrackerSignal(const QList<QContactLocalId> &ids)
       
    89 {
       
    90     computeProgress(ids);
       
    91 }
       
    92 
       
    93 void QTrackerContactSaveRequest::computeProgress(const QList<QContactLocalId> &addedIds)
       
    94 {
       
    95     Q_ASSERT(req->type() == QContactAbstractRequest::ContactSaveRequest);
       
    96     QContactSaveRequest* r = qobject_cast<QContactSaveRequest*>(req);
       
    97     if (!r) {
       
    98         QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::FinishedState);
       
    99         return;
       
   100     }
       
   101 
       
   102     foreach (QContactLocalId id, addedIds) {
       
   103         pendingContactIds.remove(id);
       
   104         // since if was OK, remove entry for error
       
   105         errorsOfContactsFinished.remove(id2Index[id]);
       
   106     }
       
   107 
       
   108     if (pendingContactIds.count() == 0) {
       
   109         // compute master error - part of qtcontacts api
       
   110         QContactManager::Error error = QContactManager::NoError;
       
   111         
       
   112         foreach(QContactManager::Error err, errorsOfContactsFinished.values()) {
       
   113             if( QContactManager::NoError != err )
       
   114             {
       
   115                 error = err;
       
   116                 break;
       
   117             }
       
   118         }
       
   119 
       
   120         QContactManagerEngine::updateContactSaveRequest(r, contactsFinished, error, errorsOfContactsFinished, QContactAbstractRequest::FinishedState);
       
   121     }
       
   122 }
       
   123 
       
   124 void QTrackerContactSaveRequest::saveContacts(const QList<QContact> &contacts)
       
   125 {
       
   126     QContactManagerEngine *engine = qobject_cast<QContactManagerEngine *> (parent());
       
   127     Q_ASSERT(engine);
       
   128 
       
   129     QSettings definitions(QSettings::IniFormat, QSettings::UserScope, "Nokia", "Trackerplugin");
       
   130     QTrackerContactsLive cLive;
       
   131     RDFServicePtr service = cLive.service();
       
   132 
       
   133     foreach(QContact contact, contacts) {
       
   134 /*
       
   135         Validation is disabled because it blocks saving contacts parsed from vcards
       
   136         TODO left the commented code while opaque (custom) details are under discussion as remainder
       
   137         QContactManager::Error error;
       
   138         if(!engine->validateContact(contact, error)) {
       
   139             contactsFinished << contact;
       
   140             errorsOfContactsFinished[errorCount++] =  error;
       
   141             computeProgress(QList<QContactLocalId>());
       
   142             continue;
       
   143         }
       
   144 */
       
   145         Live<nco::PersonContact> ncoContact;
       
   146         bool newContact = false;
       
   147 
       
   148         if(contact.localId() == 0) {
       
   149             // Save new contact. compute ID
       
   150             bool ok;
       
   151             // what if both processes read in the same time and write at the same time, no increment
       
   152             unsigned int m_lastUsedId = definitions.value("nextAvailableContactId", "1").toUInt(&ok);
       
   153             definitions.setValue("nextAvailableContactId", QString::number(++m_lastUsedId));
       
   154 
       
   155             ncoContact = service->liveNode(QUrl("contact:"+(QString::number(m_lastUsedId))));
       
   156             QContactId id;
       
   157             id.setLocalId(m_lastUsedId);
       
   158             id.setManagerUri(engine->managerUri());
       
   159             contact.setId(id);
       
   160             ncoContact->setContactUID(QString::number(m_lastUsedId));
       
   161             ncoContact->setContentCreated(QDateTime::currentDateTime());
       
   162             newContact = true;
       
   163         }  else {
       
   164             ncoContact = service->liveNode(QUrl("contact:"+QString::number(contact.localId())));
       
   165             /// @note Following needed in case we save new contact with given localId
       
   166             ncoContact->setContactUID(QString::number(contact.localId()));
       
   167             ncoContact->setContentLastModified(QDateTime::currentDateTime());
       
   168         }
       
   169         pendingContactIds.insert(contact.localId());
       
   170 
       
   171         // if there are work related details, need to be saved to Affiliation.
       
   172         if( QTrackerContactSaveRequest::contactHasWorkRelatedDetails(contact)) {
       
   173             addAffiliation(service, contact.localId());
       
   174         }
       
   175 
       
   176         // Add a special tag for contact added from addressbook, not from fb, telepathy etc.
       
   177         // this is important when returning contacts to sync team
       
   178         RDFVariable rdfContact = RDFVariable::fromType<nco::PersonContact>();
       
   179         rdfContact.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId()));
       
   180         addTag(service, rdfContact, "addressbook");
       
   181 
       
   182         saveContactDetails( service, ncoContact, contact, newContact);
       
   183 
       
   184         // name & nickname - different way from other details
       
   185         cLive.setLiveContact(ncoContact);
       
   186         cLive.setQContact(contact);
       
   187         if( !contact.detail<QContactName>().isEmpty() || !contact.detail<QContactNickname>().isEmpty() ) {
       
   188             cLive.saveName();
       
   189         }
       
   190 
       
   191         contactsFinished << contact;
       
   192         id2Index[contact.localId()] = errorCount;
       
   193         // we fill error here - once response come that everything is OK, remove entry for this contact
       
   194         errorsOfContactsFinished[errorCount++] =  QContactManager::BadArgumentError;
       
   195     }
       
   196     // remember to commit the transaction, otherwise all changes will be rolled back.
       
   197     cLive.commit();
       
   198 }
       
   199 
       
   200 
       
   201 QTrackerContactSaveRequest::~QTrackerContactSaveRequest()
       
   202 {
       
   203     // TODO Auto-generated destructor stub
       
   204 }
       
   205 
       
   206 /*!
       
   207 * Saving has to go in such way that all names are saved at once, all phone numbers together
       
   208 * filled to rdfupdate query etc.
       
   209 * This method goes through the contact and collect which contact detail definitions are there
       
   210 */
       
   211 QStringList QTrackerContactSaveRequest::detailsDefinitionsInContact(const QContact &c)
       
   212 {
       
   213     QStringList definitions;
       
   214     foreach(const QContactDetail& det, c.details())
       
   215         {
       
   216             definitions << det.definitionName();
       
   217         }
       
   218     definitions.removeDuplicates();
       
   219     return definitions;
       
   220 }
       
   221 
       
   222 //! Just moving this code out of saveContact to make it shorter
       
   223 bool QTrackerContactSaveRequest::contactHasWorkRelatedDetails(const QContact &c)
       
   224 {
       
   225     foreach(const QContactDetail& det, c.details())
       
   226     {
       
   227         if( det.contexts().contains(QContactDetail::ContextWork))
       
   228            return true;
       
   229     }
       
   230     return false;
       
   231 }
       
   232 
       
   233 // create nco::Affiliation if there is not one already in tracker
       
   234 void QTrackerContactSaveRequest::addAffiliation(RDFServicePtr service, QContactLocalId contactId)
       
   235 {
       
   236     Live<nco::PersonContact> ncoContact = service->liveNode(QUrl("contact:"+(QString::number(contactId))));
       
   237     Live<nco::Affiliation> ncoAffiliation = service->liveNode(QUrl("affiliation:"+(QString::number(contactId))));
       
   238     ncoContact->setHasAffiliation(ncoAffiliation);
       
   239 }
       
   240 
       
   241 void QTrackerContactSaveRequest::saveContactDetails( RDFServicePtr service,
       
   242                                                 Live<nco::PersonContact>& ncoContact,
       
   243                                                 const QContact& contact,
       
   244                                                 bool newContact)
       
   245 {
       
   246     QStringList detailDefinitionsToSave = detailsDefinitionsInContact(contact);
       
   247 
       
   248     // all the rest might need to save to PersonContact and to Affiliation contact
       
   249     RDFVariable rdfPerson = RDFVariable::fromType<nco::PersonContact>();
       
   250     rdfPerson.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId()));
       
   251 
       
   252     if(not newContact) {
       
   253         // Delete all existing phone numbers - office and home
       
   254         deletePhoneNumbers(service, rdfPerson);
       
   255     }
       
   256 
       
   257     foreach(QString definition, detailDefinitionsToSave)
       
   258     {
       
   259         QList<QContactDetail> details = contact.details(definition);
       
   260         Q_ASSERT(!details.isEmpty());
       
   261 
       
   262         RDFVariable rdfAffiliation;
       
   263         RDFVariable rdfPerson1;
       
   264         rdfPerson1.property<nco::hasAffiliation>() = rdfAffiliation;
       
   265         rdfPerson1.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId()));
       
   266 
       
   267         QList<QContactDetail> workDetails;
       
   268         QList<QContactDetail> homeDetails;
       
   269         foreach(const QContactDetail& det, details) {
       
   270             // details can be for both contexts, so check for both seperately
       
   271             if( det.contexts().contains(QContactDetail::ContextWork) ) {
       
   272                 workDetails << det;
       
   273             }
       
   274             if( det.contexts().contains(QContactDetail::ContextHome)) {
       
   275                 homeDetails << det;
       
   276             }
       
   277             if( !det.contexts().contains(QContactDetail::ContextHome)
       
   278                 && !det.contexts().contains(QContactDetail::ContextWork)) {
       
   279                 homeDetails << det;
       
   280             }
       
   281         }
       
   282 
       
   283         /* Save details */
       
   284         if(definition == QContactPhoneNumber::DefinitionName) {
       
   285             if (!homeDetails.isEmpty()) {
       
   286                 savePhoneNumbers(service, rdfPerson, homeDetails, newContact);
       
   287             }
       
   288             if( !workDetails.isEmpty()) {
       
   289                 savePhoneNumbers(service, rdfAffiliation, workDetails, newContact);
       
   290             }
       
   291         }
       
   292         else if(definition == QContactEmailAddress::DefinitionName) {
       
   293             if (!homeDetails.isEmpty())
       
   294                 saveEmails(service, rdfPerson, homeDetails, newContact);
       
   295             if( !workDetails.isEmpty())
       
   296                 saveEmails(service, rdfAffiliation, workDetails, newContact);
       
   297         }
       
   298         else if(definition == QContactAddress::DefinitionName) {
       
   299             if (!homeDetails.isEmpty())
       
   300                 saveAddresses(service, rdfPerson, homeDetails, newContact);
       
   301             if( !workDetails.isEmpty())
       
   302                 saveAddresses(service, rdfAffiliation, workDetails, newContact);
       
   303         }
       
   304         else if(definition == QContactUrl::DefinitionName) {
       
   305             if (!homeDetails.isEmpty())
       
   306                 saveUrls(service, rdfPerson, homeDetails, newContact);
       
   307             if( !workDetails.isEmpty())
       
   308                 saveUrls(service, rdfAffiliation, workDetails, newContact);
       
   309         }
       
   310         else {
       
   311             // TODO refactor (bug: editing photo doesn't work)
       
   312             foreach(const QContactDetail &det, details )
       
   313             {
       
   314                 definition = det.definitionName();
       
   315                 if(definition == QContactAvatar::DefinitionName) {
       
   316                     QUrl avatar = det.value(QContactAvatar::FieldImageUrl);
       
   317                     Live<nie::DataObject> fdo = service->liveNode( avatar );
       
   318                     ncoContact->setPhoto(fdo);
       
   319                 }
       
   320                 if(definition == QContactBirthday::DefinitionName) {
       
   321                     ncoContact->setBirthDate(QDateTime(det.variantValue(QContactBirthday::FieldBirthday).toDate(), QTime(), Qt::UTC));
       
   322                 }
       
   323             } // end foreach detail
       
   324         }
       
   325     }
       
   326 }
       
   327 
       
   328 // Remove all existing references to phone numbers from the contact so that edits are
       
   329 // reflected to Tracker correctly.
       
   330 // Delete the references to phone numbers - not the numbers themselves as they remain in tracker
       
   331 // with their canonical URI form - might be linked to history.
       
   332 void QTrackerContactSaveRequest::deletePhoneNumbers(RDFServicePtr service, const RDFVariable& rdfContactIn)
       
   333 {
       
   334     {
       
   335         RDFUpdate up;
       
   336         RDFVariable rdfContact = rdfContactIn.deepCopy();
       
   337         up.addDeletion(rdfContact, nco::hasPhoneNumber::iri(), rdfContact.property<nco::hasPhoneNumber>());
       
   338         service->executeQuery(up);
       
   339     }
       
   340 
       
   341     // affiliation
       
   342     {
       
   343         RDFUpdate up;
       
   344         RDFVariable rdfContact = rdfContactIn.deepCopy().property<nco::hasAffiliation>();
       
   345         up.addDeletion(rdfContact, nco::hasPhoneNumber::iri(), rdfContact.property<nco::hasPhoneNumber>());
       
   346         service->executeQuery(up);
       
   347     }
       
   348 }
       
   349 
       
   350 /*!
       
   351  * write all phone numbers on one query to tracker
       
   352  * TODO this is temporary code for creating new, saving contacts need to handle only what was
       
   353  * changed.
       
   354  */
       
   355 void QTrackerContactSaveRequest::savePhoneNumbers(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details, bool newContact )
       
   356 {
       
   357     RDFUpdate up;
       
   358     RDFVariable varForInsert = var.deepCopy();
       
   359     foreach(const QContactDetail& det, details)
       
   360     {
       
   361         QString formattedValue = det.value(QContactPhoneNumber::FieldNumber);
       
   362         // Strip RFC 3966 visual-separators reg exp "[(|-|.|)| ]"
       
   363         QString value = formattedValue.replace( QRegExp("[\\(|" \
       
   364                                                         "\\-|" \
       
   365                                                         "\\.|" \
       
   366                                                         "\\)|" \
       
   367                                                         " ]"), "");
       
   368         // Temporary, because affiliation is still used - to be refactored next week to use Live nodes
       
   369         // using RFC 3966 canonical URI form
       
   370         QUrl newPhone = QString("tel:%1").arg(value);
       
   371         Live<nco::PhoneNumber> ncoPhone = service->liveNode(newPhone);
       
   372         if(not newContact) {
       
   373             ncoPhone->remove();
       
   374         }
       
   375 
       
   376         QStringList subtypes = det.value<QStringList>(QContactPhoneNumber::FieldSubTypes);
       
   377 
       
   378         if( subtypes.contains(QContactPhoneNumber::SubTypeMobile))
       
   379             up.addInsertion(newPhone, rdf::type::iri(), nco::CellPhoneNumber::iri());
       
   380         else if( subtypes.contains(QContactPhoneNumber::SubTypeCar))
       
   381             up.addInsertion(newPhone, rdf::type::iri(), nco::CarPhoneNumber::iri());
       
   382         else if( subtypes.contains(QContactPhoneNumber::SubTypeBulletinBoardSystem))
       
   383             up.addInsertion(newPhone, rdf::type::iri(), nco::BbsNumber::iri());
       
   384         else if( subtypes.contains(QContactPhoneNumber::SubTypeFax))
       
   385             up.addInsertion(newPhone, rdf::type::iri(), nco::FaxNumber::iri());
       
   386         else if( subtypes.contains(QContactPhoneNumber::SubTypeModem))
       
   387             up.addInsertion(newPhone, rdf::type::iri(), nco::ModemNumber::iri());
       
   388         else if( subtypes.contains(QContactPhoneNumber::SubTypePager))
       
   389             up.addInsertion(newPhone, rdf::type::iri(), nco::PagerNumber::iri());
       
   390         else if( subtypes.contains(QContactPhoneNumber::SubTypeMessagingCapable))
       
   391             up.addInsertion(newPhone, rdf::type::iri(), nco::MessagingNumber::iri());
       
   392         else
       
   393             up.addInsertion(newPhone, rdf::type::iri(), nco::VoicePhoneNumber::iri());
       
   394 
       
   395         up.addInsertion(newPhone, nco::phoneNumber::iri(), LiteralValue(value));
       
   396         up.addInsertion(varForInsert, nco::hasPhoneNumber::iri(), newPhone);
       
   397     }
       
   398     service->executeQuery(up);
       
   399 }
       
   400 
       
   401 /*!
       
   402  * write all phone numbers on one query to tracker
       
   403  * TODO this is temporary code for creating new, saving contacts need to handle only what was
       
   404  * changed.
       
   405  */
       
   406 void QTrackerContactSaveRequest::saveEmails(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details, bool newContact )
       
   407 {
       
   408     RDFUpdate up;
       
   409     RDFVariable varForInsert = var.deepCopy();
       
   410     RDFVariable emails = var.property<nco::hasEmailAddress>();
       
   411     if(not newContact) {
       
   412         // delete previous references - keep email IRIs
       
   413         up.addDeletion(RDFVariableStatement(var, nco::hasEmailAddress::iri(), emails));
       
   414     }
       
   415 
       
   416     foreach(const QContactDetail& det, details)
       
   417     {
       
   418         QString value = det.value(QContactEmailAddress::FieldEmailAddress);
       
   419         // Temporary, because affiliation is still used - to be refactored next week to use only Live nodes
       
   420         QUrl newEmail = QString("mailto:%1").arg(value);
       
   421         Live<nco::EmailAddress> ncoEmail = service->liveNode(newEmail);
       
   422         up.addInsertion(newEmail, rdf::type::iri(), nco::EmailAddress::iri());
       
   423         up.addInsertion(newEmail, nco::emailAddress::iri(), LiteralValue(value));
       
   424         up.addInsertion(RDFVariableStatement(varForInsert, nco::hasEmailAddress::iri(), newEmail));
       
   425     }
       
   426     service->executeQuery(up);
       
   427 }
       
   428 
       
   429 /*!
       
   430  * write all Urls
       
   431  * TODO this is temporary code for creating new, saving contacts need to handle only what was
       
   432  * changed.
       
   433  */
       
   434 void QTrackerContactSaveRequest::saveUrls(RDFServicePtr service, RDFVariable &rdfContact, const QList<QContactDetail> &details, bool newContact )
       
   435 {
       
   436     RDFUpdate up;
       
   437     RDFVariable varForInsert = rdfContact.deepCopy();
       
   438     RDFVariable urls = rdfContact.property<nco::url>();
       
   439     RDFVariable urltypes = urls.property<rdf::type>();
       
   440 
       
   441     RDFVariable websiteUrls = rdfContact.property<nco::websiteUrl>();
       
   442     RDFVariable websiteUrlTypes = websiteUrls.property<rdf::type>();
       
   443 
       
   444     if(not newContact) {
       
   445         // first part - deleting previous before adding new again is to be removed
       
   446         up.addDeletion(RDFVariableStatement(rdfContact, nco::url::iri(), urls));
       
   447         up.addDeletion(RDFVariableStatement(rdfContact, nco::websiteUrl::iri(), websiteUrls));
       
   448     }
       
   449 
       
   450     // second part, write all urls
       
   451     foreach(const QContactDetail& det, details)
       
   452     {
       
   453         QUrl newUrl(det.value(QContactUrl::FieldUrl));//::tracker()->createLiveNode().uri();
       
   454         if(det.value(QContactUrl::FieldSubType) == QContactUrl::SubTypeFavourite)
       
   455         {
       
   456             up.addInsertion(varForInsert, nco::url::iri(), newUrl);
       
   457         }
       
   458         else // if not favourite, then homepage. don't support other
       
   459         {
       
   460             up.addInsertion(varForInsert, nco::websiteUrl::iri(), newUrl); // add it to contact
       
   461         }
       
   462     }
       
   463     service->executeQuery(up);
       
   464 }
       
   465 
       
   466 /*!
       
   467  * write all phone numbers on one query to tracker
       
   468  * TODO this is temporary code for creating new, saving contacts need to handle only what was
       
   469  * changed.
       
   470  */
       
   471 void QTrackerContactSaveRequest::saveAddresses(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details, bool newContact )
       
   472 {
       
   473     RDFUpdate up;
       
   474     RDFVariable varForInsert = var.deepCopy();
       
   475     RDFVariable addresses = var.property<nco::hasPostalAddress>();
       
   476     RDFVariable types = addresses.property<rdf::type>();
       
   477     if(not newContact) {
       
   478         up.addDeletion(RDFVariableStatement(var, nco::hasPostalAddress::iri(), addresses));
       
   479         up.addDeletion(addresses, rdf::type::iri(), types);
       
   480     }
       
   481     foreach(const QContactDetail& det, details)
       
   482     {
       
   483         QUrl newPostalAddress = ::tracker()->createLiveNode().uri();
       
   484         // TODO     nco:DomesticDeliveryAddress, nco:InternationalDeliveryAddress, nco:ParcelDeliveryAddress
       
   485         up.addInsertion(newPostalAddress, rdf::type::iri(), nco::PostalAddress::iri());
       
   486         if( det.hasValue(QContactAddress::FieldStreet))
       
   487             up.addInsertion(newPostalAddress, nco::streetAddress::iri(), LiteralValue(det.value(QContactAddress::FieldStreet)));
       
   488         if( det.hasValue(QContactAddress::FieldLocality))
       
   489             up.addInsertion(newPostalAddress, nco::locality::iri(), LiteralValue(det.value(QContactAddress::FieldLocality)));
       
   490         if( det.hasValue(QContactAddress::FieldCountry))
       
   491             up.addInsertion(newPostalAddress, nco::country::iri(), LiteralValue(det.value(QContactAddress::FieldCountry)));
       
   492         if( det.hasValue(QContactAddress::FieldPostcode))
       
   493             up.addInsertion(newPostalAddress, nco::postalcode::iri(), LiteralValue(det.value(QContactAddress::FieldPostcode)));
       
   494         if( det.hasValue(QContactAddress::FieldRegion))
       
   495             up.addInsertion(newPostalAddress, nco::region::iri(), LiteralValue(det.value(QContactAddress::FieldRegion)));
       
   496 
       
   497         up.addInsertion(RDFVariableStatement(varForInsert, nco::hasPostalAddress::iri(), newPostalAddress));
       
   498     }
       
   499     service->executeQuery(up);
       
   500 }
       
   501 
       
   502 /*!
       
   503  * Not very good solution, but we add "addressbook" tag to identify which contacts
       
   504  * are added but addressbook ( in order to separate them from facebook and telepathy
       
   505  * contacts
       
   506  */
       
   507 void QTrackerContactSaveRequest::createTagIfItDoesntExistAlready(SopranoLive::RDFServicePtr service, const QString &tag)
       
   508 {
       
   509     static bool checked = false;
       
   510     // only once, if someone remove tag we are in problems (lost contacts)
       
   511     if( !checked )
       
   512     {
       
   513         checked = true;
       
   514         RDFVariable rdfTag = RDFVariable::fromType<nao::Tag>();
       
   515         RDFVariable labelVar = rdfTag.optional().property<nao::prefLabel>();
       
   516         labelVar = LiteralValue(tag);
       
   517         RDFFilter doesntExist = labelVar.isBound().not_();// do not create if it already exist
       
   518 
       
   519         RDFUpdate up;
       
   520 
       
   521         QUrl newTag = ::tracker()->createLiveNode().uri();
       
   522         rdfTag = newTag;
       
   523         QList<RDFVariableStatement> insertions;
       
   524         insertions << RDFVariableStatement(rdfTag, rdf::type::iri(), nao::Tag::iri())
       
   525         << RDFVariableStatement(newTag, nao::prefLabel::iri(), labelVar);
       
   526         up.addInsertion(insertions); // this way we apply filter doesntExist to both insertions
       
   527         service->executeQuery(up);
       
   528     }
       
   529 }
       
   530 
       
   531 void QTrackerContactSaveRequest::addTag(RDFServicePtr service, RDFVariable &var, const QString &tag)
       
   532 {
       
   533     // TODO do all in one RDF query: create tag if not existing
       
   534     createTagIfItDoesntExistAlready(service, tag);
       
   535     RDFUpdate up;
       
   536     RDFVariable rdftag;
       
   537     rdftag.property<nao::prefLabel>() = LiteralValue(tag);
       
   538     up.addInsertion(var, nao::hasTag::iri(), rdftag);
       
   539     service->executeQuery(up);
       
   540 }