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