Merge lp:~renatofilho/address-book-service/create-source into lp:address-book-service

Proposed by Renato Araujo Oliveira Filho
Status: Superseded
Proposed branch: lp:~renatofilho/address-book-service/create-source
Merge into: lp:address-book-service
Diff against target: 2318 lines (+905/-330)
22 files modified
3rd_party/folks/dummy/lib/dummy-full-persona.vala (+12/-1)
3rd_party/folks/dummy/lib/dummy-persona.vala (+33/-3)
CMakeLists.txt (+1/-0)
debian/changelog (+16/-0)
debian/control (+1/-0)
lib/CMakeLists.txt (+2/-0)
lib/addressbook-adaptor.cpp (+10/-0)
lib/addressbook-adaptor.h (+6/-0)
lib/addressbook.cpp (+175/-72)
lib/addressbook.h (+9/-3)
lib/contacts-map.cpp (+44/-22)
lib/contacts-map.h (+9/-3)
lib/qindividual.cpp (+181/-137)
lib/qindividual.h (+19/-6)
lib/update-contact-request.cpp (+173/-72)
lib/update-contact-request.h (+17/-7)
lib/view.cpp (+3/-1)
tests/unittest/CMakeLists.txt (+3/-0)
tests/unittest/addressbook-test.cpp (+42/-2)
tests/unittest/contact-link-test.cpp (+100/-0)
tests/unittest/dummy-backend.cpp (+36/-1)
tests/unittest/dummy-backend.h (+13/-0)
To merge this branch: bzr merge lp:~renatofilho/address-book-service/create-source
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+206826@code.launchpad.net

This proposal has been superseded by a proposal from 2014-02-18.

Commit message

Implemented createSource functionality.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
100. By Renato Araujo Oliveira Filho

Merged parent branch.

101. By Renato Araujo Oliveira Filho

Merged parent branch.

102. By Renato Araujo Oliveira Filho

Merged mainline.

103. By Renato Araujo Oliveira Filho

Attempt to fix power pc build

104. By Renato Araujo Oliveira Filho

Attempt to fix powerpc tests.

105. By Renato Araujo Oliveira Filho

Attempt to fix powerpc tests.

106. By Renato Araujo Oliveira Filho

Moved DirtyContactsNotify class to a individual file and make it a QObject.

107. By Renato Araujo Oliveira Filho

Make sure that the temporary directory is created.

108. By Renato Araujo Oliveira Filho

Make the dummy backend start async in an attempt to fix powerPC tests.

109. By Renato Araujo Oliveira Filho

Added debug messages.

110. By Renato Araujo Oliveira Filho

Skip tests when running on PPC machines.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '3rd_party/folks/dummy/lib/dummy-full-persona.vala'
2--- 3rd_party/folks/dummy/lib/dummy-full-persona.vala 2013-12-13 14:24:51 +0000
3+++ 3rd_party/folks/dummy/lib/dummy-full-persona.vala 2014-02-18 00:13:04 +0000
4@@ -72,6 +72,15 @@
5 PostalAddressDetails,
6 WebServiceDetails
7 {
8+ private const string[] _default_linkable_properties =
9+ {
10+ "im-addresses",
11+ "email-addresses",
12+ "local-ids",
13+ "web-service-addresses"
14+ };
15+
16+
17 /**
18 * Create a new ‘full’ persona.
19 *
20@@ -88,7 +97,8 @@
21 * @since UNRELEASED
22 */
23 public FullPersona (PersonaStore store, string contact_id,
24- bool is_user = false, string[] linkable_properties = {})
25+ bool is_user = false,
26+ string[] linkable_properties = {})
27 {
28 base (store, contact_id, is_user, linkable_properties);
29 }
30@@ -104,6 +114,7 @@
31 this._groups_ro = this._groups.read_only_view;
32 this._roles_ro = this._roles.read_only_view;
33 this._anti_links_ro = this._anti_links.read_only_view;
34+ this.update_linkable_properties(FullPersona._default_linkable_properties);
35 }
36
37 private HashMultiMap<string, WebServiceFieldDetails> _web_service_addresses =
38
39=== modified file '3rd_party/folks/dummy/lib/dummy-persona.vala'
40--- 3rd_party/folks/dummy/lib/dummy-persona.vala 2013-12-13 14:24:51 +0000
41+++ 3rd_party/folks/dummy/lib/dummy-persona.vala 2014-02-18 00:13:04 +0000
42@@ -53,7 +53,6 @@
43 public class FolksDummy.Persona : Folks.Persona
44 {
45 private string[] _linkable_properties = new string[0];
46-
47 /**
48 * {@inheritDoc}
49 *
50@@ -96,8 +95,8 @@
51 *
52 * @since UNRELEASED
53 */
54- public Persona (PersonaStore store, string contact_id, bool is_user = false,
55- string[] linkable_properties = {})
56+ public Persona (PersonaStore store, string contact_id,
57+ bool is_user = false, string[] linkable_properties = {})
58 {
59 var uid = Folks.Persona.build_uid (BACKEND_NAME, store.id, contact_id);
60 var iid = store.id + ":" + contact_id;
61@@ -233,6 +232,37 @@
62 }
63 }
64
65+ public void update_linkable_properties (string[] linkable_properties)
66+ {
67+ var new_linkable_properties = new HashSet<string> ();
68+ new_linkable_properties.add_all_array(linkable_properties);
69+
70+ /* Check for changes. */
71+ var changed = false;
72+
73+
74+ if (this._linkable_properties.length != new_linkable_properties.size)
75+ {
76+ changed = true;
77+ }
78+ else
79+ {
80+ foreach (var p in this._linkable_properties)
81+ {
82+ if (new_linkable_properties.contains (p) == false)
83+ {
84+ changed = true;
85+ break;
86+ }
87+ }
88+ }
89+ if (changed == true)
90+ {
91+ this._linkable_properties = new_linkable_properties.to_array ();
92+ this.notify_property ("linkable-properties");
93+ }
94+ }
95+
96 /**
97 * Delay between property changes and notifications.
98 *
99
100=== modified file 'CMakeLists.txt'
101--- CMakeLists.txt 2013-11-06 14:42:23 +0000
102+++ CMakeLists.txt 2014-02-18 00:13:04 +0000
103@@ -14,6 +14,7 @@
104 pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
105 pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
106 pkg_check_modules(FOLKS REQUIRED folks>=0.9.0)
107+pkg_check_modules(FOLKS_EDS REQUIRED folks-eds)
108
109 if(FOLKS_VERSION VERSION_LESS "0.9.5")
110 set(FOLKS_VERSION_LESS_THAN_0_9_5 "1")
111
112=== modified file 'debian/changelog'
113--- debian/changelog 2014-02-13 16:49:03 +0000
114+++ debian/changelog 2014-02-18 00:13:04 +0000
115@@ -1,3 +1,19 @@
116+address-book-service (0.1.1) UNRELEASED; urgency=medium
117+
118+ * Avoid the persona to get linked automatically during the contact create or
119+ edit.
120+ * Create test to remove contacts functionality.
121+ * Updated dummy backend code.
122+ * Created test for add contacts.
123+ * Rewrite unit test to work in a server/client way.
124+ * Moved static address-book-service-lib to a different directory.
125+ * Fork dummy backend from folks tree as a temporary solution before its get
126+ release on folks.
127+ * Check if query was destroyed on client side before delete it on the server
128+ side.
129+
130+ -- Renato Araujo Oliveira Filho <renato@ubuntu> Fri, 24 Jan 2014 08:04:56 -0300
131+
132 address-book-service (0.1.0+14.04.20140213-0ubuntu1) trusty; urgency=low
133
134 [ Renato Araujo Oliveira Filho ]
135
136=== modified file 'debian/control'
137--- debian/control 2013-10-30 00:19:00 +0000
138+++ debian/control 2014-02-18 00:13:04 +0000
139@@ -7,6 +7,7 @@
140 debhelper (>= 9),
141 gobject-introspection (>= 1.31.0-2~),
142 libfolks-dev,
143+ libfolks-eds-dev,
144 libgee-0.8-dev (>= 0.8.4),
145 qt5-default,
146 qt5-qmake,
147
148=== modified file 'lib/CMakeLists.txt'
149--- lib/CMakeLists.txt 2013-10-30 19:07:49 +0000
150+++ lib/CMakeLists.txt 2014-02-18 00:13:04 +0000
151@@ -38,6 +38,7 @@
152 ${GLIB_LIBRARIES}
153 ${GIO_LIBRARIES}
154 ${FOLKS_LIBRARIES}
155+ ${FOLKS_EDS_LIBRARIES}
156 )
157
158 qt5_use_modules(${CONTACTS_SERVICE_LIB} Core Contacts DBus Versit)
159@@ -48,4 +49,5 @@
160 ${GLIB_INCLUDE_DIRS}
161 ${GIO_INCLUDE_DIRS}
162 ${FOLKS_INCLUDE_DIRS}
163+ ${FOLKS_EDS_INCLUDE_DIRS}
164 )
165
166=== modified file 'lib/addressbook-adaptor.cpp'
167--- lib/addressbook-adaptor.cpp 2013-11-04 18:34:43 +0000
168+++ lib/addressbook-adaptor.cpp 2014-02-18 00:13:04 +0000
169@@ -54,6 +54,16 @@
170 return Source();
171 }
172
173+Source AddressBookAdaptor::createSource(const QString &sourceName, const QDBusMessage &message)
174+{
175+ message.setDelayedReply(true);
176+ QMetaObject::invokeMethod(m_addressBook, "createSource",
177+ Qt::QueuedConnection,
178+ Q_ARG(const QString&, sourceName),
179+ Q_ARG(const QDBusMessage&, message));
180+ return Source();
181+}
182+
183 QString AddressBookAdaptor::createContact(const QString &contact, const QString &source, const QDBusMessage &message)
184 {
185 message.setDelayedReply(true);
186
187=== modified file 'lib/addressbook-adaptor.h'
188--- lib/addressbook-adaptor.h 2013-11-01 01:10:12 +0000
189+++ lib/addressbook-adaptor.h 2014-02-18 00:13:04 +0000
190@@ -62,6 +62,11 @@
191 " <arg direction=\"out\" type=\"(sb)\"/>\n"
192 " <annotation value=\"Source\" name=\"com.trolltech.QtDBus.QtTypeName.Out0\"/>\n"
193 " </method>\n"
194+" <method name=\"createSource\">\n"
195+" <arg direction=\"in\" type=\"s\"/>\n"
196+" <arg direction=\"out\" type=\"(sb)\"/>\n"
197+" <annotation value=\"Source\" name=\"com.trolltech.QtDBus.QtTypeName.Out0\"/>\n"
198+" </method>\n"
199 " <method name=\"sortFields\">\n"
200 " <arg direction=\"out\" type=\"as\"/>\n"
201 " </method>\n"
202@@ -103,6 +108,7 @@
203 public Q_SLOTS:
204 SourceList availableSources(const QDBusMessage &message);
205 Source source(const QDBusMessage &message);
206+ Source createSource(const QString &sourceName, const QDBusMessage &message);
207 QStringList sortFields();
208 QDBusObjectPath query(const QString &clause, const QString &sort, const QStringList &sources);
209 int removeContacts(const QStringList &contactIds, const QDBusMessage &message);
210
211=== modified file 'lib/addressbook.cpp'
212--- lib/addressbook.cpp 2013-11-05 19:26:25 +0000
213+++ lib/addressbook.cpp 2014-02-18 00:13:04 +0000
214@@ -31,6 +31,8 @@
215 #include <signal.h>
216 #include <sys/socket.h>
217
218+#include <folks/folks-eds.h>
219+
220 //this timeout represents how long the server will wait for changes on the contact before notify the client
221 #define NOTIFY_CONTACTS_TIMEOUT 500
222
223@@ -38,6 +40,14 @@
224
225 namespace
226 {
227+
228+class CreateContactData
229+{
230+public:
231+ QDBusMessage m_message;
232+ galera::AddressBook *m_addressbook;
233+};
234+
235 class UpdateContactsData
236 {
237 public:
238@@ -58,6 +68,14 @@
239 int m_sucessCount;
240 };
241
242+class CreateSourceData
243+{
244+public:
245+ QString sourceName;
246+ galera::AddressBook *m_addressbook;
247+ QDBusMessage m_message;
248+};
249+
250 }
251
252 namespace galera
253@@ -242,6 +260,37 @@
254 return Source();
255 }
256
257+Source AddressBook::createSource(const QString &sourceId, const QDBusMessage &message)
258+{
259+ CreateSourceData *data = new CreateSourceData;
260+ data->m_addressbook = this;
261+ data->m_message = message;
262+ data->sourceName = sourceId;
263+ edsf_persona_store_create_address_book(sourceId.toUtf8().data(),
264+ (GAsyncReadyCallback) AddressBook::createSourceDone,
265+ data);
266+ return Source();
267+}
268+
269+void AddressBook::createSourceDone(GObject *source,
270+ GAsyncResult *res,
271+ void *data)
272+{
273+ CreateSourceData *cData = static_cast<CreateSourceData*>(data);
274+ GError *error = 0;
275+ Source src;
276+ edsf_persona_store_create_address_book_finish(res, &error);
277+ if (error) {
278+ qWarning() << "Fail to create source" << error->message;
279+ g_error_free(error);
280+ } else {
281+ src = Source(cData->sourceName, false);
282+ }
283+ QDBusMessage reply = cData->m_message.createReply(QVariant::fromValue<Source>(src));
284+ QDBusConnection::sessionBus().send(reply);
285+ delete cData;
286+}
287+
288 void AddressBook::getSource(const QDBusMessage &message, bool onlyTheDefault)
289 {
290 FolksBackendStore *backendStore = folks_backend_store_dup();
291@@ -338,13 +387,15 @@
292 if (!qcontact.isEmpty()) {
293 GHashTable *details = QIndividual::parseDetails(qcontact);
294 //TOOD: lookup for source and use the correct store
295- QDBusMessage *cpy = new QDBusMessage(message);
296+ CreateContactData *data = new CreateContactData;
297+ data->m_message = message;
298+ data->m_addressbook = this;
299 folks_individual_aggregator_add_persona_from_details(m_individualAggregator,
300 NULL, //parent
301 folks_individual_aggregator_get_primary_store(m_individualAggregator),
302 details,
303 (GAsyncReadyCallback) createContactDone,
304- (void*) cpy);
305+ (void*) data);
306 g_hash_table_destroy(details);
307 return "";
308 }
309@@ -429,6 +480,28 @@
310 }
311 }
312
313+void AddressBook::addAntiLinksDone(FolksAntiLinkable *antilinkable,
314+ GAsyncResult *result,
315+ void *data)
316+{
317+ CreateContactData *createData = static_cast<CreateContactData*>(data);
318+ QDBusMessage reply;
319+
320+ GError *error = 0;
321+ folks_anti_linkable_change_anti_links_finish(antilinkable, result, &error);
322+ if (error) {
323+ qWarning() << "Fail to anti link pesona" << folks_persona_get_display_id(FOLKS_PERSONA(antilinkable)) << error->message;
324+ reply = createData->m_message.createErrorReply("Fail to anti link pesona:", error->message);
325+ g_error_free(error);
326+ } else {
327+ FolksIndividual *individual = folks_persona_get_individual(FOLKS_PERSONA(antilinkable));
328+ // return the result/contactId to the client
329+ reply = createData->m_message.createReply(QString::fromUtf8(folks_individual_get_id(individual)));
330+ }
331+ QDBusConnection::sessionBus().send(reply);
332+ delete createData;
333+}
334+
335 QStringList AddressBook::sortFields()
336 {
337 return SortClause::supportedFields();
338@@ -455,25 +528,26 @@
339 m_updateCommandResult = contacts;
340 m_updateCommandPendingContacts << VCardParser::vcardToContact(contacts);
341
342- updateContactsDone(0, QString());
343-
344+ updateContactsDone("", "");
345 return QStringList();
346 }
347
348-void AddressBook::updateContactsDone(galera::QIndividual *individual, const QString &error)
349+void AddressBook::updateContactsDone(const QString &contactId,
350+ const QString &error)
351 {
352- Q_UNUSED(individual);
353 qDebug() << Q_FUNC_INFO;
354-
355 int currentContactIndex = m_updateCommandResult.size() - m_updateCommandPendingContacts.size() - 1;
356
357 if (!error.isEmpty()) {
358 // update the result with the error
359 m_updateCommandResult[currentContactIndex] = error;
360- } else if (individual){
361+ } else if (!contactId.isEmpty()){
362 // update the result with the new contact info
363- m_updatedIds << individual->id();
364- QStringList newContacts = VCardParser::contactToVcard(QList<QContact>() << individual->contact());
365+ m_updatedIds << contactId;
366+ ContactEntry *entry = m_contacts->value(contactId);
367+ Q_ASSERT(entry);
368+ QContact contact = entry->individual()->contact();
369+ QStringList newContacts = VCardParser::contactToVcard(QList<QContact>() << contact);
370 if (newContacts.length() == 1) {
371 m_updateCommandResult[currentContactIndex] = newContacts[0];
372 } else {
373@@ -485,12 +559,13 @@
374 QContact newContact = m_updateCommandPendingContacts.takeFirst();
375 ContactEntry *entry = m_contacts->value(newContact.detail<QContactGuid>().guid());
376 if (entry) {
377- entry->individual()->update(newContact, this, SLOT(updateContactsDone(galera::QIndividual*,QString)));
378+ entry->individual()->update(newContact, this,
379+ SLOT(updateContactsDone(QString,QString)));
380 } else {
381- updateContactsDone(0, "Contact not found!");
382+ updateContactsDone("", "Contact not found!");
383 }
384 } else {
385- folks_persona_store_flush(folks_individual_aggregator_get_primary_store(m_individualAggregator), 0, 0);
386+
387 QDBusMessage reply = m_updateCommandReplyMessage.createReply(m_updateCommandResult);
388 QDBusConnection::sessionBus().send(reply);
389
390@@ -504,17 +579,13 @@
391 }
392 }
393
394-
395 QString AddressBook::removeContact(FolksIndividual *individual)
396 {
397- if (m_contacts->contains(individual)) {
398- ContactEntry *ci = m_contacts->take(individual);
399- if (ci) {
400- QString id = QString::fromUtf8(folks_individual_get_id(individual));
401- qDebug() << "Remove contact" << id;
402- delete ci;
403- return id;
404- }
405+ QString contactId = QString::fromUtf8(folks_individual_get_id(individual));
406+ ContactEntry *ci = m_contacts->take(contactId);
407+ if (ci) {
408+ delete ci;
409+ return contactId;
410 }
411 return QString();
412 }
413@@ -526,7 +597,6 @@
414 if (entry) {
415 entry->individual()->setIndividual(individual);
416 } else {
417- Q_ASSERT(!m_contacts->contains(individual));
418 QIndividual *i = new QIndividual(individual, m_individualAggregator);
419 i->addListener(this, SLOT(individualChanged(QIndividual*)));
420 m_contacts->insert(new ContactEntry(i));
421@@ -540,51 +610,72 @@
422 AddressBook *self)
423 {
424 qDebug() << Q_FUNC_INFO;
425- QStringList removedIds;
426- QStringList addedIds;
427-
428- GeeIterator *iter;
429- GeeSet *removed = gee_multi_map_get_keys(changes);
430- GeeCollection *added = gee_multi_map_get_values(changes);
431-
432- iter = gee_iterable_iterator(GEE_ITERABLE(added));
433- while(gee_iterator_next(iter)) {
434- FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter));
435- if (individual) {
436- // add contact to the map
437- addedIds << self->addContact(individual);
438- g_object_unref(individual);
439- }
440- }
441- g_object_unref (iter);
442-
443-
444- iter = gee_iterable_iterator(GEE_ITERABLE(removed));
445- while(gee_iterator_next(iter)) {
446- FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter));
447- if (individual) {
448- QString id = QString::fromUtf8(folks_individual_get_id(individual));
449- if (!addedIds.contains(id)) {
450- // delete from contact map
451- removedIds << self->removeContact(individual);
452- }
453- g_object_unref(individual);
454- }
455- }
456- g_object_unref (iter);
457-
458- //TODO: check for linked and unliked contacts
459+ Q_UNUSED(individualAggregator);
460+
461+ QSet<QString> removedIds;
462+ QSet<QString> addedIds;
463+ QSet<QString> updatedIds;
464+
465+ GeeSet *keys = gee_multi_map_get_keys(changes);
466+ GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(keys));
467+
468+ while(gee_iterator_next(iter)) {
469+ FolksIndividual *individualKey = FOLKS_INDIVIDUAL(gee_iterator_get(iter));
470+ GeeCollection *values = gee_multi_map_get(changes, individualKey);
471+ GeeIterator *iterV;
472+
473+ iterV = gee_iterable_iterator(GEE_ITERABLE(values));
474+ while(gee_iterator_next(iterV)) {
475+ FolksIndividual *individualValue = FOLKS_INDIVIDUAL(gee_iterator_get(iterV));
476+
477+ // contact added
478+ if (individualKey == 0) {
479+ addedIds << self->addContact(individualValue);
480+ } else if (individualValue != 0){
481+ qDebug() << "Link changes";
482+ QString idValue = QString::fromUtf8(folks_individual_get_id(individualValue));
483+ if (self->m_contacts->value(idValue)) {
484+ updatedIds << self->addContact(individualValue);
485+ } else {
486+ addedIds << self->addContact(individualValue);
487+ }
488+ }
489+
490+ if (individualValue) {
491+ g_object_unref(individualValue);
492+ }
493+ }
494+
495+ g_object_unref(iterV);
496+ g_object_unref(values);
497+
498+ if (individualKey) {
499+ QString id = QString::fromUtf8(folks_individual_get_id(individualKey));
500+ if (!addedIds.contains(id) &&
501+ !updatedIds.contains(id)) {
502+ removedIds << self->removeContact(individualKey);
503+ }
504+ g_object_unref(individualKey);
505+ }
506+ }
507+
508+ g_object_unref(keys);
509+
510 if (!removedIds.isEmpty() && self->m_ready) {
511- Q_EMIT self->m_adaptor->contactsRemoved(removedIds);
512+ Q_EMIT self->m_adaptor->contactsRemoved(removedIds.toList());
513 }
514
515 if (!addedIds.isEmpty() && self->m_ready) {
516- Q_EMIT self->m_adaptor->contactsAdded(addedIds);
517- }
518-
519- g_object_unref(added);
520- g_object_unref(removed);
521+ Q_EMIT self->m_adaptor->contactsAdded(addedIds.toList());
522+ }
523+
524+ if (!updatedIds.isEmpty() && self->m_ready) {
525+ Q_EMIT self->m_adaptor->contactsUpdated(updatedIds.toList());
526+ }
527+
528 qDebug() << "Added" << addedIds;
529+ qDebug() << "Removed" << removedIds;
530+ qDebug() << "Changed" << updatedIds;
531 }
532
533 void AddressBook::prepareFolksDone(GObject *source,
534@@ -598,29 +689,41 @@
535
536 void AddressBook::createContactDone(FolksIndividualAggregator *individualAggregator,
537 GAsyncResult *res,
538- QDBusMessage *msg)
539+ void *data)
540 {
541- qDebug() << "Create Contact Done" << msg;
542+ CreateContactData *createData = static_cast<CreateContactData*>(data);
543+
544 FolksPersona *persona;
545 GError *error = NULL;
546 QDBusMessage reply;
547 persona = folks_individual_aggregator_add_persona_from_details_finish(individualAggregator, res, &error);
548 if (error != NULL) {
549 qWarning() << "Failed to create individual from contact:" << error->message;
550- reply = msg->createErrorReply("Failed to create individual from contact", error->message);
551+ reply = createData->m_message.createErrorReply("Failed to create individual from contact", error->message);
552 g_clear_error(&error);
553 } else if (persona == NULL) {
554 qWarning() << "Failed to create individual from contact: Persona already exists";
555- reply = msg->createErrorReply("Failed to create individual from contact", "Contact already exists");
556+ reply = createData->m_message.createErrorReply("Failed to create individual from contact", "Contact already exists");
557+ } else if (QIndividual::autoLinkEnabled()){
558+ FolksIndividual *individual = folks_persona_get_individual(persona);
559+ reply = createData->m_message.createReply(QString::fromUtf8(folks_individual_get_id(individual)));
560 } else {
561- qDebug() << "Persona created:" << persona;
562- FolksIndividual *individual;
563- individual = folks_persona_get_individual(persona);
564- reply = msg->createReply(QString::fromUtf8(folks_individual_get_id(individual)));
565+ // avoid the new persona get linked
566+ GeeHashSet *antiLinks = gee_hash_set_new(G_TYPE_STRING,
567+ (GBoxedCopyFunc) g_strdup,
568+ g_free,
569+ NULL, NULL, NULL, NULL, NULL, NULL);
570+ gee_collection_add(GEE_COLLECTION(antiLinks), "*");
571+ folks_anti_linkable_change_anti_links(FOLKS_ANTI_LINKABLE(persona),
572+ GEE_SET(antiLinks),
573+ (GAsyncReadyCallback) addAntiLinksDone,
574+ data);
575+ g_object_unref(antiLinks);
576+ return;
577 }
578 //TODO: use dbus connection
579 QDBusConnection::sessionBus().send(reply);
580- delete msg;
581+ delete createData;
582 }
583
584 void AddressBook::isQuiescentChanged(GObject *source, GParamSpec *param, AddressBook *self)
585
586=== modified file 'lib/addressbook.h'
587--- lib/addressbook.h 2013-11-01 13:15:51 +0000
588+++ lib/addressbook.h 2014-02-18 00:13:04 +0000
589@@ -69,10 +69,11 @@
590 void shutdown();
591 SourceList availableSources(const QDBusMessage &message);
592 Source source(const QDBusMessage &message);
593+ Source createSource(const QString &sourceId, const QDBusMessage &message);
594 QString createContact(const QString &contact, const QString &source, const QDBusMessage &message);
595 int removeContacts(const QStringList &contactIds, const QDBusMessage &message);
596 QStringList updateContacts(const QStringList &contacts, const QDBusMessage &message);
597- void updateContactsDone(galera::QIndividual *individual, const QString &error);
598+ void updateContactsDone(const QString &contactId, const QString &error);
599
600 private Q_SLOTS:
601 void viewClosed();
602@@ -139,11 +140,16 @@
603 AddressBook *self);
604 static void createContactDone(FolksIndividualAggregator *individualAggregator,
605 GAsyncResult *res,
606- QDBusMessage *msg);
607+ void *data);
608 static void removeContactDone(FolksIndividualAggregator *individualAggregator,
609 GAsyncResult *result,
610 void *data);
611-
612+ static void addAntiLinksDone(FolksAntiLinkable *antilinkable,
613+ GAsyncResult *result,
614+ void *data);
615+ static void createSourceDone(GObject *source,
616+ GAsyncResult *res,
617+ void *data);
618 friend class DirtyContactsNotify;
619 };
620
621
622=== modified file 'lib/contacts-map.cpp'
623--- lib/contacts-map.cpp 2013-11-06 18:49:59 +0000
624+++ lib/contacts-map.cpp 2014-02-18 00:13:04 +0000
625@@ -33,7 +33,7 @@
626
627 ContactEntry::~ContactEntry()
628 {
629- delete m_individual;
630+ delete m_individual;
631 }
632
633 QIndividual *ContactEntry::individual() const
634@@ -52,11 +52,6 @@
635 clear();
636 }
637
638-ContactEntry *ContactsMap::value(FolksIndividual *individual) const
639-{
640- return m_individualsToEntry[individual];
641-}
642-
643 ContactEntry *ContactsMap::value(const QString &id) const
644 {
645 return m_idToEntry[id];
646@@ -64,32 +59,33 @@
647
648 ContactEntry *ContactsMap::take(FolksIndividual *individual)
649 {
650- if (m_individualsToEntry.remove(individual)) {
651- return m_idToEntry.take(folks_individual_get_id(individual));
652- }
653- return 0;
654+ QString contactId = QString::fromUtf8(folks_individual_get_id(individual));
655+ return take(contactId);
656+}
657+
658+ContactEntry *ContactsMap::take(const QString &id)
659+{
660+ QMutexLocker locker(&m_mutex);
661+ return m_idToEntry.take(id);
662 }
663
664 void ContactsMap::remove(const QString &id)
665 {
666- ContactEntry *entry = m_idToEntry[id];
667+ QMutexLocker locker(&m_mutex);
668+ ContactEntry *entry = m_idToEntry.value(id,0);
669 if (entry) {
670- m_individualsToEntry.remove(entry->individual()->individual());
671 m_idToEntry.remove(id);
672 delete entry;
673 }
674 }
675
676-bool ContactsMap::contains(FolksIndividual *individual) const
677-{
678- return m_individualsToEntry.contains(individual);
679-}
680-
681 void ContactsMap::insert(ContactEntry *entry)
682 {
683+ QMutexLocker locker(&m_mutex);
684 FolksIndividual *fIndividual = entry->individual()->individual();
685- m_idToEntry.insert(folks_individual_get_id(fIndividual), entry);
686- m_individualsToEntry.insert(fIndividual, entry);
687+ if (fIndividual) {
688+ m_idToEntry.insert(folks_individual_get_id(fIndividual), entry);
689+ }
690 }
691
692 int ContactsMap::size() const
693@@ -99,16 +95,25 @@
694
695 void ContactsMap::clear()
696 {
697+ QMutexLocker locker(&m_mutex);
698 QList<ContactEntry*> entries = m_idToEntry.values();
699 m_idToEntry.clear();
700- m_individualsToEntry.clear();
701-
702 qDeleteAll(entries);
703 }
704
705+void ContactsMap::lock()
706+{
707+ m_mutex.lock();
708+}
709+
710+void ContactsMap::unlock()
711+{
712+ m_mutex.unlock();
713+}
714+
715 QList<ContactEntry*> ContactsMap::values() const
716 {
717- return m_individualsToEntry.values();
718+ return m_idToEntry.values();
719 }
720
721 ContactEntry *ContactsMap::valueFromVCard(const QString &vcard) const
722@@ -125,4 +130,21 @@
723 return 0;
724 }
725
726+bool ContactsMap::contains(FolksIndividual *individual) const
727+{
728+ QString contactId = QString::fromUtf8(folks_individual_get_id(individual));
729+ return contains(contactId);
730+}
731+
732+bool ContactsMap::contains(const QString &id) const
733+{
734+ return m_idToEntry.contains(id);
735+}
736+
737+ContactEntry *ContactsMap::value(FolksIndividual *individual) const
738+{
739+ QString contactId = QString::fromUtf8(folks_individual_get_id(individual));
740+ return m_idToEntry.value(contactId, 0);
741+}
742+
743 } //namespace
744
745=== modified file 'lib/contacts-map.h'
746--- lib/contacts-map.h 2013-11-05 00:25:03 +0000
747+++ lib/contacts-map.h 2014-02-18 00:13:04 +0000
748@@ -22,6 +22,7 @@
749 #include <QtCore/QString>
750 #include <QtCore/QStringList>
751 #include <QtCore/QHash>
752+#include <QtCore/QMutex>
753
754 #include <folks/folks.h>
755 #include <glib.h>
756@@ -57,20 +58,25 @@
757 ContactEntry *valueFromVCard(const QString &vcard) const;
758
759 bool contains(FolksIndividual *individual) const;
760+ bool contains(const QString &id) const;
761+
762 ContactEntry *value(FolksIndividual *individual) const;
763 ContactEntry *value(const QString &id) const;
764+
765 ContactEntry *take(FolksIndividual *individual);
766+ ContactEntry *take(const QString &id);
767+
768 void remove(const QString &id);
769 void insert(ContactEntry *entry);
770 int size() const;
771 void clear();
772-
773-
774+ void lock();
775+ void unlock();
776 QList<ContactEntry*> values() const;
777
778 private:
779- QHash<FolksIndividual *, ContactEntry*> m_individualsToEntry;
780 QHash<QString, ContactEntry*> m_idToEntry;
781+ QMutex m_mutex;
782 };
783
784 } //namespace
785
786=== modified file 'lib/qindividual.cpp'
787--- lib/qindividual.cpp 2013-11-05 00:25:03 +0000
788+++ lib/qindividual.cpp 2014-02-18 00:13:04 +0000
789@@ -118,15 +118,15 @@
790
791 namespace galera
792 {
793+bool QIndividual::m_autoLink = false;
794
795 QIndividual::QIndividual(FolksIndividual *individual, FolksIndividualAggregator *aggregator)
796- : m_individual(individual),
797- m_primaryPersona(0),
798+ : m_individual(0),
799 m_aggregator(aggregator),
800- m_contact(0)
801+ m_contact(0),
802+ m_currentUpdate(0)
803 {
804- g_object_ref(m_individual);
805- updateContact();
806+ setIndividual(individual);
807 }
808
809 void QIndividual::notifyUpdate()
810@@ -139,20 +139,20 @@
811
812 QIndividual::~QIndividual()
813 {
814- if (m_contact) {
815- delete m_contact;
816+ if (m_currentUpdate) {
817+ m_currentUpdate->disconnect(m_updateConnection);
818+ // this will leave the update object to destroy itself
819+ // this is necessary because the individual can be destroyed during a update
820+ // Eg. If the individual get linked
821+ m_currentUpdate->deatach();
822+ m_currentUpdate = 0;
823 }
824- Q_ASSERT(m_individual);
825- g_object_unref(m_individual);
826+ clear();
827 }
828
829 QString QIndividual::id() const
830 {
831- if (m_individual) {
832- return QString::fromUtf8(folks_individual_get_id(m_individual));
833- } else {
834- return "";
835- }
836+ return m_id;
837 }
838
839 QtContacts::QContactDetail QIndividual::getUid() const
840@@ -167,25 +167,17 @@
841
842 QList<QtContacts::QContactDetail> QIndividual::getClientPidMap() const
843 {
844- QList<QtContacts::QContactDetail> details;
845 int index = 1;
846- GeeSet *personas = folks_individual_get_personas(m_individual);
847- if (!personas) {
848- return details;
849- }
850- GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas));
851+ QList<QContactDetail> details;
852
853- while(gee_iterator_next(iter)) {
854+ Q_FOREACH(const QString id, m_personas.keys()) {
855 QContactExtendedDetail detail;
856- FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter));
857+
858 detail.setName("CLIENTPIDMAP");
859 detail.setValue(QContactExtendedDetail::FieldData, index++);
860- detail.setValue(QContactExtendedDetail::FieldData + 1, QString::fromUtf8(folks_persona_get_uid(persona)));
861+ detail.setValue(QContactExtendedDetail::FieldData + 1, id);
862 details << detail;
863- g_object_unref(persona);
864 }
865-
866- g_object_unref(iter);
867 return details;
868 }
869
870@@ -360,9 +352,13 @@
871 g_free(uri);
872 g_object_unref(cache);
873 }
874+ // Avoid to set a empty url
875+ if (url.isEmpty()) {
876+ return avatar;
877+ }
878 avatar.setImageUrl(QUrl(url));
879+ avatar.setDetailUri(QString("%1.1").arg(index));
880 }
881- avatar.setDetailUri(QString("%1.1").arg(index));
882 return avatar;
883 }
884
885@@ -744,40 +740,64 @@
886
887 QtContacts::QContact &QIndividual::contact()
888 {
889- if (!m_contact) {
890+ if (!m_contact && m_individual) {
891+ updatePersonas();
892 updateContact();
893 }
894 return *m_contact;
895 }
896
897+void QIndividual::updatePersonas()
898+{
899+ Q_FOREACH(FolksPersona *p, m_personas.values()) {
900+ g_object_unref(p);
901+ }
902+
903+ GeeSet *personas = folks_individual_get_personas(m_individual);
904+ if (!personas) {
905+ return;
906+ }
907+
908+ GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas));
909+ while(gee_iterator_next(iter)) {
910+ FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter));
911+ g_signal_connect(G_OBJECT(persona), "notify::avatar",
912+ (GCallback) QIndividual::folksPersonaChanged,
913+ const_cast<QIndividual*>(this));
914+
915+ m_personas.insert(QString::fromUtf8(folks_persona_get_iid(persona)), persona);
916+ }
917+
918+ g_object_unref(iter);
919+}
920+
921 void QIndividual::updateContact()
922 {
923- Q_ASSERT(m_individual);
924+ // disconnect any previous handler
925+ Q_FOREACH(FolksPersona *p, m_notifyConnections.keys()) {
926+ Q_FOREACH(int handlerId, m_notifyConnections.value(p)) {
927+ g_signal_handler_disconnect(p, handlerId);
928+ }
929+ m_notifyConnections.remove(p);
930+ }
931+
932+ if (m_contact) {
933+ delete m_contact;
934+ m_contact = 0;
935+ }
936
937 m_contact = new QContact();
938+ if (!m_individual) {
939+ return;
940+ }
941+
942 m_contact->appendDetail(getUid());
943 Q_FOREACH(QContactDetail detail, getClientPidMap()) {
944 m_contact->appendDetail(detail);
945 }
946
947 int personaIndex = 1;
948- GeeSet *personas = folks_individual_get_personas(m_individual);
949- Q_ASSERT(personas);
950- GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas));
951-
952- FolksPersona *persona;
953-
954- // disconnect any previous handler
955- Q_FOREACH(gpointer persona, m_notifyConnections.keys()) {
956- Q_FOREACH(int handlerId, m_notifyConnections[persona]) {
957- g_signal_handler_disconnect(persona, handlerId);
958- }
959- }
960-
961- m_notifyConnections.clear();
962-
963- while(gee_iterator_next(iter)) {
964- persona = FOLKS_PERSONA(gee_iterator_get(iter));
965+ Q_FOREACH(FolksPersona *persona, m_personas.values()) {
966 Q_ASSERT(FOLKS_IS_PERSONA(persona));
967
968 int wsize = 0;
969@@ -790,25 +810,27 @@
970 wPropList << wproperties[i];
971 }
972
973- appendDetailsForPersona(m_contact,
974- getPersonaName(persona, personaIndex),
975- !wPropList.contains("structured-name"));
976- appendDetailsForPersona(m_contact,
977- getPersonaFullName(persona, personaIndex),
978- !wPropList.contains("full-name"));
979- appendDetailsForPersona(m_contact,
980- getPersonaNickName(persona, personaIndex),
981- !wPropList.contains("structured-name"));
982- appendDetailsForPersona(m_contact,
983- getPersonaBirthday(persona, personaIndex),
984- !wPropList.contains("birthday"));
985- appendDetailsForPersona(m_contact,
986- getPersonaPhoto(persona, personaIndex),
987- !wPropList.contains("avatar"));
988- appendDetailsForPersona(m_contact,
989- getPersonaFavorite(persona, personaIndex),
990- !wPropList.contains("is-favourite"));
991-
992+ // vcard only support one of these details by contact
993+ if (personaIndex == 1) {
994+ appendDetailsForPersona(m_contact,
995+ getPersonaName(persona, personaIndex),
996+ !wPropList.contains("structured-name"));
997+ appendDetailsForPersona(m_contact,
998+ getPersonaFullName(persona, personaIndex),
999+ !wPropList.contains("full-name"));
1000+ appendDetailsForPersona(m_contact,
1001+ getPersonaNickName(persona, personaIndex),
1002+ !wPropList.contains("structured-name"));
1003+ appendDetailsForPersona(m_contact,
1004+ getPersonaBirthday(persona, personaIndex),
1005+ !wPropList.contains("birthday"));
1006+ appendDetailsForPersona(m_contact,
1007+ getPersonaPhoto(persona, personaIndex),
1008+ !wPropList.contains("avatar"));
1009+ appendDetailsForPersona(m_contact,
1010+ getPersonaFavorite(persona, personaIndex),
1011+ !wPropList.contains("is-favourite"));
1012+ }
1013
1014 QList<QContactDetail> details;
1015 QContactDetail prefDetail;
1016@@ -855,33 +877,34 @@
1017 !wPropList.contains("urls"));
1018 personaIndex++;
1019
1020+ QList<int> ids = m_notifyConnections.value(persona);
1021+
1022 // for now we are getting updated only about avatar changes
1023- int handlerId = g_signal_connect(G_OBJECT(persona), "notify::avatar",
1024- (GCallback) QIndividual::folksPersonaChanged,
1025- const_cast<QIndividual*>(this));
1026- QList<int> ids = m_notifyConnections[persona];
1027- ids << handlerId;
1028+ ids << g_signal_connect(G_OBJECT(persona), "notify::avatar",
1029+ (GCallback) QIndividual::folksPersonaChanged,
1030+ const_cast<QIndividual*>(this));
1031 m_notifyConnections[persona] = ids;
1032-
1033- g_object_unref(persona);
1034 }
1035-
1036- g_object_unref(iter);
1037 }
1038
1039 bool QIndividual::update(const QtContacts::QContact &newContact, QObject *object, const char *slot)
1040 {
1041 QContact &originalContact = contact();
1042 if (newContact != originalContact) {
1043- UpdateContactRequest *request = new UpdateContactRequest(newContact, this, object, slot);
1044+ // only suppport one update by time
1045+ Q_ASSERT(m_currentUpdate == 0);
1046+ m_currentUpdate = new UpdateContactRequest(newContact, this, object, slot);
1047+ m_updateConnection = QObject::connect(m_currentUpdate,
1048+ &UpdateContactRequest::done,
1049+ [this] (const QString &errorMessage) {
1050
1051- QObject::connect(request, &UpdateContactRequest::done, [request, this] (const QString &errorMessage) {
1052 if (errorMessage.isEmpty()) {
1053 this->updateContact();
1054 }
1055- request->deleteLater();
1056+ m_currentUpdate->deleteLater();
1057+ m_currentUpdate = 0;
1058 });
1059- request->start();
1060+ m_currentUpdate->start();
1061 return true;
1062 } else {
1063 qDebug() << "Contact is equal";
1064@@ -895,31 +918,40 @@
1065 return update(contact, object, slot);
1066 }
1067
1068-
1069 FolksIndividual *QIndividual::individual() const
1070 {
1071 return m_individual;
1072 }
1073
1074-FolksPersona *QIndividual::getPersona(int index) const
1075-{
1076- int size = 0;
1077- FolksPersona *persona = 0;
1078- GeeSet *personas = folks_individual_get_personas(m_individual);
1079- gpointer* values = gee_collection_to_array(GEE_COLLECTION(personas), &size);
1080-
1081- if ((index > 0) && (index <= size)) {
1082- persona = FOLKS_PERSONA(values[index - 1]);
1083- }
1084-
1085- g_free(values);
1086- return persona;
1087-}
1088-
1089-int QIndividual::personaCount() const
1090-{
1091- GeeSet *personas = folks_individual_get_personas(m_individual);
1092- return gee_collection_get_size(GEE_COLLECTION(personas));
1093+QList<FolksPersona *> QIndividual::personas() const
1094+{
1095+ return m_personas.values();
1096+}
1097+
1098+void QIndividual::clearPersonas()
1099+{
1100+ Q_FOREACH(FolksPersona *p, m_personas.values()) {
1101+ Q_FOREACH(int handlerId, m_notifyConnections.value(p)) {
1102+ g_signal_handler_disconnect(p, handlerId);
1103+ }
1104+ m_notifyConnections.remove(p);
1105+ g_object_unref(p);
1106+ }
1107+ m_personas.clear();
1108+}
1109+
1110+void QIndividual::clear()
1111+{
1112+ clearPersonas();
1113+ if (m_individual) {
1114+ g_object_unref(m_individual);
1115+ m_individual = 0;
1116+ }
1117+
1118+ if (m_contact) {
1119+ delete m_contact;
1120+ m_contact = 0;
1121+ }
1122 }
1123
1124 void QIndividual::addListener(QObject *object, const char *slot)
1125@@ -932,27 +964,45 @@
1126 }
1127 }
1128
1129+bool QIndividual::isValid() const
1130+{
1131+ return (m_individual != 0);
1132+}
1133+
1134+void QIndividual::flush()
1135+{
1136+ // flush the folks persona store
1137+ folks_persona_store_flush(folks_individual_aggregator_get_primary_store(m_aggregator), 0, 0);
1138+
1139+ // cause the contact info to be reload
1140+ if (m_contact) {
1141+ delete m_contact;
1142+ m_contact = 0;
1143+ }
1144+}
1145+
1146 void QIndividual::setIndividual(FolksIndividual *individual)
1147 {
1148 if (m_individual != individual) {
1149- if (m_individual) {
1150- g_object_unref(m_individual);
1151+ clear();
1152+
1153+ if (individual) {
1154+ QString newId = QString::fromUtf8(folks_individual_get_id(individual));
1155+ if (!m_id.isEmpty()) {
1156+ // we can only update to individual with the same id
1157+ Q_ASSERT(newId == m_id);
1158+ } else {
1159+ m_id = newId;
1160+ }
1161 }
1162+
1163 m_individual = individual;
1164 if (m_individual) {
1165 g_object_ref(m_individual);
1166 }
1167-
1168- // initialize qcontact
1169- if (m_contact) {
1170- delete m_contact;
1171- m_contact = 0;
1172- }
1173- updateContact();
1174 }
1175 }
1176
1177-
1178 GHashTable *QIndividual::parseAddressDetails(GHashTable *details,
1179 const QList<QtContacts::QContactDetail> &cDetails,
1180 const QtContacts::QContactDetail &prefDetail)
1181@@ -982,13 +1032,15 @@
1182 g_value_take_object(value, collection);
1183 }
1184
1185- FolksPostalAddressFieldDetails *pafd = folks_postal_address_field_details_new(postalAddress, NULL);
1186- DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(pafd),
1187- address,
1188- detail == prefDetail);
1189- gee_collection_add(collection, pafd);
1190+ if (!folks_postal_address_is_empty(postalAddress)) {
1191+ FolksPostalAddressFieldDetails *pafd = folks_postal_address_field_details_new(postalAddress, NULL);
1192+ DetailContextParser::parseContext(FOLKS_ABSTRACT_FIELD_DETAILS(pafd),
1193+ address,
1194+ detail == prefDetail);
1195+ gee_collection_add(collection, pafd);
1196+ g_object_unref(pafd);
1197+ }
1198
1199- g_object_unref(pafd);
1200 g_object_unref(postalAddress);
1201 }
1202 GeeUtils::personaDetailsInsert(details, FOLKS_PERSONA_DETAIL_POSTAL_ADDRESSES, value);
1203@@ -1293,7 +1345,6 @@
1204 return details;
1205 }
1206
1207-
1208 GHashTable *QIndividual::parseDetails(const QtContacts::QContact &contact)
1209 {
1210 GHashTable *details = g_hash_table_new_full(g_str_hash,
1211@@ -1309,7 +1360,6 @@
1212 parseFullNameDetails(details, contact.details(QContactDisplayLabel::Type));
1213 parseNicknameDetails(details, contact.details(QContactNickname::Type));
1214
1215-
1216 parseAddressDetails(details,
1217 contact.details(QContactAddress::Type),
1218 contact.preferredDetail(VCardParser::PreferredActionNames[QContactAddress::Type]));
1219@@ -1335,29 +1385,23 @@
1220 return details;
1221 }
1222
1223+void QIndividual::enableAutoLink(bool flag)
1224+{
1225+ m_autoLink = flag;
1226+}
1227+
1228+bool QIndividual::autoLinkEnabled()
1229+{
1230+ return m_autoLink;
1231+}
1232+
1233 FolksPersona* QIndividual::primaryPersona()
1234 {
1235- Q_ASSERT(m_individual);
1236-
1237- if (m_primaryPersona) {
1238- return m_primaryPersona;
1239- }
1240-
1241- GeeSet *personas = folks_individual_get_personas(m_individual);
1242- GeeIterator *iter = gee_iterable_iterator(GEE_ITERABLE(personas));
1243- FolksPersonaStore *primaryStore = folks_individual_aggregator_get_primary_store(m_aggregator);
1244-
1245- while(m_primaryPersona == NULL && gee_iterator_next(iter)) {
1246- FolksPersona *persona = FOLKS_PERSONA(gee_iterator_get(iter));
1247- if(folks_persona_get_store(persona) == primaryStore) {
1248- m_primaryPersona = persona;
1249- g_object_ref (persona);
1250- }
1251- g_object_unref(persona);
1252- }
1253- g_object_unref(iter);
1254-
1255- return m_primaryPersona;
1256+ if (m_personas.size() > 0) {
1257+ return m_personas.begin().value();
1258+ } else {
1259+ return 0;
1260+ }
1261 }
1262
1263 QtContacts::QContactDetail QIndividual::detailFromUri(QtContacts::QContactDetail::DetailType type, const QString &uri) const
1264
1265=== modified file 'lib/qindividual.h'
1266--- lib/qindividual.h 2013-10-30 19:07:49 +0000
1267+++ lib/qindividual.h 2014-02-18 00:13:04 +0000
1268@@ -33,6 +33,8 @@
1269 {
1270 typedef GHashTable* (*ParseDetailsFunc)(GHashTable*, const QList<QtContacts::QContactDetail> &);
1271
1272+class UpdateContactRequest;
1273+
1274 class QIndividual
1275 {
1276 public:
1277@@ -46,20 +48,28 @@
1278 bool update(const QtContacts::QContact &contact, QObject *object, const char *slot);
1279 void setIndividual(FolksIndividual *individual);
1280 FolksIndividual *individual() const;
1281-
1282- FolksPersona *getPersona(int index) const;
1283- int personaCount() const;
1284+ QList<FolksPersona*> personas() const;
1285 void addListener(QObject *object, const char *slot);
1286+ bool isValid() const;
1287+ void flush();
1288
1289 static GHashTable *parseDetails(const QtContacts::QContact &contact);
1290+
1291+ // enable or disable auto-link
1292+ static void enableAutoLink(bool flag);
1293+ static bool autoLinkEnabled();
1294+
1295 private:
1296 FolksIndividual *m_individual;
1297- FolksPersona *m_primaryPersona;
1298 FolksIndividualAggregator *m_aggregator;
1299 QtContacts::QContact *m_contact;
1300- QMap<QString, QPair<QtContacts::QContactDetail, FolksAbstractFieldDetails*> > m_fieldsMap;
1301+ UpdateContactRequest *m_currentUpdate;
1302 QList<QPair<QObject*, QMetaMethod> > m_listeners;
1303- QMap<gpointer, QList<int> > m_notifyConnections;
1304+ QMap<QString, FolksPersona*> m_personas;
1305+ QMap<FolksPersona*, QList<int> > m_notifyConnections;
1306+ QString m_id;
1307+ QMetaObject::Connection m_updateConnection;
1308+ static bool m_autoLink;
1309
1310 QIndividual();
1311 QIndividual(const QIndividual &);
1312@@ -68,6 +78,9 @@
1313
1314 QMultiHash<QString, QString> parseDetails(FolksAbstractFieldDetails *details) const;
1315 void updateContact();
1316+ void updatePersonas();
1317+ void clearPersonas();
1318+ void clear();
1319
1320 FolksPersona *primaryPersona();
1321 QtContacts::QContactDetail detailFromUri(QtContacts::QContactDetail::DetailType type, const QString &uri) const;
1322
1323=== modified file 'lib/update-contact-request.cpp'
1324--- lib/update-contact-request.cpp 2013-10-30 19:07:49 +0000
1325+++ lib/update-contact-request.cpp 2014-02-18 00:13:04 +0000
1326@@ -27,19 +27,16 @@
1327
1328 using namespace QtContacts;
1329
1330-namespace {
1331-
1332-
1333-
1334-}
1335-
1336 namespace galera {
1337
1338 UpdateContactRequest::UpdateContactRequest(QtContacts::QContact newContact, QIndividual *parent, QObject *listener, const char *slot)
1339 : QObject(),
1340+ m_parent(parent),
1341+ m_object(listener),
1342+ m_currentPersona(0),
1343+ m_eventLoop(0),
1344 m_newContact(newContact),
1345- m_parent(parent),
1346- m_object(listener)
1347+ m_currentPersonaIndex(0)
1348 {
1349 int slotIndex = listener->metaObject()->indexOfSlot(++slot);
1350 if (slotIndex == -1) {
1351@@ -49,18 +46,67 @@
1352 }
1353 }
1354
1355+UpdateContactRequest::~UpdateContactRequest()
1356+{
1357+ // check if there is a operation running
1358+ if (m_currentPersona != 0) {
1359+ wait();
1360+ }
1361+}
1362+
1363 void UpdateContactRequest::invokeSlot(const QString &errorMessage)
1364 {
1365- if (m_slot.isValid()) {
1366- m_slot.invoke(m_object, Q_ARG(galera::QIndividual*, m_parent), Q_ARG(QString, errorMessage));
1367- }
1368+ if (m_slot.isValid() && m_parent) {
1369+ m_slot.invoke(m_object, Q_ARG(QString, m_parent->id()),
1370+ Q_ARG(QString, errorMessage));
1371+ } else if (m_parent == 0) {
1372+ // the object was detached we need to destroy it
1373+ deleteLater();
1374+ }
1375+
1376+ if (m_eventLoop) {
1377+ m_eventLoop->quit();
1378+ }
1379+
1380 Q_EMIT done(errorMessage);
1381 }
1382
1383 void UpdateContactRequest::start()
1384 {
1385 m_currentDetailType = QContactDetail::TypeAddress;
1386- updatePersona(1);
1387+ m_personas = m_parent->personas();
1388+ m_originalContact = m_parent->contact();
1389+ m_currentPersonaIndex = 0;
1390+ updatePersona();
1391+}
1392+
1393+void UpdateContactRequest::wait()
1394+{
1395+ Q_ASSERT(m_eventLoop == 0);
1396+ QEventLoop eventLoop;
1397+ m_eventLoop = &eventLoop;
1398+ eventLoop.exec();
1399+ m_eventLoop = 0;
1400+}
1401+
1402+void UpdateContactRequest::deatach()
1403+{
1404+ m_parent = 0;
1405+}
1406+
1407+bool UpdateContactRequest::isEqual(const QtContacts::QContactDetail &detailA,
1408+ const QtContacts::QContactDetail &detailB)
1409+{
1410+ if (detailA.type() != detailB.type()) {
1411+ return false;
1412+ }
1413+
1414+ switch(detailA.type()) {
1415+ case QContactDetail::TypeFavorite:
1416+ return detailA.value(QContactFavorite::FieldFavorite) == detailB.value(QContactFavorite::FieldFavorite);
1417+ default:
1418+ return (detailA == detailB);
1419+ }
1420 }
1421
1422 bool UpdateContactRequest::isEqual(QList<QtContacts::QContactDetail> listA,
1423@@ -71,7 +117,7 @@
1424 }
1425
1426 for(int i=0; i < listA.size(); i++) {
1427- if (listA[i] != listB[i]) {
1428+ if (!isEqual(listA[i], listB[i])) {
1429 return false;
1430 }
1431 }
1432@@ -116,7 +162,8 @@
1433 }
1434
1435 Q_FOREACH(QContactDetail det, contact.details(type)) {
1436- if ((includeEmptyPersona && det.detailUri().isEmpty()) || checkPersona(det, persona)) {
1437+ if ((includeEmptyPersona && det.detailUri().isEmpty()) ||
1438+ checkPersona(det, persona)) {
1439 if (!det.isEmpty()) {
1440 personaDetails << det;
1441 if (pref && det == globalPref) {
1442@@ -132,19 +179,19 @@
1443 int persona,
1444 QtContacts::QContactDetail *pref) const
1445 {
1446- return detailsFromPersona(m_parent->contact(), type, persona, false, pref);
1447+ return detailsFromPersona(m_originalContact, type, persona, false, pref);
1448 }
1449
1450 QList<QtContacts::QContactDetail> UpdateContactRequest::detailsFromPersona(QtContacts::QContactDetail::DetailType type,
1451 int persona,
1452 QtContacts::QContactDetail *pref) const
1453 {
1454- return detailsFromPersona(m_newContact, type, persona, true, pref);
1455+ // only return new details for the first persona, this will avoid to create the details for all personas
1456+ return detailsFromPersona(m_newContact, type, persona, (persona==1), pref);
1457 }
1458
1459 void UpdateContactRequest::updateAddress()
1460 {
1461- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1462 QContactDetail originalPref;
1463 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeAddress,
1464 m_currentPersonaIndex,
1465@@ -154,8 +201,8 @@
1466 m_currentPersonaIndex,
1467 &prefDetail);
1468
1469- if (persona &&
1470- FOLKS_IS_POSTAL_ADDRESS_DETAILS(persona) &&
1471+ if (m_currentPersona &&
1472+ FOLKS_IS_POSTAL_ADDRESS_DETAILS(m_currentPersona) &&
1473 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1474 qDebug() << "Adderess diff";
1475 GeeSet *newSet = SET_AFD_NEW();
1476@@ -188,7 +235,7 @@
1477 g_object_unref(pa);
1478 }
1479
1480- folks_postal_address_details_change_postal_addresses(FOLKS_POSTAL_ADDRESS_DETAILS(persona),
1481+ folks_postal_address_details_change_postal_addresses(FOLKS_POSTAL_ADDRESS_DETAILS(m_currentPersona),
1482 newSet,
1483 (GAsyncReadyCallback) updateDetailsDone,
1484 this);
1485@@ -200,12 +247,15 @@
1486
1487 void UpdateContactRequest::updateAvatar()
1488 {
1489- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1490 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeAvatar, m_currentPersonaIndex, 0);
1491 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeAvatar, m_currentPersonaIndex, 0);
1492
1493- if (persona && FOLKS_IS_AVATAR_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1494- qDebug() << "avatar diff";
1495+ if (m_currentPersona &&
1496+ FOLKS_IS_AVATAR_DETAILS(m_currentPersona) &&
1497+ !isEqual(originalDetails, newDetails)) {
1498+ qDebug() << "avatar diff:"
1499+ << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n"
1500+ << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail());
1501 //Only supports one avatar
1502 QUrl avatarUri;
1503 if (newDetails.count()) {
1504@@ -225,7 +275,7 @@
1505 }
1506 }
1507
1508- folks_avatar_details_change_avatar(FOLKS_AVATAR_DETAILS(persona),
1509+ folks_avatar_details_change_avatar(FOLKS_AVATAR_DETAILS(m_currentPersona),
1510 G_LOADABLE_ICON(avatarFileIcon),
1511 (GAsyncReadyCallback) updateDetailsDone,
1512 this);
1513@@ -239,11 +289,12 @@
1514
1515 void UpdateContactRequest::updateBirthday()
1516 {
1517- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1518 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeBirthday, m_currentPersonaIndex, 0);
1519 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeBirthday, m_currentPersonaIndex, 0);
1520
1521- if (persona && FOLKS_IS_BIRTHDAY_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1522+ if (m_currentPersona &&
1523+ FOLKS_IS_BIRTHDAY_DETAILS(m_currentPersona) &&
1524+ !isEqual(originalDetails, newDetails)) {
1525 qDebug() << "birthday diff";
1526 //Only supports one birthday
1527 QDateTime dateTimeBirthday;
1528@@ -258,7 +309,7 @@
1529 if (dateTimeBirthday.isValid()) {
1530 dateTime = g_date_time_new_from_unix_utc(dateTimeBirthday.toMSecsSinceEpoch() / 1000);
1531 }
1532- folks_birthday_details_change_birthday(FOLKS_BIRTHDAY_DETAILS(persona),
1533+ folks_birthday_details_change_birthday(FOLKS_BIRTHDAY_DETAILS(m_currentPersona),
1534 dateTime,
1535 (GAsyncReadyCallback) updateDetailsDone,
1536 this);
1537@@ -272,12 +323,15 @@
1538
1539 void UpdateContactRequest::updateFullName()
1540 {
1541- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1542 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeDisplayLabel, m_currentPersonaIndex, 0);
1543 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeDisplayLabel, m_currentPersonaIndex, 0);
1544
1545- if (persona && FOLKS_IS_NAME_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1546- qDebug() << "Full Name diff";
1547+ if (m_currentPersona &&
1548+ FOLKS_IS_NAME_DETAILS(m_currentPersona) &&
1549+ !isEqual(originalDetails, newDetails)) {
1550+ qDebug() << "Full Name diff:"
1551+ << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n"
1552+ << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail());
1553 //Only supports one fullName
1554 QString fullName;
1555 if (newDetails.count()) {
1556@@ -286,7 +340,7 @@
1557 }
1558
1559 QByteArray fullNameUtf8 = fullName.toUtf8();
1560- folks_name_details_change_full_name(FOLKS_NAME_DETAILS(persona),
1561+ folks_name_details_change_full_name(FOLKS_NAME_DETAILS(m_currentPersona),
1562 fullNameUtf8.constData(),
1563 (GAsyncReadyCallback) updateDetailsDone,
1564 this);
1565@@ -297,7 +351,6 @@
1566
1567 void UpdateContactRequest::updateEmail()
1568 {
1569- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1570 QContactDetail originalPref;
1571 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeEmailAddress,
1572 m_currentPersonaIndex,
1573@@ -308,8 +361,8 @@
1574 m_currentPersonaIndex,
1575 &prefDetail);
1576
1577- if (persona &&
1578- FOLKS_IS_EMAIL_DETAILS(persona) &&
1579+ if (m_currentPersona &&
1580+ FOLKS_IS_EMAIL_DETAILS(m_currentPersona) &&
1581 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1582 qDebug() << "email diff";
1583 GeeSet *newSet = SET_AFD_NEW();
1584@@ -325,7 +378,7 @@
1585 g_object_unref(field);
1586 }
1587
1588- folks_email_details_change_email_addresses(FOLKS_EMAIL_DETAILS(persona),
1589+ folks_email_details_change_email_addresses(FOLKS_EMAIL_DETAILS(m_currentPersona),
1590 newSet,
1591 (GAsyncReadyCallback) updateDetailsDone,
1592 this);
1593@@ -337,11 +390,12 @@
1594
1595 void UpdateContactRequest::updateName()
1596 {
1597- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1598 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeName, m_currentPersonaIndex, 0);
1599 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeName, m_currentPersonaIndex, 0);
1600
1601- if (persona && FOLKS_IS_NAME_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1602+ if (m_currentPersona &&
1603+ FOLKS_IS_NAME_DETAILS(m_currentPersona) &&
1604+ !isEqual(originalDetails, newDetails)) {
1605 qDebug() << "Name diff";
1606 //Only supports one fullName
1607 FolksStructuredName *sn = 0;
1608@@ -360,7 +414,7 @@
1609 suffix.constData());
1610 }
1611
1612- folks_name_details_change_structured_name(FOLKS_NAME_DETAILS(persona),
1613+ folks_name_details_change_structured_name(FOLKS_NAME_DETAILS(m_currentPersona),
1614 sn,
1615 (GAsyncReadyCallback) updateDetailsDone,
1616 this);
1617@@ -374,11 +428,12 @@
1618
1619 void UpdateContactRequest::updateNickname()
1620 {
1621- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1622 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeNickname, m_currentPersonaIndex, 0);
1623 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeNickname, m_currentPersonaIndex, 0);
1624
1625- if (persona && FOLKS_IS_NAME_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1626+ if (m_currentPersona &&
1627+ FOLKS_IS_NAME_DETAILS(m_currentPersona) &&
1628+ !isEqual(originalDetails, newDetails)) {
1629 qDebug() << "Nickname diff";
1630 //Only supports one fullName
1631 QString nicknameValue;
1632@@ -388,7 +443,7 @@
1633 }
1634
1635 QByteArray nicknameValueUtf8 = nicknameValue.toUtf8();
1636- folks_name_details_change_nickname(FOLKS_NAME_DETAILS(persona),
1637+ folks_name_details_change_nickname(FOLKS_NAME_DETAILS(m_currentPersona),
1638 nicknameValueUtf8.constData(),
1639 (GAsyncReadyCallback) updateDetailsDone,
1640 this);
1641@@ -399,14 +454,13 @@
1642
1643 void UpdateContactRequest::updateNote()
1644 {
1645- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1646 QContactDetail originalPref;
1647 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeNote, m_currentPersonaIndex, &originalPref);
1648 QContactDetail prefDetail;
1649 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeNote, m_currentPersonaIndex, &prefDetail);
1650
1651- if (persona &&
1652- FOLKS_IS_EMAIL_DETAILS(persona) &&
1653+ if (m_currentPersona &&
1654+ FOLKS_IS_EMAIL_DETAILS(m_currentPersona) &&
1655 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1656 qDebug() << "notes diff";
1657 GeeSet *newSet = SET_AFD_NEW();
1658@@ -422,7 +476,7 @@
1659 g_object_unref(field);
1660 }
1661
1662- folks_note_details_change_notes(FOLKS_NOTE_DETAILS(persona),
1663+ folks_note_details_change_notes(FOLKS_NOTE_DETAILS(m_currentPersona),
1664 newSet,
1665 (GAsyncReadyCallback) updateDetailsDone,
1666 this);
1667@@ -434,7 +488,6 @@
1668
1669 void UpdateContactRequest::updateOnlineAccount()
1670 {
1671- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1672 QContactDetail originalPref;
1673 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeOnlineAccount,
1674 m_currentPersonaIndex,
1675@@ -444,8 +497,8 @@
1676 m_currentPersonaIndex,
1677 &prefDetail);
1678
1679- if (persona &&
1680- FOLKS_IS_IM_DETAILS(persona) &&
1681+ if (m_currentPersona &&
1682+ FOLKS_IS_IM_DETAILS(m_currentPersona) &&
1683 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1684 qDebug() << "OnlineAccounts diff";
1685 GeeMultiMap *imMap = GEE_MULTI_MAP_AFD_NEW(FOLKS_TYPE_IM_FIELD_DETAILS);
1686@@ -467,7 +520,7 @@
1687 }
1688 }
1689
1690- folks_im_details_change_im_addresses(FOLKS_IM_DETAILS(persona),
1691+ folks_im_details_change_im_addresses(FOLKS_IM_DETAILS(m_currentPersona),
1692 imMap,
1693 (GAsyncReadyCallback) updateDetailsDone,
1694 this);
1695@@ -480,7 +533,6 @@
1696
1697 void UpdateContactRequest::updateOrganization()
1698 {
1699- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1700 QContactDetail originalPref;
1701 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeOrganization,
1702 m_currentPersonaIndex,
1703@@ -490,8 +542,8 @@
1704 m_currentPersonaIndex,
1705 &prefDetail);
1706
1707- if (persona &&
1708- FOLKS_IS_ROLE_DETAILS(persona) &&
1709+ if (m_currentPersona &&
1710+ FOLKS_IS_ROLE_DETAILS(m_currentPersona) &&
1711 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1712 qDebug() << "Organization diff";
1713 GeeSet *newSet = SET_AFD_NEW();
1714@@ -515,7 +567,7 @@
1715 g_object_unref(roleValue);
1716 }
1717
1718- folks_role_details_change_roles(FOLKS_ROLE_DETAILS(persona),
1719+ folks_role_details_change_roles(FOLKS_ROLE_DETAILS(m_currentPersona),
1720 newSet,
1721 (GAsyncReadyCallback) updateDetailsDone,
1722 this);
1723@@ -528,7 +580,6 @@
1724
1725 void UpdateContactRequest::updatePhone()
1726 {
1727- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1728 QContactDetail originalPref;
1729 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypePhoneNumber,
1730 m_currentPersonaIndex,
1731@@ -538,10 +589,13 @@
1732 m_currentPersonaIndex,
1733 &prefDetail);
1734
1735- if (persona &&
1736- FOLKS_IS_PHONE_DETAILS(persona) &&
1737+ if (m_currentPersona &&
1738+ FOLKS_IS_PHONE_DETAILS(m_currentPersona) &&
1739 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1740- qDebug() << "Phone diff";
1741+ qDebug() << "Phone diff:"
1742+ << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n"
1743+ << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail());
1744+
1745 GeeSet *newSet = SET_AFD_NEW();
1746
1747 Q_FOREACH(QContactDetail newDetail, newDetails) {
1748@@ -555,7 +609,7 @@
1749 g_object_unref(field);
1750 }
1751
1752- folks_phone_details_change_phone_numbers(FOLKS_PHONE_DETAILS(persona),
1753+ folks_phone_details_change_phone_numbers(FOLKS_PHONE_DETAILS(m_currentPersona),
1754 newSet,
1755 (GAsyncReadyCallback) updateDetailsDone,
1756 this);
1757@@ -567,7 +621,6 @@
1758
1759 void UpdateContactRequest::updateUrl()
1760 {
1761- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1762 QContactDetail originalPref;
1763 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeUrl,
1764 m_currentPersonaIndex,
1765@@ -578,8 +631,8 @@
1766 m_currentPersonaIndex,
1767 &prefDetail);
1768
1769- if (persona &&
1770- FOLKS_IS_PHONE_DETAILS(persona) &&
1771+ if (m_currentPersona &&
1772+ FOLKS_IS_URL_DETAILS(m_currentPersona) &&
1773 !isEqual(originalDetails, originalPref, newDetails, prefDetail)) {
1774 qDebug() << "Url diff";
1775 GeeSet *newSet = SET_AFD_NEW();
1776@@ -595,7 +648,7 @@
1777 g_object_unref(field);
1778 }
1779
1780- folks_url_details_change_urls(FOLKS_URL_DETAILS(persona),
1781+ folks_url_details_change_urls(FOLKS_URL_DETAILS(m_currentPersona),
1782 newSet,
1783 (GAsyncReadyCallback) updateDetailsDone,
1784 this);
1785@@ -607,19 +660,23 @@
1786
1787 void UpdateContactRequest::updateFavorite()
1788 {
1789- FolksPersona *persona = m_parent->getPersona(m_currentPersonaIndex);
1790 QList<QContactDetail> originalDetails = originalDetailsFromPersona(QContactDetail::TypeFavorite, m_currentPersonaIndex, 0);
1791 QList<QContactDetail> newDetails = detailsFromPersona(QContactDetail::TypeFavorite, m_currentPersonaIndex, 0);
1792
1793- if (persona && FOLKS_IS_FAVOURITE_DETAILS(persona) && !isEqual(originalDetails, newDetails)) {
1794- qDebug() << "Favorite diff";
1795+ if (m_currentPersona &&
1796+ FOLKS_IS_FAVOURITE_DETAILS(m_currentPersona) &&
1797+ !isEqual(originalDetails, newDetails)) {
1798+ qDebug() << "Favorite diff:"
1799+ << "\n\t" << originalDetails.size() << (originalDetails.size() > 0 ? originalDetails[0] : QContactDetail()) << "\n"
1800+ << "\n\t" << newDetails.size() << (newDetails.size() > 0 ? newDetails[0] : QContactDetail());
1801+
1802 //Only supports one fullName
1803 bool isFavorite = false;
1804 if (newDetails.count()) {
1805 QContactFavorite favorite = static_cast<QContactFavorite>(newDetails[0]);
1806 isFavorite = favorite.isFavorite();
1807 }
1808- folks_favourite_details_change_is_favourite(FOLKS_FAVOURITE_DETAILS(persona),
1809+ folks_favourite_details_change_is_favourite(FOLKS_FAVOURITE_DETAILS(m_currentPersona),
1810 isFavorite,
1811 (GAsyncReadyCallback) updateDetailsDone,
1812 this);
1813@@ -628,14 +685,44 @@
1814 }
1815 }
1816
1817-void UpdateContactRequest::updatePersona(int index)
1818+void UpdateContactRequest::updatePersona()
1819 {
1820- if (index > m_parent->personaCount()) {
1821+ if (m_personas.size() <= m_currentPersonaIndex) {
1822+ m_currentPersona = 0;
1823+ if (m_parent) {
1824+ m_parent->flush();
1825+ }
1826 invokeSlot();
1827 } else {
1828- m_currentPersonaIndex = index;
1829+ m_currentPersona = m_personas[m_currentPersonaIndex];
1830 m_currentDetailType = QContactDetail::TypeUndefined;
1831- updateDetailsDone(0, 0, this);
1832+ m_currentPersonaIndex++;
1833+
1834+ if (QIndividual::autoLinkEnabled()) {
1835+ updateDetailsDone(0, 0, this);
1836+ } else {
1837+ // all personas edited by the user will have the auto link disabled
1838+ GeeSet *antiLinks;
1839+ antiLinks = GEE_SET(gee_hash_set_new(G_TYPE_STRING,
1840+ (GBoxedCopyFunc) g_strdup,
1841+ g_free,
1842+ NULL, NULL, NULL, NULL, NULL, NULL));
1843+
1844+ GeeSet *oldLinks = folks_anti_linkable_get_anti_links(FOLKS_ANTI_LINKABLE(m_currentPersona));
1845+ if (oldLinks &&
1846+ gee_collection_contains(GEE_COLLECTION(antiLinks), "*")) {
1847+ updateDetailsDone(0, 0, this);
1848+ return;
1849+ } else if (oldLinks) {
1850+ gee_collection_add_all(GEE_COLLECTION(antiLinks), GEE_COLLECTION(oldLinks));
1851+ }
1852+ gee_collection_add(GEE_COLLECTION(antiLinks), "*");
1853+
1854+ folks_anti_linkable_change_anti_links(FOLKS_ANTI_LINKABLE(m_currentPersona),
1855+ antiLinks,
1856+ (GAsyncReadyCallback) folksAddAntiLinksDone,
1857+ this);
1858+ }
1859 }
1860 }
1861
1862@@ -780,8 +867,9 @@
1863 updateDetailsDone(0, 0, self);
1864 break;
1865 case QContactDetail::TypeVersion:
1866- self->m_currentPersonaIndex += 1;
1867- self->updatePersona(self->m_currentPersonaIndex);
1868+ g_object_unref(self->m_currentPersona);
1869+ self->m_currentPersona = 0;
1870+ self->updatePersona();
1871 break;
1872 default:
1873 qWarning() << "Update not implemented for" << self->m_currentDetailType;
1874@@ -790,4 +878,17 @@
1875 }
1876 }
1877
1878+void UpdateContactRequest::folksAddAntiLinksDone(FolksAntiLinkable *antilinkable,
1879+ GAsyncResult *result,
1880+ UpdateContactRequest *self)
1881+{
1882+ GError *error = 0;
1883+ folks_anti_linkable_change_anti_links_finish(antilinkable, result, &error);
1884+ if (error) {
1885+ qWarning() << "Error during the anti link operation:" << error->message;
1886+ g_error_free(error);
1887+ }
1888+ updateDetailsDone(0, 0, self);
1889 }
1890+
1891+} // namespace
1892
1893=== modified file 'lib/update-contact-request.h'
1894--- lib/update-contact-request.h 2013-10-30 19:07:49 +0000
1895+++ lib/update-contact-request.h 2014-02-18 00:13:04 +0000
1896@@ -23,6 +23,7 @@
1897 #include <QtCore/QString>
1898 #include <QtCore/QList>
1899 #include <QtCore/QMetaMethod>
1900+#include <QtCore/QEventLoop>
1901
1902 #include <QtContacts/QContact>
1903
1904@@ -38,20 +39,26 @@
1905
1906 public:
1907 UpdateContactRequest(QtContacts::QContact newContact, QIndividual *parent, QObject *listener, const char *slot);
1908-
1909+ ~UpdateContactRequest();
1910 void start();
1911+ void wait();
1912+ void deatach();
1913
1914 Q_SIGNALS:
1915 void done(const QString &errorMessage);
1916
1917 private:
1918+ QIndividual *m_parent;
1919+ QObject *m_object;
1920+ FolksPersona *m_currentPersona;
1921+ QEventLoop *m_eventLoop;
1922+
1923+ QList<FolksPersona*> m_personas;
1924+ QtContacts::QContact m_originalContact;
1925 QtContacts::QContact m_newContact;
1926 int m_currentDetailType;
1927+ QMetaMethod m_slot;
1928 int m_currentPersonaIndex;
1929- int m_maxPersona;
1930- QIndividual *m_parent;
1931- QObject *m_object;
1932- QMetaMethod m_slot;
1933
1934 void invokeSlot(const QString &errorMessage = QString());
1935
1936@@ -61,6 +68,8 @@
1937 const QtContacts::QContactDetail &prefB);
1938 static bool isEqual(QList<QtContacts::QContactDetail> listA,
1939 QList<QtContacts::QContactDetail> listB);
1940+ static bool isEqual(const QtContacts::QContactDetail &detailA,
1941+ const QtContacts::QContactDetail &detailB);
1942 static bool checkPersona(QtContacts::QContactDetail &det, int persona);
1943 static QList<QtContacts::QContactDetail> detailsFromPersona(const QtContacts::QContact &contact,
1944 QtContacts::QContactDetail::DetailType type,
1945@@ -74,7 +83,8 @@
1946 int persona,
1947 QtContacts::QContactDetail *pref) const;
1948
1949- void updatePersona(int index);
1950+
1951+ void updatePersona();
1952 void updateAddress();
1953 void updateAvatar();
1954 void updateBirthday();
1955@@ -94,7 +104,7 @@
1956 GAsyncResult *result);
1957
1958 static void updateDetailsDone(GObject *detail, GAsyncResult *result, gpointer userdata);
1959-
1960+ static void folksAddAntiLinksDone(FolksAntiLinkable *antilinkable, GAsyncResult *result, UpdateContactRequest *self);
1961 };
1962
1963 }
1964
1965=== modified file 'lib/view.cpp'
1966--- lib/view.cpp 2013-10-30 19:07:49 +0000
1967+++ lib/view.cpp 2014-02-18 00:13:04 +0000
1968@@ -108,11 +108,13 @@
1969 protected:
1970 void run()
1971 {
1972+ m_allContacts->lock();
1973 Q_FOREACH(ContactEntry *entry, m_allContacts->values())
1974 {
1975 m_stoppedLock.lockForRead();
1976 if (m_stopped) {
1977 m_stoppedLock.unlock();
1978+ m_allContacts->unlock();
1979 return;
1980 }
1981 m_stoppedLock.unlock();
1982@@ -121,8 +123,8 @@
1983 m_contacts << entry;
1984 }
1985 }
1986-
1987 chageSort(m_sortClause);
1988+ m_allContacts->unlock();
1989 }
1990
1991 private:
1992
1993=== modified file 'tests/unittest/CMakeLists.txt'
1994--- tests/unittest/CMakeLists.txt 2013-11-01 01:10:12 +0000
1995+++ tests/unittest/CMakeLists.txt 2014-02-18 00:13:04 +0000
1996@@ -70,6 +70,9 @@
1997
1998 declare_test(addressbook-test True ${BASE_CLIENT_TEST_SRC})
1999 declare_test(service-life-cycle-test True ${BASE_CLIENT_TEST_SRC})
2000+ # Disable test until the new folks arrive on jenkins
2001+ #declare_test(contact-link-test True ${BASE_CLIENT_TEST_SRC})
2002+
2003 elseif()
2004 message(STATUS "DBus test runner not found. Some tests will be disabled")
2005 endif()
2006
2007=== modified file 'tests/unittest/addressbook-test.cpp'
2008--- tests/unittest/addressbook-test.cpp 2013-12-13 19:06:57 +0000
2009+++ tests/unittest/addressbook-test.cpp 2014-02-18 00:13:04 +0000
2010@@ -111,7 +111,7 @@
2011 m_resultBasicVcard = QStringLiteral("BEGIN:VCARD\r\n"
2012 "VERSION:3.0\r\n"
2013 "UID:%1\r\n"
2014- "CLIENTPIDMAP:1;dummy:dummy-store:0\r\n"
2015+ "CLIENTPIDMAP:1;dummy:dummy-store:%2\r\n"
2016 "N;PID=1.1:Tal;Fulano_;de;;\r\n"
2017 "FN;PID=1.1:Fulano_ Tal\r\n"
2018 "X-QTPROJECT-FAVORITE;PID=1.1:false;0\r\n"
2019@@ -213,7 +213,7 @@
2020
2021 // user returned id to fill the new vcard
2022 QString newContactId = reply.value();
2023- QString newVcard = m_resultBasicVcard.arg(newContactId);
2024+ QString newVcard = m_resultBasicVcard.arg(newContactId).arg(0);
2025
2026 // try create a contact with the same id
2027 QDBusReply<QString> reply2 = m_serverIface->call("createContact", newVcard, "dummy-store");
2028@@ -267,6 +267,46 @@
2029 QDBusReply<QStringList> replyList = m_dummyIface->call("listContacts");
2030 QCOMPARE(replyList.value().count(), 0);
2031 }
2032+
2033+ void testUpdateContact()
2034+ {
2035+ // create a basic contact
2036+ QDBusReply<QString> replyAdd = m_serverIface->call("createContact", m_basicVcard, "dummy-store");
2037+ QString newContactId = replyAdd.value();
2038+
2039+ // update the contact phone number
2040+ QString vcard = m_resultBasicVcard.arg(newContactId).arg(3);
2041+ vcard = vcard.replace("8888888", "0000000");
2042+ QList<QtContacts::QContact> contacts = galera::VCardParser::vcardToContact(QStringList() << vcard);
2043+ QtContacts::QContact contactUpdated = contacts[0];
2044+
2045+ // spy 'contactsUpdated' signal
2046+ QSignalSpy updateContactSpy(m_serverIface, SIGNAL(contactsUpdated(const QStringList &)));
2047+ QDBusReply<QStringList> replyUpdate = m_serverIface->call("updateContacts", QStringList() << vcard);
2048+ QStringList result = replyUpdate.value();
2049+ QCOMPARE(result.size(), 1);
2050+
2051+ // check if contact returned by update function contains the new data
2052+ contacts = galera::VCardParser::vcardToContact(result);
2053+ QtContacts::QContact contactUpdatedResult = contacts[0];
2054+ compareContact(contactUpdatedResult, contactUpdated);
2055+
2056+ // check if the 'contactsUpdated' signal was fired with the correct args
2057+ QTRY_COMPARE(updateContactSpy.count(), 1);
2058+ QList<QVariant> args = updateContactSpy.takeFirst();
2059+ QCOMPARE(args.count(), 1);
2060+ QStringList ids = args[0].toStringList();
2061+ QCOMPARE(ids[0], newContactId);
2062+
2063+ // check if the contact was updated into the backend
2064+ QDBusReply<QStringList> replyList = m_dummyIface->call("listContacts");
2065+ result = replyList.value();
2066+ QCOMPARE(result.count(), 1);
2067+
2068+ contacts = galera::VCardParser::vcardToContact(result);
2069+ contactUpdatedResult = contacts[0];
2070+ compareContact(contactUpdatedResult, contactUpdated);
2071+ }
2072 };
2073
2074 QTEST_MAIN(AddressBookTest)
2075
2076=== added file 'tests/unittest/contact-link-test.cpp'
2077--- tests/unittest/contact-link-test.cpp 1970-01-01 00:00:00 +0000
2078+++ tests/unittest/contact-link-test.cpp 2014-02-18 00:13:04 +0000
2079@@ -0,0 +1,100 @@
2080+/*
2081+ * Copyright 2013 Canonical Ltd.
2082+ *
2083+ * This file is part of contact-service-app.
2084+ *
2085+ * contact-service-app is free software; you can redistribute it and/or modify
2086+ * it under the terms of the GNU General Public License as published by
2087+ * the Free Software Foundation; version 3.
2088+ *
2089+ * contact-service-app is distributed in the hope that it will be useful,
2090+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2091+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2092+ * GNU General Public License for more details.
2093+ *
2094+ * You should have received a copy of the GNU General Public License
2095+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2096+ */
2097+
2098+#include "base-client-test.h"
2099+#include "lib/source.h"
2100+#include "lib/qindividual.h"
2101+#include "common/dbus-service-defs.h"
2102+#include "common/vcard-parser.h"
2103+
2104+#include <QObject>
2105+#include <QtDBus>
2106+#include <QtTest>
2107+#include <QDebug>
2108+#include <QtVersit>
2109+
2110+class ContactLinkTest : public BaseClientTest
2111+{
2112+ Q_OBJECT
2113+private:
2114+ QString m_vcard;
2115+
2116+private Q_SLOTS:
2117+ void initTestCase()
2118+ {
2119+ BaseClientTest::initTestCase();
2120+ m_vcard = QStringLiteral("BEGIN:VCARD\n"
2121+ "VERSION:3.0\n"
2122+ "N:tal;fulano_;de;;\n"
2123+ "EMAIL:email@ubuntu.com\n"
2124+ "TEL;PID=1.1;TYPE=ISDN:33331410\n"
2125+ "END:VCARD");
2126+ }
2127+
2128+ void testCreateContactWithSameEmail()
2129+ {
2130+ // call create contact
2131+ QDBusReply<QString> reply = m_serverIface->call("createContact", m_vcard, "dummy-store");
2132+
2133+ // check if the returned id is valid
2134+ QString contactAId = reply.value();
2135+ QVERIFY(!contactAId.isEmpty());
2136+
2137+ QString vcardB = m_vcard.replace("fulano", "contact");
2138+ reply = m_serverIface->call("createContact", vcardB, "dummy-store");
2139+
2140+ // check if the returned id is valid
2141+ QString contactBId = reply.value();
2142+ QVERIFY(!contactBId.isEmpty());
2143+
2144+ QVERIFY(contactAId != contactBId);
2145+ }
2146+
2147+ void testAppendOtherContactEmail()
2148+ {
2149+ // call create contact
2150+ QDBusReply<QString> reply = m_serverIface->call("createContact", m_vcard, "dummy-store");
2151+
2152+ // check if the returned id is valid
2153+ QString contactAId = reply.value();
2154+ QVERIFY(!contactAId.isEmpty());
2155+
2156+ // add a contact with diff email
2157+ QString vcardB = m_vcard.replace("fulano", "contact").replace("email","email2");
2158+ reply = m_serverIface->call("createContact", vcardB, "dummy-store");
2159+
2160+ // check if the returned id is valid
2161+ QString contactBId = reply.value();
2162+ QVERIFY(!contactBId.isEmpty());
2163+
2164+ // udate contactB with same email as contactA
2165+ vcardB = m_vcard.replace("fulano", "contact");
2166+ reply = m_serverIface->call("createContact", vcardB, "dummy-store");
2167+
2168+ // check if the returned id is valid
2169+ contactBId = reply.value();
2170+ QVERIFY(!contactBId.isEmpty());
2171+
2172+ // check if id still different
2173+ QVERIFY(contactAId != contactBId);
2174+ }
2175+};
2176+
2177+QTEST_MAIN(ContactLinkTest)
2178+
2179+#include "contact-link-test.moc"
2180
2181=== modified file 'tests/unittest/dummy-backend.cpp'
2182--- tests/unittest/dummy-backend.cpp 2013-12-13 19:57:13 +0000
2183+++ tests/unittest/dummy-backend.cpp 2014-02-18 00:13:04 +0000
2184@@ -192,6 +192,24 @@
2185 return QString();
2186 }
2187
2188+void DummyBackendProxy::contactUpdated(const QString &contactId,
2189+ const QString &errorMsg)
2190+{
2191+ m_contactUpdated = true;
2192+}
2193+
2194+QString DummyBackendProxy::updateContact(const QString &contactId,
2195+ const QtContacts::QContact &qcontact)
2196+{
2197+ galera::QIndividual *i = m_contacts.value(contactId);
2198+ Q_ASSERT(i);
2199+ ScopedEventLoop loop(&m_eventLoop);
2200+ m_contactUpdated = false;
2201+ i->update(qcontact, this, SLOT(contactUpdated(QString,QString)));
2202+ loop.exec();
2203+ return i->id();
2204+}
2205+
2206 void DummyBackendProxy::configurePrimaryStore()
2207 {
2208 static const char* writableProperties[] = {
2209@@ -205,6 +223,7 @@
2210 "Dummy personas",
2211 const_cast<char**>(writableProperties), 4);
2212 folks_dummy_persona_store_set_persona_type(m_primaryPersonaStore, FOLKS_DUMMY_TYPE_FULL_PERSONA);
2213+ folks_dummy_persona_store_update_trust_level(m_primaryPersonaStore, FOLKS_PERSONA_STORE_TRUST_FULL);
2214
2215 GeeHashSet *personaStores = gee_hash_set_new(FOLKS_TYPE_PERSONA_STORE,
2216 (GBoxedCopyFunc) g_object_ref, g_object_unref,
2217@@ -349,9 +368,12 @@
2218 GeeMultiMap *changes,
2219 DummyBackendProxy *self)
2220 {
2221+ Q_UNUSED(individualAggregator);
2222+
2223 GeeIterator *iter;
2224 GeeSet *removed = gee_multi_map_get_keys(changes);
2225 GeeCollection *added = gee_multi_map_get_values(changes);
2226+ QStringList addedIds;
2227
2228 iter = gee_iterable_iterator(GEE_ITERABLE(added));
2229 while(gee_iterator_next(iter)) {
2230@@ -359,6 +381,7 @@
2231 if (individual) {
2232 galera::QIndividual *idv = new galera::QIndividual(individual, self->m_aggregator);
2233 self->m_contacts.insert(idv->id(), idv);
2234+ addedIds << idv->id();
2235 g_object_unref(individual);
2236 }
2237 }
2238@@ -369,7 +392,7 @@
2239 FolksIndividual *individual = FOLKS_INDIVIDUAL(gee_iterator_get(iter));
2240 if (individual) {
2241 QString id = QString::fromUtf8(folks_individual_get_id(individual));
2242- if (self->m_contacts.contains(id)) {
2243+ if (!addedIds.contains(id) && self->m_contacts.contains(id)) {
2244 delete self->m_contacts.take(id);
2245 }
2246 g_object_unref(individual);
2247@@ -426,3 +449,15 @@
2248 QList<QtContacts::QContact> contacts = galera::VCardParser::vcardToContact(QStringList() << vcard);
2249 return m_proxy->createContact(contacts[0]);
2250 }
2251+
2252+QString DummyBackendAdaptor::updateContact(const QString &contactId, const QString &vcard)
2253+{
2254+ QList<QtContacts::QContact> contacts = galera::VCardParser::vcardToContact(QStringList() << vcard);
2255+ return m_proxy->updateContact(contactId, contacts[0]);
2256+}
2257+
2258+void DummyBackendAdaptor::enableAutoLink(bool flag)
2259+{
2260+ galera::QIndividual::enableAutoLink(flag);
2261+}
2262+
2263
2264=== modified file 'tests/unittest/dummy-backend.h'
2265--- tests/unittest/dummy-backend.h 2013-11-11 14:21:24 +0000
2266+++ tests/unittest/dummy-backend.h 2014-02-18 00:13:04 +0000
2267@@ -46,6 +46,7 @@
2268 bool isReady() const;
2269
2270 QString createContact(const QtContacts::QContact &qcontact);
2271+ QString updateContact(const QString &contactId, const QtContacts::QContact &qcontact);
2272 QList<QtContacts::QContact> contacts() const;
2273 QList<galera::QIndividual*> individuals() const;
2274
2275@@ -55,6 +56,7 @@
2276 void shutdown();
2277 QStringList listContacts() const;
2278 void reset();
2279+ void contactUpdated(const QString &contactId, const QString &errorMsg);
2280
2281 Q_SIGNALS:
2282 void ready();
2283@@ -71,6 +73,7 @@
2284 bool m_isReady;
2285 int m_individualsChangedDetailedId;
2286 QHash<QString, galera::QIndividual*> m_contacts;
2287+ bool m_contactUpdated;
2288
2289 bool registerObject();
2290 void initFolks();
2291@@ -108,10 +111,18 @@
2292 " <arg direction=\"out\" type=\"b\"/>\n"
2293 " </method>\n"
2294 " <method name=\"quit\"/>\n"
2295+" <method name=\"enableAutoLink\">\n"
2296+" <arg direction=\"in\" type=\"b\"/>\n"
2297+" </method>\n"
2298 " <method name=\"createContact\">\n"
2299 " <arg direction=\"in\" type=\"s\"/>\n"
2300 " <arg direction=\"out\" type=\"s\"/>\n"
2301 " </method>\n"
2302+" <method name=\"updateContact\">\n"
2303+" <arg direction=\"in\" type=\"s\"/>\n"
2304+" <arg direction=\"in\" type=\"s\"/>\n"
2305+" <arg direction=\"out\" type=\"s\"/>\n"
2306+" </method>\n"
2307 " <method name=\"listContacts\">\n"
2308 " <arg direction=\"out\" type=\"as\"/>\n"
2309 " </method>\n"
2310@@ -130,6 +141,8 @@
2311 void reset();
2312 QStringList listContacts();
2313 QString createContact(const QString &vcard);
2314+ QString updateContact(const QString &contactId, const QString &vcard);
2315+ void enableAutoLink(bool flag);
2316
2317 Q_SIGNALS:
2318 void ready();

Subscribers

People subscribed via source and target branches