Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117856439
D5232.1775316716.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
D5232.1775316716.diff
View Options
diff --git a/lib/kolab_sync_data_contacts.php b/lib/kolab_sync_data_contacts.php
--- a/lib/kolab_sync_data_contacts.php
+++ b/lib/kolab_sync_data_contacts.php
@@ -192,16 +192,12 @@
$result[$key] = $value;
}
+ $emails = $this->getKolabContactEmails($data);
+
// email address(es): email1Address, email2Address, email3Address
for ($x = 0; $x < 3; $x++) {
- if (!empty($data['email'][$x])) {
- $email = $data['email'][$x];
- if (is_array($email)) {
- $email = $email['address'];
- }
- if ($email) {
- $result['email' . ($x + 1) . 'Address'] = $email;
- }
+ if (!empty($emails[$x])) {
+ $result['email' . ($x + 1) . 'Address'] = $emails[$x];
}
}
@@ -269,31 +265,7 @@
}
// email address(es): email1Address, email2Address, email3Address
- $emails = [];
- for ($x = 0; $x < 3; $x++) {
- $key = 'email' . ($x + 1) . 'Address';
- $value = $data->$key ?? null;
- if ($value) {
- // Android sends email address as: Lars Kneschke <l.kneschke@metaways.de>
- if (preg_match('/(.*)<(.+@[^@]+)>/', $value, $matches)) {
- $value = trim($matches[2]);
- }
-
- // sanitize email address, it can contain broken (non-unicode) characters (#3287)
- $value = rcube_charset::clean($value);
-
- // try to find address type, at least we can do this if
- // address wasn't changed
- $type = '';
- foreach ((array)$contact['email'] as $email) {
- if ($email['address'] == $value) {
- $type = $email['type'];
- }
- }
- $emails[] = ['address' => $value, 'type' => $type];
- }
- }
- $contact['email'] = $emails;
+ $this->setKolabContactEmails($contact, $data);
return $contact;
}
@@ -638,4 +610,99 @@
return [];
}
+
+ /**
+ * Extract list of email addresses from a Kolab contact
+ */
+ protected function getKolabContactEmails($contact)
+ {
+ // Contacts from XML (Kolab3) contain 'email' item set with an array that contains address and type
+ // or is just an address.
+ // Contacts from DAV (Kolab4) contain 'email:<type>' items with an array of email addresses.
+
+ $emails = [];
+ foreach (['email', 'email:work', 'email:other', 'email:home'] as $key) {
+ foreach ($contact[$key] ?? [] as $item) {
+ if (is_string($item) && strpos($item, '@')) {
+ $emails[] = $item;
+ } elseif (is_array($item) && !empty($item)) {
+ if (isset($item['address'])) {
+ $emails[] = $item['address'];
+ } else {
+ $emails = array_merge($emails, $item);
+ }
+ }
+ }
+ }
+
+ // Remove duplicates and empty values
+ return array_values(array_filter(array_unique($emails)));
+ }
+
+ /**
+ * Set Kolab contact email addresses
+ */
+ protected function setKolabContactEmails(&$contact, $data)
+ {
+ // On Kolab3 (XML) contacts have 'email' item that is a list of arrays that contain address and type
+ // or is just an address. No types.
+ // On Kolab4 (DAV) contacts have 'email:home', 'email:other' and 'email:home' items with
+ // an array of email addresses each.
+
+ // Get addresses from ActiveSync properties (email1Address, email2Address, email3Address)
+ $emails = [];
+ for ($x = 0; $x < 3; $x++) {
+ $key = 'email' . ($x + 1) . 'Address';
+ $email = $data->$key ?? null;
+ if ($email) {
+ // sanitize email address, it can contain broken (non-unicode) characters (#3287)
+ $email = rcube_charset::clean($email);
+
+ // Android sends email address as: Lars Kneschke <l.kneschke@metaways.de>
+ if (preg_match('/(.*)<(.+@[^@]+)>/', $email, $matches)) {
+ $email = trim($matches[2]);
+ }
+
+ $emails[] = $email;
+ }
+ }
+
+ // Warning: If contact has more than 3 addresses in Kolab they will be removed
+
+ if ($this->backend instanceof kolab_sync_storage_kolab4) {
+ // Remove addresses that do not exist anymore
+ $existing = [];
+ foreach (['email:work', 'email:other', 'email:home'] as $key) {
+ if (!empty($contact[$key])) {
+ $contact[$key] = array_values(array_intersect($contact[$key], $emails));
+ $existing = array_merge($existing, $contact[$key]);
+ if (empty($contact[$key])) {
+ unset($contact[$key]);
+ }
+ }
+ }
+
+ // Add new addresses
+ foreach (array_diff($emails, $existing) as $email) {
+ if (!isset($contact['email:other'])) {
+ $contact['email:other'] = [];
+ }
+ $contact['email:other'][] = $email;
+ }
+ } else {
+ foreach ($emails as $idx => $email) {
+ // try to find address type, at least we can do this if address wasn't changed
+ $type = '';
+ foreach ($contact['email'] ?? [] as $existing) {
+ if (isset($existing['address']) && $existing['address'] == $email) {
+ $type = $existing['type'] ?? '';
+ }
+ }
+
+ $emails[$idx] = ['address' => $email, 'type' => $type];
+ }
+
+ $contact['email'] = $emails;
+ }
+ }
}
diff --git a/tests/Sync/Sync/ContactsTest.php b/tests/Sync/Sync/ContactsTest.php
--- a/tests/Sync/Sync/ContactsTest.php
+++ b/tests/Sync/Sync/ContactsTest.php
@@ -79,10 +79,19 @@
$root .= "/ns:Commands/ns:Add";
$this->assertStringMatchesFormat("CRC%s", $xpath->query("{$root}/ns:ServerId")->item(0)->nodeValue);
- $this->assertSame('Jack', $xpath->query("{$root}/ns:ApplicationData/Contacts:FirstName")->item(0)->nodeValue);
- $this->assertSame('Strong', $xpath->query("{$root}/ns:ApplicationData/Contacts:LastName")->item(0)->nodeValue);
- $this->assertSame('Jane', $xpath->query("{$root}/ns:ApplicationData/Contacts:FirstName")->item(1)->nodeValue);
- $this->assertSame('Doe', $xpath->query("{$root}/ns:ApplicationData/Contacts:LastName")->item(1)->nodeValue);
+ $r = "{$root}[1]/ns:ApplicationData";
+ $this->assertSame('Jack', $xpath->query("{$r}/Contacts:FirstName")->item(0)->nodeValue);
+ $this->assertSame('Strong', $xpath->query("{$r}/Contacts:LastName")->item(0)->nodeValue);
+ $this->assertSame('jack@kolab.org', $xpath->query("{$r}/Contacts:Email1Address")->item(0)->nodeValue);
+ $this->assertNull($xpath->query("{$r}/Contacts:Email2Address")->item(0));
+ $this->assertNull($xpath->query("{$r}/Contacts:Email3Address")->item(0));
+ $r = "{$root}[2]/ns:ApplicationData";
+ $this->assertSame('Jane', $xpath->query("{$r}/Contacts:FirstName")->item(0)->nodeValue);
+ $this->assertSame('J.', $xpath->query("{$r}/Contacts:MiddleName")->item(0)->nodeValue);
+ $this->assertSame('Doe', $xpath->query("{$r}/Contacts:LastName")->item(0)->nodeValue);
+ $this->assertNull($xpath->query("{$r}/Contacts:Email1Address")->item(0));
+ $this->assertNull($xpath->query("{$r}/Contacts:Email2Address")->item(0));
+ $this->assertNull($xpath->query("{$r}/Contacts:Email3Address")->item(0));
return $syncKey;
}
@@ -117,6 +126,10 @@
<ClientId>42</ClientId>
<ApplicationData>
<Contacts:FirstName>Lars</Contacts:FirstName>
+ <Contacts:LastName>Ulrich</Contacts:LastName>
+ <Contacts:Email1Address>lars@kolab.org</Contacts:Email1Address>
+ <Contacts:Email2Address>lars.tw@kolab.org</Contacts:Email2Address>
+ <Contacts:Email3Address>lars.th@kolab.org</Contacts:Email3Address>
</ApplicationData>
</Add>
</Commands>
@@ -141,7 +154,13 @@
$serverId = $xpath->query("ns:ServerId", $root)->item(0)->nodeValue;
$this->assertStringMatchesFormat("CRC%s", $serverId);
- // TODO: Test the content on the server
+ // Assert the content on the server
+ $contacts = $this->getDavObjects('Contacts', 'contact');
+ $this->assertCount(3, $contacts);
+ usort($contacts, function ($c1, $c2) { return $c1['surname'] <=> $c2['surname']; });
+ $this->assertSame('Lars', $contacts[2]['firstname']);
+ $this->assertSame('Ulrich', $contacts[2]['surname']);
+ $this->assertSame(['lars@kolab.org', 'lars.tw@kolab.org', 'lars.th@kolab.org'], $contacts[2]['email:other']);
return [$syncKey, $serverId];
}
diff --git a/tests/SyncTestCase.php b/tests/SyncTestCase.php
--- a/tests/SyncTestCase.php
+++ b/tests/SyncTestCase.php
@@ -323,6 +323,26 @@
return $decoder->decode();
}
+ /**
+ * Get objects from a DAV folder
+ */
+ protected function getDavObjects($foldername, $type, $query = [])
+ {
+ $dav = $this->getDavStorage();
+
+ foreach ($dav->get_folders($type) as $folder) {
+ if ($folder->get_name() === $foldername) {
+ $result = [];
+ foreach ($folder->select($query) as $object) {
+ $result[] = $object;
+ }
+ return $result;
+ }
+ }
+
+ throw new \Exception("Folder not found");
+ }
+
/**
* Initialize DAV storage
*/
diff --git a/tests/src/contact.vcard1 b/tests/src/contact.vcard1
--- a/tests/src/contact.vcard1
+++ b/tests/src/contact.vcard1
@@ -1,6 +1,6 @@
BEGIN:VCARD
VERSION:3.0
-UID:urn:uuid:abcdef-0123-4567-89ab-abcdefabcdef
+UID:abcdef-0123-4567-89ab-abcdefabcdef
FN:Jane Doe
N:Doe;Jane;J.;;
END:VCARD
diff --git a/tests/src/contact.vcard2 b/tests/src/contact.vcard2
--- a/tests/src/contact.vcard2
+++ b/tests/src/contact.vcard2
@@ -1,6 +1,7 @@
BEGIN:VCARD
VERSION:3.0
-UID:urn:uuid:abcdef-0123-4567-89ab-abcdefabc123
+UID:abcdef-0123-4567-89ab-abcdefabc123
FN:Jack Strong
N:Strong;Jack;;;
+EMAIL;TYPE=work:jack@kolab.org
END:VCARD
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 4, 3:31 PM (2 d, 4 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18830175
Default Alt Text
D5232.1775316716.diff (10 KB)
Attached To
Mode
D5232: Fix synchronization of email addresses for DAV contacts
Attached
Detach File
Event Timeline