From 5f17657e82abfae6bce0027e243e22a4183174a6 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Aug 2014 18:19:55 +0200 Subject: Support contacts import in GMail CSV format --- program/lib/Roundcube/rcube_csv2vcard.php | 175 ++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 9 deletions(-) (limited to 'program/lib') diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php index 06bc387d5..b7d159178 100644 --- a/program/lib/Roundcube/rcube_csv2vcard.php +++ b/program/lib/Roundcube/rcube_csv2vcard.php @@ -149,6 +149,13 @@ class rcube_csv2vcard // GMail 'groups' => 'groups', + 'group_membership' => 'groups', + 'given_name' => 'firstname', + 'additional_name' => 'middlename', + 'family_name' => 'surname', + 'name' => 'displayname', + 'name_prefix' => 'prefix', + 'name_suffix' => 'suffix', ); /** @@ -272,12 +279,95 @@ class rcube_csv2vcard 'work_mobile' => "Work Mobile", 'work_title' => "Work Title", 'work_zip' => "Work Zip", - 'groups' => "Group", + 'group' => "Group", + + // GMail + 'groups' => "Groups", + 'group_membership' => "Group Membership", + 'given_name' => "Given Name", + 'additional_name' => "Additional Name", + 'family_name' => "Family Name", + 'name' => "Name", + 'name_prefix' => "Name Prefix", + 'name_suffix' => "Name Suffix", + ); + + /** + * Special fields map for GMail format + * + * @var array + */ + protected $gmail_label_map = array( + 'E-mail' => array( + 'Value' => array( + 'home' => 'email:home', + 'work' => 'email:work', + ), + ), + 'Phone' => array( + 'Value' => array( + 'home' => 'phone:home', + 'homefax' => 'phone:homefax', + 'main' => 'phone:pref', + 'pager' => 'phone:pager', + 'mobile' => 'phone:cell', + 'work' => 'phone:work', + 'workfax' => 'phone:workfax', + ), + ), + 'Relation' => array( + 'Value' => array( + 'spouse' => 'spouse', + ), + ), + 'Website' => array( + 'Value' => array( + 'profile' => 'website:profile', + 'blog' => 'website:blog', + 'homepage' => 'website:homepage', + 'work' => 'website:work', + ), + ), + 'Address' => array( + 'Street' => array( + 'home' => 'street:home', + 'work' => 'street:work', + ), + 'City' => array( + 'home' => 'locality:home', + 'work' => 'locality:work', + ), + 'Region' => array( + 'home' => 'region:home', + 'work' => 'region:work', + ), + 'Postal Code' => array( + 'home' => 'zipcode:home', + 'work' => 'zipcode:work', + ), + 'Country' => array( + 'home' => 'country:home', + 'work' => 'country:work', + ), + ), + 'Organization' => array( + 'Name' => array( + '' => 'organization', + ), + 'Title' => array( + '' => 'jobtitle', + ), + 'Department' => array( + '' => 'department', + ), + ), ); + protected $local_label_map = array(); - protected $vcards = array(); - protected $map = array(); + protected $vcards = array(); + protected $map = array(); + protected $gmail_map = array(); /** @@ -308,16 +398,24 @@ class rcube_csv2vcard public function import($csv) { // convert to UTF-8 - $head = substr($csv, 0, 4096); - $charset = rcube_charset::detect($head, RCUBE_CHARSET); - $csv = rcube_charset::convert($csv, $charset); - $head = ''; + $head = substr($csv, 0, 4096); + $charset = rcube_charset::detect($head, RCUBE_CHARSET); + $csv = rcube_charset::convert($csv, $charset); + $csv = preg_replace(array('/^[\xFE\xFF]{2}/', '/^\xEF\xBB\xBF/', '/^\x00+/'), '', $csv); // also remove BOM + $head = ''; + $prev_line = false; - $this->map = array(); + $this->map = array(); + $this->gmail_map = array(); // Parse file foreach (preg_split("/[\r\n]+/", $csv) as $line) { + if (!empty($prev_line)) { + $line = '"' . $line; + } + $elements = $this->parse_line($line); + if (empty($elements)) { continue; } @@ -331,7 +429,28 @@ class rcube_csv2vcard } // Parse data row else { + // handle multiline elements (e.g. Gmail) + if (!empty($prev_line)) { + $first = array_shift($elements); + + if ($first[0] == '"') { + $prev_line[count($prev_line)-1] = '"' . $prev_line[count($prev_line)-1] . "\n" . substr($first, 1); + } + else { + $prev_line[count($prev_line)-1] .= "\n" . $first; + } + + $elements = array_merge($prev_line, $elements); + } + + $last_element = $elements[count($elements)-1]; + if ($last_element[0] == '"') { + $elements[count($elements)-1] = substr($last_element, 1); + $prev_line = $elements; + continue; + } $this->csv_to_vcard($elements); + $prev_line = false; } } } @@ -389,6 +508,7 @@ class rcube_csv2vcard $map1[$i] = $this->csv2vcard_map[$label]; } } + // check localized labels if (!empty($this->local_label_map)) { for ($i = 0; $i < $size; $i++) { @@ -406,6 +526,22 @@ class rcube_csv2vcard } $this->map = count($map1) >= count($map2) ? $map1 : $map2; + + // support special Gmail format + foreach ($this->gmail_label_map as $key => $items) { + $num = 1; + while (($_key = "$key $num - Type") && ($found = array_search($_key, $elements)) !== false) { + $this->gmail_map["$key:$num"] = array('_key' => $key, '_idx' => $found); + foreach (array_keys($items) as $item_key) { + $_key = "$key $num - $item_key"; + if (($found = array_search($_key, $elements)) !== false) { + $this->gmail_map["$key:$num"][$item_key] = $found; + } + } + + $num++; + } + } } /** @@ -421,6 +557,22 @@ class rcube_csv2vcard } } + // Gmail format support + foreach ($this->gmail_map as $idx => $item) { + $type = preg_replace('/[^a-z]/', '', strtolower($data[$item['_idx']])); + $key = $item['_key']; + + unset($item['_idx']); + unset($item['_key']); + + foreach ($item as $item_key => $item_idx) { + $value = $data[$item_idx]; + if ($value !== null && $value !== '' && ($data_idx = $this->gmail_label_map[$key][$item_key][$type])) { + $contact[$data_idx] = $value; + } + } + } + if (empty($contact)) { return; } @@ -430,9 +582,14 @@ class rcube_csv2vcard $contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; } - // categories/groups separator in vCard is ',' not ';' if (!empty($contact['groups'])) { + // categories/groups separator in vCard is ',' not ';' $contact['groups'] = str_replace(';', ',', $contact['groups']); + + // remove "* " added by GMail + if (!empty($this->gmail_map)) { + $contact['groups'] = str_replace('* ', '', $contact['groups']); + } } // Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" -- cgit v1.2.3