Merge lp:~renatofilho/address-book-service/fix-1269561 into lp:address-book-service

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Merged at revision: 98
Proposed branch: lp:~renatofilho/address-book-service/fix-1269561
Merge into: lp:address-book-service
Diff against target: 2173 lines (+851/-330)
17 files modified
3rd_party/folks/dummy/lib/dummy-full-persona.vala (+12/-1)
3rd_party/folks/dummy/lib/dummy-persona.vala (+33/-3)
debian/changelog (+16/-0)
lib/addressbook.cpp (+144/-72)
lib/addressbook.h (+5/-3)
lib/contacts-map.cpp (+44/-22)
lib/contacts-map.h (+9/-3)
lib/qindividual.cpp (+183/-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 (+2/-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/fix-1269561
Reviewer Review Type Date Requested Status
Bill Filler (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+202877@code.launchpad.net

Commit message

Avoid the persona to get linked automatically during the contact create or edit.

Description of the change

* Are there any related MPs required for this MP to build/function as expected? Please list. NO

* Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes) YES

* Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator? YES

* Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/address-book-service) on device or emulator? YES

* If you changed the UI, was the change specified/approved by design? N/A

* If you changed the packaging (debian), did you subscribe a core-dev to this MP? N/A

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

Update changelog.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
99. By Renato Araujo Oliveira Filho

Fixed changelog syntax.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
100. By Renato Araujo Oliveira Filho

Removed unnecessary code.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

One problem so far:
- create a contact with a gmail.com email address
- upon save a IM account for jabber with the same email address is saved (this shouldn't happen)
- try and delete the jabber account and press save and the app hangs on spinner

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

Another problem:
- create two contacts with same email address
- delete the first contact (works ok)
- delete the second contact. Does not work, contact never goes away

review: Needs Fixing
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> One problem so far:
> - create a contact with a gmail.com email address
> - upon save a IM account for jabber with the same email address is saved (this
> shouldn't happen)
> - try and delete the jabber account and press save and the app hangs on
> spinner

I suggest to handle this as a different MR since this bug already exist in the current version;

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Another problem:
> - create two contacts with same email address
> - delete the first contact (works ok)
> - delete the second contact. Does not work, contact never goes away

Fixed on rev. 101

101. By Renato Araujo Oliveira Filho

Update contact info after link/unlink operations.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

This is still broken:
- create two contacts, A and B with same home email address a@b.com
- delete the first contact (works ok)
- delete the second contact. (works ok)
- kill the address-book-app
- restart the app and contact A is back (BUG)
- the contents of contact A now have 2 home email addresses listed, both a@b.com (BUG)
- you cannot delete contact A now, won't go away (BUG)

review: Needs Fixing
102. By Renato Araujo Oliveira Filho

Fixed contact removal after create a anti lik rule.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
103. By Renato Araujo Oliveira Filho

Avoid crash during the contact update.
Deatach the update operation from contact before the id change.

104. By Renato Araujo Oliveira Filho

Set anti link rule before edit the persona to avoid links.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
105. By Renato Araujo Oliveira Filho

Create autolink flag to tests.

106. By Renato Araujo Oliveira Filho

Update dummy folks to support link personas.

107. By Renato Araujo Oliveira Filho

Created unit test for link contacts.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
108. By Renato Araujo Oliveira Filho

Revert changes on folks code.

109. By Renato Araujo Oliveira Filho

Fixed test to avoid remove contact when folks emit contacts changes during the contact link.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
110. By Renato Araujo Oliveira Filho

Added missing file.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
111. By Renato Araujo Oliveira Filho

Fixed dbus reply for contact update.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
112. By Renato Araujo Oliveira Filho

Updated folks dummy backend to support contact link.

113. By Renato Araujo Oliveira Filho

Fixed contact update return value.

114. By Renato Araujo Oliveira Filho

Added unit test for contact update.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
115. By Renato Araujo Oliveira Filho

Remove null fron the array list used on linkable properties.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
116. By Renato Araujo Oliveira Filho

Disable unit test for link contacts until the new folks arrive on jenkins

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
117. By Renato Araujo Oliveira Filho

add a new ref on the current persona during the update process.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
118. By Renato Araujo Oliveira Filho

Merged mainline.
Fixed reference leak on individual personas.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
119. By Renato Araujo Oliveira Filho

Enabled contact link test.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

Needs fixing:
1) create a contact with home email a@a.com
2) save the contact
3) edit the contact and add another home email a@a.com
this fails

review: Needs Fixing
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

> Needs fixing:
> 1) create a contact with home email a@a.com
> 2) save the contact
> 3) edit the contact and add another home email a@a.com
> this fails

This bug already exists on the current version, and to avoid this MR grow even more, I created a different MR only for this bug:

https://code.launchpad.net/~renatofilho/address-book-service/fix-duplicated-fields/+merge/208040

Revision history for this message
Bill Filler (bfiller) wrote :

approved

review: Approve

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

Subscribers

People subscribed via source and target branches