Fixed testRemoveContacts unit test after porting to Qt Mobility changes.
[qtcontacts-tracker:qtcontacts-tracker.git] / qcontacttrackerbackend.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 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 "qcontacttrackerbackend_p.h"
43
44 #include <QtTracker/Tracker>
45 #include <QtTracker/ontologies/nco.h>
46 #include <QtTracker/ontologies/nie.h>
47 #include <QtTracker/ontologies/nao.h>
48 #include <QRegExp>
49 #include <QDir>
50 #include <QFile>
51 #include <QSet>
52 #include <QList>
53
54 #include "qtcontacts.h"
55
56 #include "trackerchangelistener.h"
57 #include "qtrackercontactsaverequest.h"
58 #include <qtrackerrelationshipfetchrequest.h>
59 #include <qtrackerrelationshipsaverequest.h>
60 #include <qtrackercontactidfetchrequest.h>
61
62
63 QContactManagerEngine* ContactTrackerFactory::engine(const QMap<QString, QString>& parameters, QContactManager::Error& error)
64 {
65     Q_UNUSED(error);
66     return new QContactTrackerEngine(managerName(), 1, parameters);
67 }
68
69 QString ContactTrackerFactory::managerName() const
70 {
71     return QString("tracker");
72 }
73 Q_EXPORT_PLUGIN2(qtcontacts_tracker, ContactTrackerFactory);
74
75 QContactTrackerEngine::QContactTrackerEngine(const QString& engineName, int engineVersion, const QMap<QString, QString>& parameters)
76     : d(new QContactTrackerEngineData),
77     contactArchiveFile("removed"),
78     contactArchiveDir(QDir::homePath()+"/.contacts")
79 {
80     Q_UNUSED(parameters);
81     d->m_engineName = engineName;
82     d->m_engineVersion = engineVersion;
83     connectToSignals();
84 }
85
86 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString>& parameters)
87     : d(new QContactTrackerEngineData),
88     contactArchiveFile("removed"),
89     contactArchiveDir(QDir::homePath()+"/.contacts")
90 {
91     Q_UNUSED(parameters);
92     connectToSignals();
93 }
94
95 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
96     : QContactManagerEngine(), d(other.d)
97 {
98     Q_UNUSED(other);
99     connectToSignals();
100 }
101
102 void QContactTrackerEngine::connectToSignals()
103 {
104     TrackerChangeListener *listener = new TrackerChangeListener(this);
105     connect(listener, SIGNAL(contactsAdded(const QList<QContactLocalId>&)), SIGNAL(contactsAdded(const QList<QContactLocalId>&)));
106     connect(listener, SIGNAL(contactsChanged(const QList<QContactLocalId>&)), SIGNAL(contactsChanged(const QList<QContactLocalId>&)));
107     connect(listener, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)), SIGNAL(contactsRemoved(const QList<QContactLocalId>&)));
108 }
109
110 QContactTrackerEngine& QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
111 {
112     d = other.d;
113     return *this;
114 }
115
116 QContactTrackerEngine::~QContactTrackerEngine()
117 {
118 }
119
120 QContactManagerEngine* QContactTrackerEngine::clone()
121 {
122     // this engine allows sharing - so we increase the reference count.
123     d->m_refCount.ref();
124     return this;
125 }
126
127 void QContactTrackerEngine::deref()
128 {
129     if (!d->m_refCount.deref())
130         delete this;
131 }
132
133 QList<QContactLocalId> QContactTrackerEngine::contacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error& error) const
134 {
135
136     // TODO Implement sorting
137     QList<QContactLocalId> ids;
138     RDFVariable rdfContact = RDFVariable::fromType<nco::PersonContact>();
139     if (filter.type() == QContactFilter::ChangeLogFilter) {
140         const QContactChangeLogFilter& clFilter = static_cast<const QContactChangeLogFilter&>(filter);
141         // Removed since
142         if (clFilter.eventType() == QContactChangeLogFilter::EventRemoved) {
143             error = QContactManager::NotSupportedError;
144             return ids;
145         }
146         // Added since
147         if (clFilter.eventType() == QContactChangeLogFilter::EventAdded) {
148             rdfContact.property<nao::hasTag>().property<nao::prefLabel>() = LiteralValue("addressbook");
149             rdfContact.property<nie::contentCreated>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
150         }
151         // Changed since
152         else if (clFilter.eventType() == QContactChangeLogFilter::EventChanged) {
153             rdfContact.property<nao::hasTag>().property<nao::prefLabel>() = LiteralValue("addressbook");
154             rdfContact.property<nie::contentLastModified>() >= LiteralValue(clFilter.since().toString(Qt::ISODate));
155         }
156     }
157     RDFSelect query;
158     query.addColumn("contact_uri", rdfContact);
159     query.addColumn("contactId", rdfContact.property<nco::contactUID>());
160     foreach (QContactSortOrder sort, sortOrders) {
161         query.orderBy(contactDetail2Rdf(rdfContact, sort.detailDefinitionName(), sort.detailFieldName()),
162                       sort.direction() == Qt::AscendingOrder);
163     }
164     LiveNodes ncoContacts = ::tracker()->modelQuery(query);
165     for (int i = 0; i < ncoContacts->rowCount(); i++) {
166         ids.append(ncoContacts->index(i, 1).data().toUInt());
167     }
168
169     error = QContactManager::NoError;
170     return ids;
171 }
172
173 QList<QContactLocalId> QContactTrackerEngine::contacts(const QList<QContactSortOrder>& sortOrders, QContactManager::Error& error) const
174 {
175     Q_UNUSED(sortOrders)
176
177     QList<QContactLocalId> ids;
178     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
179     RDFSelect query;
180
181     query.addColumn("contact_uri", RDFContact);
182     query.addColumn("contactId", RDFContact.property<nco::contactUID>());
183     LiveNodes ncoContacts = ::tracker()->modelQuery(query);
184     for(int i=0; i<ncoContacts->rowCount(); i++) {
185         ids.append(ncoContacts->index(i, 1).data().toUInt());
186     }
187
188     error = QContactManager::NoError;
189     return ids;
190 }
191
192 QContact QContactTrackerEngine::contact(const QContactLocalId& contactId, QContactManager::Error& error ) const
193 {
194     qWarning() << "QContactManager::contact()" << "api is not supported for tracker plugin. Please use asynchronous API QContactFetchRequest.";
195     return contact_impl(contactId, error);
196 }
197 // used in tests, removed warning while decided if to provide sync api. Until then customers are advised to use async
198 QContact QContactTrackerEngine::contact_impl(const QContactLocalId& contactId, QContactManager::Error& error ) const
199 {
200     // the rest of the code is for internal usage, unit tests etc.
201     QContactLocalIdFilter idlist;
202     QList<QContactLocalId> ids; ids << contactId;
203     idlist.setIds(ids);
204     QContactFetchRequest request;
205     QStringList fields;
206
207     fields << QContactAvatar::DefinitionName
208             << QContactBirthday::DefinitionName
209             << QContactAddress::DefinitionName
210             << QContactEmailAddress::DefinitionName
211             << QContactDisplayLabel::DefinitionName
212             << QContactGender::DefinitionName
213             << QContactAnniversary::DefinitionName
214             << QContactName::DefinitionName
215             << QContactOnlineAccount::DefinitionName
216             << QContactOrganization::DefinitionName
217             << QContactPhoneNumber::DefinitionName
218             << QContactOnlineAccount::DefinitionName
219             << QContactUrl::DefinitionName;
220     request.setDefinitionRestrictions(fields);
221     request.setFilter(idlist);
222
223     QContactTrackerEngine engine(*this);
224     engine.startRequest(&request);
225     // 10 seconds should be enough
226     engine.waitForRequestFinished(&request, 10000);
227
228     if( !request.isFinished() || request.contacts().size() == 0) {
229         error = QContactManager::UnspecifiedError;
230     }
231     else {
232         // leave the code for now while not all other code is fixed
233         error = QContactManager::NoError;
234         return request.contacts()[0];
235     }
236
237     return QContact();
238 }
239
240 bool QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
241 {
242     Q_ASSERT(req);
243     if(!req->isActive())
244     {
245         return req->isFinished(); // might be already finished
246     }
247     QTime t;
248     t.start();
249     while(t.elapsed() < msecs || msecs == 0) // 0 for infinite
250     {
251         usleep(10000);
252         QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
253         if(req->isFinished())
254             return true;
255     }
256     qDebug() << Q_FUNC_INFO <<"Status Finished" << req->isFinished();
257     return req->isFinished();
258
259 }
260
261 bool QContactTrackerEngine::saveContact( QContact* contact, QContactManager::Error& error)
262 {
263     // Signal emitted from TrackerChangeListener
264     QContactSaveRequest request;
265     QList<QContact> contacts(QList<QContact>()<<*contact);
266     request.setContacts(contacts);
267     QContactTrackerEngine engine(*this);
268     engine.startRequest(&request);
269     // 10 seconds should be enough
270     engine.waitForRequestFinished(&request, 10000);
271     error = request.error();
272     Q_ASSERT(request.contacts().size() == 1);
273     *contact = request.contacts()[0];
274
275     if( request.isFinished() && error == QContactManager::NoError)
276         return true;
277     else
278         return false;
279 }
280
281 bool QContactTrackerEngine::removeContact(const QContactLocalId& contactId, QContactManager::Error& error)
282 {
283     error = QContactManager::NoError;
284
285     // TODO: Do with LiveNodes when they support strict querying.
286     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
287     RDFContact.property<nco::contactUID>() = LiteralValue(QString::number(contactId));
288     RDFSelect query;
289
290     query.addColumn("contact_uri", RDFContact);
291     LiveNodes ncoContacts = ::tracker()->modelQuery(query);
292     if(ncoContacts->rowCount() == 0) {
293         error = QContactManager::DoesNotExistError;
294         return false;
295     }
296
297
298     Live< nco::PersonContact> ncoContact = ncoContacts->liveNode(0);
299     LiveNodes contactMediums = ncoContact->getHasContactMediums();
300     foreach(Live<nco::ContactMedium> media, contactMediums) {
301         media->remove();
302     }
303     ncoContact->remove();
304
305     //Temporary workaround to get removed- signal
306     QList<QContactLocalId> removed;
307     removed << contactId;
308     emit contactsRemoved(removed);
309
310     return true;
311 }
312
313 bool QContactTrackerEngine::saveContacts(QList<QContact>* contacts, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error& error)
314 {
315     // @todo: Handle errors per saved contact.
316     Q_UNUSED(errorMap)
317
318     error = QContactManager::NoError;
319
320     if(contacts == 0) {
321         error = QContactManager::BadArgumentError;
322         return false;
323     }
324
325     // Signal emitted from TrackerChangeListener
326     QContactSaveRequest request;
327     QList<QContact> contactList;
328     for (int i = 0; i < contacts->size(); ++i) {
329         contactList.append(contacts->at(i));
330     }
331     request.setContacts(contactList);
332     QContactTrackerEngine engine(*this);
333     engine.startRequest(&request);
334     /// @todo what should be the delay
335     engine.waitForRequestFinished(&request, 1000*contacts->size());
336     /// @todo what should we do in case request.isFinished() == false
337     if (request.isFinished() == false) {
338         qWarning() << "QContactTrackerEngine::saveContacts:" << "request not finished";
339     }
340     error = request.error();
341     for (int i = 0; i < contacts->size(); ++i) {
342         (*contacts)[i] = request.contacts().at(i);
343     }
344
345     // Returns false if we have any errors - true if everything went ok.
346     return (request.errorMap().isEmpty() && error == QContactManager::NoError);
347 }
348
349 bool QContactTrackerEngine::removeContacts(QList<QContactLocalId>* contactIds, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error& error)
350 {
351     // Cannot report errors - giving up.
352     if(!errorMap) {
353         error = QContactManager::BadArgumentError;
354         return false;
355     }
356
357     // let's clear the error hash so there is nothing old haunting us.
358     errorMap->clear();
359
360     if (!contactIds) {
361         error = QContactManager::BadArgumentError;
362         return false;
363     }
364
365     for (int i = 0; i < contactIds->count(); i++) {
366         QContactManager::Error lastError;
367         removeContact(contactIds->at(i), lastError);
368         if (lastError == QContactManager::NoError) {
369             (*contactIds)[i] = 0;
370         }
371         else {
372             errorMap->insert(i, lastError);
373         }
374     }
375
376     // Returns true if no errors were encountered - false if there was errors.
377     // emit signals removed as they are fired from QContactManager
378     return (errorMap->isEmpty());
379 }
380
381 QMap<QString, QContactDetailDefinition> QContactTrackerEngine::detailDefinitions(const QString& contactType,
382                                                                                  QContactManager::Error& error) const
383 {
384     if (contactType != QContactType::TypeContact) {
385         error = QContactManager::InvalidContactTypeError;
386         return QMap<QString, QContactDetailDefinition>();
387     }
388
389     // lazy initialisation of schema definitions.
390     if (d->m_definitions.isEmpty()) {
391         // none in the list?  get the schema definitions, and modify them to match our capabilities.
392         d->m_definitions = QContactManagerEngine::schemaDefinitions().value(QContactType::TypeContact);
393         {
394             qDebug() << "the definitions";
395             QList<QString> defs = d->m_definitions.keys();
396             foreach(QString def,  defs)
397             {
398                 qDebug() << def;
399             }
400         }
401         // modification: name is unique
402         QContactDetailDefinition nameDef = d->m_definitions.value(QContactName::DefinitionName);
403         nameDef.setUnique(true);
404         d->m_definitions.insert(QContactName::DefinitionName, nameDef);
405
406         // modification: avatar is unique.
407         QContactDetailDefinition avatarDef = d->m_definitions.value(QContactAvatar::DefinitionName);
408         avatarDef.setUnique(true);
409         d->m_definitions.insert(QContactAvatar::DefinitionName, avatarDef);
410
411         // modification: url is unique.
412         {
413             const QContactDetailDefinition urlDef = d->m_definitions.value(
414                     QContactUrl::DefinitionName);
415             QContactDetailDefinition newUrlDef;
416
417             QMap<QString, QContactDetailDefinitionField> urlFieldNames = urlDef.fields();
418             QMap<QString, QContactDetailDefinitionField> &fields(urlFieldNames);
419             QContactDetailFieldDefinition f;
420
421             f.setDataType(QVariant::String);
422             QVariantList subTypes;
423             // removing social networking url
424             subTypes << QString(QLatin1String(QContactUrl::SubTypeFavourite));
425             subTypes << QString(QLatin1String(QContactUrl::SubTypeHomePage));
426             f.setAllowableValues(subTypes);
427             fields.insert(QContactUrl::FieldSubType, f);
428             newUrlDef.setFields(fields);
429             newUrlDef.setUnique(true);
430             d->m_definitions.insert(QContactUrl::DefinitionName, newUrlDef);
431         }
432
433         // QContactOnlineAccount custom fields
434         {
435             const QContactDetailDefinition accDef = d->m_definitions.value(QContactOnlineAccount::DefinitionName);
436             QContactDetailDefinition newAccountDefinition;
437
438             QMap<QString, QContactDetailDefinitionField> accountFieldName = accDef.fields();
439             QMap<QString, QContactDetailDefinitionField> &fields(accountFieldName);
440             QContactDetailDefinitionField f;
441
442             f.setDataType(QVariant::String);
443             fields.insert("Account", f);
444             fields.insert("AccountPath", f);
445             newAccountDefinition.setFields(fields);
446             d->m_definitions.insert(QContactOnlineAccount::DefinitionName, newAccountDefinition);
447         }
448
449
450     }
451
452     error = QContactManager::NoError;
453     return d->m_definitions;
454 }
455
456 /*!
457  * \reimp
458  */
459 bool QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature) const
460 {
461     switch (feature) {
462         case QContactManager::Groups:
463         case QContactManager::ActionPreferences:
464         case QContactManager::Relationships:
465             return true;
466         case QContactManager::ArbitraryRelationshipTypes:
467             return true;
468         case QContactManager::MutableDefinitions:
469             return true;
470         case QContactManager::ChangeLogs:
471             return true;
472         default:
473             return false;
474     }
475 }
476
477
478 /*!
479  * \reimp
480  */
481 /*!
482  * Definition identifiers which are natively (fast) filterable
483  * on the default backend store managed by the manager from which the capabilities object was accessed
484  */
485 bool QContactTrackerEngine::filterSupported(const QContactFilter& filter) const
486 {
487     switch (filter.type()) {
488         case QContactFilter::InvalidFilter:
489         case QContactFilter::DefaultFilter:
490         case QContactFilter::LocalIdFilter:
491         case QContactFilter::ContactDetailFilter:
492         case QContactFilter::ContactDetailRangeFilter:
493         case QContactFilter::ActionFilter:
494         case QContactFilter::ChangeLogFilter:
495         case QContactFilter::RelationshipFilter:
496
497 // not yet done
498 //        case QContactFilter::IntersectionFilter:
499 //        case QContactFilter::UnionFilter:
500             return true;
501         default:
502             return false;
503     }
504 }
505
506 /*!
507  * Returns the list of data types supported by the Tracker engine
508  */
509 QList<QVariant::Type> QContactTrackerEngine::supportedDataTypes() const
510 {
511     // TODO: Check supported datatypes for Tracker backend.
512     QList<QVariant::Type> st;
513     st.append(QVariant::String);
514     st.append(QVariant::Date);
515     st.append(QVariant::DateTime);
516     return st;
517 }
518
519 /*!
520  * Returns the name of the Tracker engine
521  */
522 QString QContactTrackerEngine::managerName() const
523 {
524     return d->m_engineName;
525 }
526
527 /*!
528  * Returns the implementation version of this engine
529  */
530 int QContactTrackerEngine::implementationVersion() const
531 {
532     return d->m_engineVersion;
533 }
534
535 RDFVariable QContactTrackerEngine::contactDetail2Rdf(const RDFVariable& rdfContact, const QString& definitionName,
536                                                       const QString& fieldName) const
537 {
538     if (definitionName == QContactName::DefinitionName) {
539         if (fieldName == QContactName::FieldFirst) {
540             return rdfContact.property<nco::nameGiven>();
541         }
542         else if (fieldName == QContactName::FieldLast) {
543             return rdfContact.property<nco::nameFamily>();
544         }
545         else if (fieldName == QContactName::FieldMiddle) {
546             return rdfContact.property<nco::nameAdditional>();
547         }
548         else if (fieldName == QContactName::FieldPrefix) {
549             return rdfContact.property<nco::nameHonorificPrefix>();
550         }
551         else if (fieldName == QContactName::FieldSuffix) {
552             return rdfContact.property<nco::nameHonorificSuffix>();
553         }
554         else if (fieldName == QContactNickname::FieldNickname) {
555             return rdfContact.property<nco::nickname>();
556         }
557     }
558     return RDFVariable();
559 }
560
561 /*! \reimp */
562 void QContactTrackerEngine::requestDestroyed(QContactAbstractRequest* req)
563 {
564     if( d->m_requests.contains(req) )
565     {
566         QTrackerContactAsyncRequest *request = d->m_requests.take(req);
567         delete request;
568     }
569 }
570
571 /*! \reimp */
572 bool QContactTrackerEngine::startRequest(QContactAbstractRequest* req)
573 {
574     QTrackerContactAsyncRequest *request = 0;
575     switch (req->type())
576     {
577         case QContactAbstractRequest::ContactLocalIdFetchRequest:
578             request = new QTrackerContactIdFetchRequest(req, this);
579             break;
580         case QContactAbstractRequest::ContactFetchRequest:
581             request = new QTrackerContactFetchRequest(req, this);
582             break;
583         case QContactAbstractRequest::ContactSaveRequest:
584             request = new QTrackerContactSaveRequest(req, this);
585             break;
586         case QContactAbstractRequest::RelationshipFetchRequest:
587             request = new QTrackerRelationshipFetchRequest(req, this);
588             break;
589         case QContactAbstractRequest::RelationshipSaveRequest:
590             request = new QTrackerRelationshipSaveRequest(req, this);
591             break;
592         default:
593             return false;
594     }
595     d->m_requests[req] = request;
596     return true;
597 }
598
599 /*! \reimp */
600 QString QContactTrackerEngine::synthesizeDisplayLabel(const QContact& contact, QContactManager::Error& error) const
601 {
602     QString label = QContactManagerEngine::synthesizedDisplayLabel(contact, error);
603     if (label.isEmpty())
604         label = contact.detail<QContactNickname>().nickname();
605     if(label.isEmpty())
606         label = contact.detail<QContactOnlineAccount>().nickname();
607     return label;
608 }