65 |
65 |
66 if(contacts.isEmpty()) { |
66 if(contacts.isEmpty()) { |
67 QMap<int, QContactManager::Error> errors; |
67 QMap<int, QContactManager::Error> errors; |
68 errors[0] = QContactManager::BadArgumentError; |
68 errors[0] = QContactManager::BadArgumentError; |
69 QContactSaveRequest* saveRequest = qobject_cast<QContactSaveRequest*>(req); |
69 QContactSaveRequest* saveRequest = qobject_cast<QContactSaveRequest*>(req); |
70 QContactManagerEngine::updateContactSaveRequest(saveRequest, contacts, QContactManager::BadArgumentError, |
70 QContactManagerEngine::updateContactSaveRequest(saveRequest, contacts, QContactManager::BadArgumentError, errors, r->state()); |
71 errors); |
|
72 return; |
71 return; |
73 } |
72 } |
74 |
73 |
75 QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState); |
74 QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState); |
76 |
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> &))); |
77 |
79 |
78 // Save contacts with batch size |
80 // Save contacts with batch size |
79 /// @todo where to get reasonable batch size |
81 /// @todo where to get reasonable batch size |
80 int batchSize = 100; |
82 int batchSize = 100; |
81 for (int i = 0; i < contacts.size(); i+=batchSize) { |
83 for (int i = 0; i < contacts.size(); i+=batchSize) { |
123 Q_ASSERT(engine); |
127 Q_ASSERT(engine); |
124 |
128 |
125 QSettings definitions(QSettings::IniFormat, QSettings::UserScope, "Nokia", "Trackerplugin"); |
129 QSettings definitions(QSettings::IniFormat, QSettings::UserScope, "Nokia", "Trackerplugin"); |
126 QTrackerContactsLive cLive; |
130 QTrackerContactsLive cLive; |
127 RDFServicePtr service = cLive.service(); |
131 RDFServicePtr service = cLive.service(); |
128 bool isModified = false; |
|
129 |
132 |
130 foreach(QContact contact, contacts) { |
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 |
131 QContactManager::Error error; |
137 QContactManager::Error error; |
132 |
|
133 // Ensure that the contact data is ok. This comes from QContactModelEngine |
|
134 if(!engine->validateContact(contact, error)) { |
138 if(!engine->validateContact(contact, error)) { |
135 contactsFinished << contact; |
139 contactsFinished << contact; |
136 errorsOfContactsFinished[errorCount++] = error; |
140 errorsOfContactsFinished[errorCount++] = error; |
137 computeProgress(QList<QContactLocalId>()); |
141 computeProgress(QList<QContactLocalId>()); |
138 continue; |
142 continue; |
139 } |
143 } |
140 |
144 */ |
141 Live<nco::PersonContact> ncoContact; |
145 Live<nco::PersonContact> ncoContact; |
|
146 bool newContact = false; |
142 |
147 |
143 if(contact.localId() == 0) { |
148 if(contact.localId() == 0) { |
144 // Save new contact. compute ID |
149 // Save new contact. compute ID |
145 bool ok; |
150 bool ok; |
146 // what if both processes read in the same time and write at the same time, no increment |
151 // what if both processes read in the same time and write at the same time, no increment |
172 // this is important when returning contacts to sync team |
177 // this is important when returning contacts to sync team |
173 RDFVariable rdfContact = RDFVariable::fromType<nco::PersonContact>(); |
178 RDFVariable rdfContact = RDFVariable::fromType<nco::PersonContact>(); |
174 rdfContact.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
179 rdfContact.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
175 addTag(service, rdfContact, "addressbook"); |
180 addTag(service, rdfContact, "addressbook"); |
176 |
181 |
177 saveContactDetails( service, ncoContact, contact); |
182 saveContactDetails( service, ncoContact, contact, newContact); |
178 |
183 |
179 // name & nickname - different way from other details |
184 // name & nickname - different way from other details |
180 cLive.setLiveContact(ncoContact); |
185 cLive.setLiveContact(ncoContact); |
181 cLive.setQContact(contact); |
186 cLive.setQContact(contact); |
182 if( !contact.detail<QContactName>().isEmpty() || !contact.detail<QContactNickname>().isEmpty() ) { |
187 if( !contact.detail<QContactName>().isEmpty() || !contact.detail<QContactNickname>().isEmpty() ) { |
183 cLive.saveName(); |
188 cLive.saveName(); |
184 } |
189 } |
185 |
190 |
186 // TODO add async signal handling of for transaction's commitFinished |
|
187 contactsFinished << contact; |
191 contactsFinished << contact; |
188 errorsOfContactsFinished[errorCount++] = QContactManager::NoError; // TODO ask how to get error code from tracker |
192 id2Index[contact.localId()] = errorCount; |
189 } |
193 // we fill error here - once response come that everything is OK, remove entry for this contact |
190 |
194 errorsOfContactsFinished[errorCount++] = QContactManager::BadArgumentError; |
191 TrackerChangeListener *changeListener = new TrackerChangeListener(this); |
195 } |
192 if (isModified) { |
|
193 connect(changeListener, SIGNAL(contactsChanged(const QList<QContactLocalId> &)), |
|
194 SLOT(onTrackerSignal(const QList<QContactLocalId> &))); |
|
195 } else { |
|
196 connect(changeListener, SIGNAL(contactsAdded(const QList<QContactLocalId> &)), |
|
197 SLOT(onTrackerSignal(const QList<QContactLocalId> &))); |
|
198 } |
|
199 |
|
200 // remember to commit the transaction, otherwise all changes will be rolled back. |
196 // remember to commit the transaction, otherwise all changes will be rolled back. |
201 cLive.commit(); |
197 cLive.commit(); |
202 } |
198 } |
203 |
199 |
204 |
200 |
242 ncoContact->setHasAffiliation(ncoAffiliation); |
238 ncoContact->setHasAffiliation(ncoAffiliation); |
243 } |
239 } |
244 |
240 |
245 void QTrackerContactSaveRequest::saveContactDetails( RDFServicePtr service, |
241 void QTrackerContactSaveRequest::saveContactDetails( RDFServicePtr service, |
246 Live<nco::PersonContact>& ncoContact, |
242 Live<nco::PersonContact>& ncoContact, |
247 const QContact& contact) |
243 const QContact& contact, |
|
244 bool newContact) |
248 { |
245 { |
249 QStringList detailDefinitionsToSave = detailsDefinitionsInContact(contact); |
246 QStringList detailDefinitionsToSave = detailsDefinitionsInContact(contact); |
250 |
247 |
251 // all the rest might need to save to PersonContact and to Affiliation contact |
248 // all the rest might need to save to PersonContact and to Affiliation contact |
252 RDFVariable rdfPerson = RDFVariable::fromType<nco::PersonContact>(); |
249 RDFVariable rdfPerson = RDFVariable::fromType<nco::PersonContact>(); |
253 rdfPerson.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
250 rdfPerson.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
254 |
251 |
255 // Delete all existing phone numbers - office and home |
252 if(not newContact) { |
256 deletePhoneNumbers(service, rdfPerson); |
253 // Delete all existing phone numbers - office and home |
|
254 deletePhoneNumbers(service, rdfPerson); |
|
255 } |
257 |
256 |
258 foreach(QString definition, detailDefinitionsToSave) |
257 foreach(QString definition, detailDefinitionsToSave) |
259 { |
258 { |
260 QList<QContactDetail> details = contact.details(definition); |
259 QList<QContactDetail> details = contact.details(definition); |
261 Q_ASSERT(!details.isEmpty()); |
260 Q_ASSERT(!details.isEmpty()); |
266 rdfPerson1.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
265 rdfPerson1.property<nco::contactUID>() = LiteralValue(QString::number(contact.localId())); |
267 |
266 |
268 QList<QContactDetail> workDetails; |
267 QList<QContactDetail> workDetails; |
269 QList<QContactDetail> homeDetails; |
268 QList<QContactDetail> homeDetails; |
270 foreach(const QContactDetail& det, details) { |
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) ) { |
271 if( det.contexts().contains(QContactDetail::ContextWork) ) { |
272 workDetails << det; |
272 workDetails << det; |
273 } else { |
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)) { |
274 homeDetails << det; |
279 homeDetails << det; |
275 } |
280 } |
276 } |
281 } |
277 |
282 |
278 /* Save details */ |
283 /* Save details */ |
279 if(definition == QContactPhoneNumber::DefinitionName) { |
284 if(definition == QContactPhoneNumber::DefinitionName) { |
280 if (!homeDetails.isEmpty()) { |
285 if (!homeDetails.isEmpty()) { |
281 savePhoneNumbers(service, rdfPerson, homeDetails); |
286 savePhoneNumbers(service, rdfPerson, homeDetails, newContact); |
282 } |
287 } |
283 if( !workDetails.isEmpty()) { |
288 if( !workDetails.isEmpty()) { |
284 savePhoneNumbers(service, rdfAffiliation, workDetails); |
289 savePhoneNumbers(service, rdfAffiliation, workDetails, newContact); |
285 } |
290 } |
286 } |
291 } |
287 else if(definition == QContactEmailAddress::DefinitionName) { |
292 else if(definition == QContactEmailAddress::DefinitionName) { |
288 if (!homeDetails.isEmpty()) |
293 if (!homeDetails.isEmpty()) |
289 saveEmails(service, rdfPerson, homeDetails); |
294 saveEmails(service, rdfPerson, homeDetails, newContact); |
290 if( !workDetails.isEmpty()) |
295 if( !workDetails.isEmpty()) |
291 saveEmails(service, rdfAffiliation, workDetails); |
296 saveEmails(service, rdfAffiliation, workDetails, newContact); |
292 } |
297 } |
293 else if(definition == QContactAddress::DefinitionName) { |
298 else if(definition == QContactAddress::DefinitionName) { |
294 if (!homeDetails.isEmpty()) |
299 if (!homeDetails.isEmpty()) |
295 saveAddresses(service, rdfPerson, homeDetails); |
300 saveAddresses(service, rdfPerson, homeDetails, newContact); |
296 if( !workDetails.isEmpty()) |
301 if( !workDetails.isEmpty()) |
297 saveAddresses(service, rdfAffiliation, workDetails); |
302 saveAddresses(service, rdfAffiliation, workDetails, newContact); |
298 } |
303 } |
299 else if(definition == QContactUrl::DefinitionName) { |
304 else if(definition == QContactUrl::DefinitionName) { |
300 if (!homeDetails.isEmpty()) |
305 if (!homeDetails.isEmpty()) |
301 saveUrls(service, rdfPerson, homeDetails); |
306 saveUrls(service, rdfPerson, homeDetails, newContact); |
302 if( !workDetails.isEmpty()) |
307 if( !workDetails.isEmpty()) |
303 saveUrls(service, rdfAffiliation, workDetails); |
308 saveUrls(service, rdfAffiliation, workDetails, newContact); |
304 } |
309 } |
305 else { |
310 else { |
306 // TODO refactor (bug: editing photo doesn't work) |
311 // TODO refactor (bug: editing photo doesn't work) |
307 foreach(const QContactDetail &det, details ) |
312 foreach(const QContactDetail &det, details ) |
308 { |
313 { |
309 definition = det.definitionName(); |
314 definition = det.definitionName(); |
310 if(definition == QContactAvatar::DefinitionName) { |
315 if(definition == QContactAvatar::DefinitionName) { |
311 QUrl avatar = det.value(QContactAvatar::FieldAvatar); |
316 QUrl avatar = det.value(QContactAvatar::FieldImageUrl); |
312 Live<nie::DataObject> fdo = service->liveNode( avatar ); |
317 Live<nie::DataObject> fdo = service->liveNode( avatar ); |
313 ncoContact->setPhoto(fdo); |
318 ncoContact->setPhoto(fdo); |
314 } |
319 } |
315 if(definition == QContactBirthday::DefinitionName) { |
320 if(definition == QContactBirthday::DefinitionName) { |
316 ncoContact->setBirthDate(QDateTime(det.variantValue(QContactBirthday::FieldBirthday).toDate(), QTime(), Qt::UTC)); |
321 ncoContact->setBirthDate(QDateTime(det.variantValue(QContactBirthday::FieldBirthday).toDate(), QTime(), Qt::UTC)); |
362 " ]"), ""); |
367 " ]"), ""); |
363 // Temporary, because affiliation is still used - to be refactored next week to use Live nodes |
368 // Temporary, because affiliation is still used - to be refactored next week to use Live nodes |
364 // using RFC 3966 canonical URI form |
369 // using RFC 3966 canonical URI form |
365 QUrl newPhone = QString("tel:%1").arg(value); |
370 QUrl newPhone = QString("tel:%1").arg(value); |
366 Live<nco::PhoneNumber> ncoPhone = service->liveNode(newPhone); |
371 Live<nco::PhoneNumber> ncoPhone = service->liveNode(newPhone); |
|
372 if(not newContact) { |
|
373 ncoPhone->remove(); |
|
374 } |
367 |
375 |
368 QStringList subtypes = det.value<QStringList>(QContactPhoneNumber::FieldSubTypes); |
376 QStringList subtypes = det.value<QStringList>(QContactPhoneNumber::FieldSubTypes); |
369 |
377 |
370 if( subtypes.contains(QContactPhoneNumber::SubTypeMobile)) |
378 if( subtypes.contains(QContactPhoneNumber::SubTypeMobile)) |
371 up.addInsertion(newPhone, rdf::type::iri(), nco::CellPhoneNumber::iri()); |
379 up.addInsertion(newPhone, rdf::type::iri(), nco::CellPhoneNumber::iri()); |
372 else if( subtypes.contains(QContactPhoneNumber::SubTypeCar)) |
380 else if( subtypes.contains(QContactPhoneNumber::SubTypeCar)) |
373 up.addInsertion(newPhone, rdf::type::iri(), nco::CarPhoneNumber::iri()); |
381 up.addInsertion(newPhone, rdf::type::iri(), nco::CarPhoneNumber::iri()); |
374 else if( subtypes.contains(QContactPhoneNumber::SubTypeBulletinBoardSystem)) |
382 else if( subtypes.contains(QContactPhoneNumber::SubTypeBulletinBoardSystem)) |
375 up.addInsertion(newPhone, rdf::type::iri(), nco::BbsNumber::iri()); |
383 up.addInsertion(newPhone, rdf::type::iri(), nco::BbsNumber::iri()); |
376 else if( subtypes.contains(QContactPhoneNumber::SubTypeFacsimile)) |
384 else if( subtypes.contains(QContactPhoneNumber::SubTypeFax)) |
377 up.addInsertion(newPhone, rdf::type::iri(), nco::FaxNumber::iri()); |
385 up.addInsertion(newPhone, rdf::type::iri(), nco::FaxNumber::iri()); |
378 else if( subtypes.contains(QContactPhoneNumber::SubTypeModem)) |
386 else if( subtypes.contains(QContactPhoneNumber::SubTypeModem)) |
379 up.addInsertion(newPhone, rdf::type::iri(), nco::ModemNumber::iri()); |
387 up.addInsertion(newPhone, rdf::type::iri(), nco::ModemNumber::iri()); |
380 else if( subtypes.contains(QContactPhoneNumber::SubTypePager)) |
388 else if( subtypes.contains(QContactPhoneNumber::SubTypePager)) |
381 up.addInsertion(newPhone, rdf::type::iri(), nco::PagerNumber::iri()); |
389 up.addInsertion(newPhone, rdf::type::iri(), nco::PagerNumber::iri()); |
393 /*! |
401 /*! |
394 * write all phone numbers on one query to tracker |
402 * write all phone numbers on one query to tracker |
395 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
403 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
396 * changed. |
404 * changed. |
397 */ |
405 */ |
398 void QTrackerContactSaveRequest::saveEmails(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details ) |
406 void QTrackerContactSaveRequest::saveEmails(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details, bool newContact ) |
399 { |
407 { |
400 RDFUpdate up; |
408 RDFUpdate up; |
401 RDFVariable varForInsert = var.deepCopy(); |
409 RDFVariable varForInsert = var.deepCopy(); |
402 RDFVariable emails = var.property<nco::hasEmailAddress>(); |
410 RDFVariable emails = var.property<nco::hasEmailAddress>(); |
403 // delete previous references - keep email IRIs |
411 if(not newContact) { |
404 up.addDeletion(RDFVariableStatement(var, nco::hasEmailAddress::iri(), emails)); |
412 // delete previous references - keep email IRIs |
|
413 up.addDeletion(RDFVariableStatement(var, nco::hasEmailAddress::iri(), emails)); |
|
414 } |
405 |
415 |
406 foreach(const QContactDetail& det, details) |
416 foreach(const QContactDetail& det, details) |
407 { |
417 { |
408 QString value = det.value(QContactEmailAddress::FieldEmailAddress); |
418 QString value = det.value(QContactEmailAddress::FieldEmailAddress); |
409 // Temporary, because affiliation is still used - to be refactored next week to use only Live nodes |
419 // Temporary, because affiliation is still used - to be refactored next week to use only Live nodes |
419 /*! |
429 /*! |
420 * write all Urls |
430 * write all Urls |
421 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
431 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
422 * changed. |
432 * changed. |
423 */ |
433 */ |
424 void QTrackerContactSaveRequest::saveUrls(RDFServicePtr service, RDFVariable &rdfContact, const QList<QContactDetail> &details ) |
434 void QTrackerContactSaveRequest::saveUrls(RDFServicePtr service, RDFVariable &rdfContact, const QList<QContactDetail> &details, bool newContact ) |
425 { |
435 { |
426 RDFUpdate up; |
436 RDFUpdate up; |
427 RDFVariable varForInsert = rdfContact.deepCopy(); |
437 RDFVariable varForInsert = rdfContact.deepCopy(); |
428 RDFVariable urls = rdfContact.property<nco::url>(); |
438 RDFVariable urls = rdfContact.property<nco::url>(); |
429 RDFVariable urltypes = urls.property<rdf::type>(); |
439 RDFVariable urltypes = urls.property<rdf::type>(); |
430 |
440 |
431 RDFVariable websiteUrls = rdfContact.property<nco::websiteUrl>(); |
441 RDFVariable websiteUrls = rdfContact.property<nco::websiteUrl>(); |
432 RDFVariable websiteUrlTypes = websiteUrls.property<rdf::type>(); |
442 RDFVariable websiteUrlTypes = websiteUrls.property<rdf::type>(); |
433 |
443 |
434 up.addDeletion(RDFVariableStatement(rdfContact, nco::url::iri(), urls)); |
444 if(not newContact) { |
435 up.addDeletion(RDFVariableStatement(rdfContact, nco::websiteUrl::iri(), websiteUrls)); |
445 // first part - deleting previous before adding new again is to be removed |
436 // 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 } |
437 |
449 |
438 // second part, write all urls |
450 // second part, write all urls |
439 foreach(const QContactDetail& det, details) |
451 foreach(const QContactDetail& det, details) |
440 { |
452 { |
441 QUrl newUrl(det.value(QContactUrl::FieldUrl));//::tracker()->createLiveNode().uri(); |
453 QUrl newUrl(det.value(QContactUrl::FieldUrl));//::tracker()->createLiveNode().uri(); |
454 /*! |
466 /*! |
455 * write all phone numbers on one query to tracker |
467 * write all phone numbers on one query to tracker |
456 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
468 * TODO this is temporary code for creating new, saving contacts need to handle only what was |
457 * changed. |
469 * changed. |
458 */ |
470 */ |
459 void QTrackerContactSaveRequest::saveAddresses(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details ) |
471 void QTrackerContactSaveRequest::saveAddresses(RDFServicePtr service, RDFVariable &var, const QList<QContactDetail> &details, bool newContact ) |
460 { |
472 { |
461 RDFUpdate up; |
473 RDFUpdate up; |
462 RDFVariable varForInsert = var.deepCopy(); |
474 RDFVariable varForInsert = var.deepCopy(); |
463 RDFVariable addresses = var.property<nco::hasPostalAddress>(); |
475 RDFVariable addresses = var.property<nco::hasPostalAddress>(); |
464 RDFVariable types = addresses.property<rdf::type>(); |
476 RDFVariable types = addresses.property<rdf::type>(); |
465 up.addDeletion(RDFVariableStatement(var, nco::hasPostalAddress::iri(), addresses)); |
477 if(not newContact) { |
466 up.addDeletion(addresses, rdf::type::iri(), types); |
478 up.addDeletion(RDFVariableStatement(var, nco::hasPostalAddress::iri(), addresses)); |
|
479 up.addDeletion(addresses, rdf::type::iri(), types); |
|
480 } |
467 foreach(const QContactDetail& det, details) |
481 foreach(const QContactDetail& det, details) |
468 { |
482 { |
469 QUrl newPostalAddress = ::tracker()->createLiveNode().uri(); |
483 QUrl newPostalAddress = ::tracker()->createLiveNode().uri(); |
470 // TODO nco:DomesticDeliveryAddress, nco:InternationalDeliveryAddress, nco:ParcelDeliveryAddress |
484 // TODO nco:DomesticDeliveryAddress, nco:InternationalDeliveryAddress, nco:ParcelDeliveryAddress |
471 up.addInsertion(newPostalAddress, rdf::type::iri(), nco::PostalAddress::iri()); |
485 up.addInsertion(newPostalAddress, rdf::type::iri(), nco::PostalAddress::iri()); |