Merge lp:~renatofilho/address-book-service/fix-1269561 into lp:address-book-service
- fix-1269561
- Merge into trunk
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 |
Related bugs: |
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:/
* 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
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:97
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 98. By Renato Araujo Oliveira Filho
-
Update changelog.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:98
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 99. By Renato Araujo Oliveira Filho
-
Fixed changelog syntax.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:99
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 100. By Renato Araujo Oliveira Filho
-
Removed unnecessary code.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:100
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
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
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;
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:101
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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)
- 102. By Renato Araujo Oliveira Filho
-
Fixed contact removal after create a anti lik rule.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:102
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:104
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:107
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:109
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 110. By Renato Araujo Oliveira Filho
-
Added missing file.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:110
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 111. By Renato Araujo Oliveira Filho
-
Fixed dbus reply for contact update.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:111
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:114
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 115. By Renato Araujo Oliveira Filho
-
Remove null fron the array list used on linkable properties.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:115
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 116. By Renato Araujo Oliveira Filho
-
Disable unit test for link contacts until the new folks arrive on jenkins
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:116
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 117. By Renato Araujo Oliveira Filho
-
add a new ref on the current persona during the update process.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:117
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 118. By Renato Araujo Oliveira Filho
-
Merged mainline.
Fixed reference leak on individual personas.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:118
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 119. By Renato Araujo Oliveira Filho
-
Enabled contact link test.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:119
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
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:/
Preview Diff
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(); |
FAILED: Continuous integration, rev:97 jenkins. qa.ubuntu. com/job/ address- book-service- ci/131/ jenkins. qa.ubuntu. com/job/ address- book-service- trusty- amd64-ci/ 38/console jenkins. qa.ubuntu. com/job/ address- book-service- trusty- armhf-ci/ 38/console jenkins. qa.ubuntu. com/job/ address- book-service- trusty- i386-ci/ 39/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/address- book-service- ci/131/ rebuild
http://