| | |
| | | //'business_street_2' => '', |
| | | //'business_street_3' => '', |
| | | 'car_phone' => 'phone:car', |
| | | 'categories' => 'categories', |
| | | 'categories' => 'groups', |
| | | //'children' => '', |
| | | 'company' => 'organization', |
| | | //'company_main_phone' => '', |
| | | 'department' => 'department', |
| | | //'email_2_address' => '', //@TODO |
| | | 'email_2_address' => 'email:other', |
| | | //'email_2_type' => '', |
| | | //'email_3_address' => '', //@TODO |
| | | 'email_3_address' => 'email:other', |
| | | //'email_3_type' => '', |
| | | 'email_address' => 'email:main', |
| | | 'email_address' => 'email:pref', |
| | | //'email_type' => '', |
| | | 'first_name' => 'firstname', |
| | | 'gender' => 'gender', |
| | |
| | | 'work_state' => 'region:work', |
| | | 'home_city_short' => 'locality:home', |
| | | 'home_state_short' => 'region:home', |
| | | |
| | | // Atmail |
| | | 'date_of_birth' => 'birthday', |
| | | 'email' => 'email:pref', |
| | | 'home_mobile' => 'phone:cell', |
| | | 'home_zip' => 'zipcode:home', |
| | | 'info' => 'notes', |
| | | 'user_photo' => 'photo', |
| | | 'url' => 'website:homepage', |
| | | 'work_company' => 'organization', |
| | | 'work_dept' => 'departament', |
| | | 'work_fax' => 'phone:work,fax', |
| | | 'work_mobile' => 'phone:work,cell', |
| | | 'work_title' => 'jobtitle', |
| | | 'work_zip' => 'zipcode:work', |
| | | 'group' => 'groups', |
| | | |
| | | // GMail |
| | | 'groups' => 'groups', |
| | | 'group_membership' => 'groups', |
| | | 'given_name' => 'firstname', |
| | | 'additional_name' => 'middlename', |
| | | 'family_name' => 'surname', |
| | | 'name' => 'displayname', |
| | | 'name_prefix' => 'prefix', |
| | | 'name_suffix' => 'suffix', |
| | | ); |
| | | |
| | | /** |
| | |
| | | //'company_main_phone' => "Company Main Phone", |
| | | 'department' => "Department", |
| | | //'directory_server' => "Directory Server", |
| | | //'email_2_address' => "E-mail 2 Address", |
| | | 'email_2_address' => "E-mail 2 Address", |
| | | //'email_2_type' => "E-mail 2 Type", |
| | | //'email_3_address' => "E-mail 3 Address", |
| | | 'email_3_address' => "E-mail 3 Address", |
| | | //'email_3_type' => "E-mail 3 Type", |
| | | 'email_address' => "E-mail Address", |
| | | //'email_type' => "E-mail Type", |
| | |
| | | 'work_phone' => "Work Phone", |
| | | 'work_address' => "Work Address", |
| | | //'work_address_2' => "Work Address 2", |
| | | 'work_city' => "Work City", |
| | | 'work_country' => "Work Country", |
| | | 'work_state' => "Work State", |
| | | 'work_zipcode' => "Work ZipCode", |
| | | |
| | | // Atmail |
| | | 'date_of_birth' => "Date of Birth", |
| | | 'email' => "Email", |
| | | //'email_2' => "Email2", |
| | | //'email_3' => "Email3", |
| | | //'email_4' => "Email4", |
| | | //'email_5' => "Email5", |
| | | 'home_mobile' => "Home Mobile", |
| | | 'home_zip' => "Home Zip", |
| | | 'info' => "Info", |
| | | 'user_photo' => "User Photo", |
| | | 'url' => "URL", |
| | | 'work_company' => "Work Company", |
| | | 'work_dept' => "Work Dept", |
| | | 'work_fax' => "Work Fax", |
| | | 'work_mobile' => "Work Mobile", |
| | | 'work_title' => "Work Title", |
| | | 'work_zip' => "Work Zip", |
| | | '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', |
| | | '*' => 'email:other', |
| | | ), |
| | | ), |
| | | '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(); |
| | | |
| | | |
| | | /** |
| | |
| | | public function import($csv) |
| | | { |
| | | // convert to UTF-8 |
| | | $head = substr($csv, 0, 4096); |
| | | $fallback = rcube::get_instance()->config->get('default_charset', 'ISO-8859-1'); // fallback to Latin-1? |
| | | $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 $i => $line) { |
| | | foreach (preg_split("/[\r\n]+/", $csv) as $line) { |
| | | if (!empty($prev_line)) { |
| | | $line = '"' . $line; |
| | | } |
| | | |
| | | $elements = $this->parse_line($line); |
| | | |
| | | if (empty($elements)) { |
| | | continue; |
| | | } |
| | |
| | | } |
| | | // 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; |
| | | } |
| | | } |
| | | } |
| | |
| | | $map1[$i] = $this->csv2vcard_map[$label]; |
| | | } |
| | | } |
| | | |
| | | // check localized labels |
| | | if (!empty($this->local_label_map)) { |
| | | for ($i = 0; $i < $size; $i++) { |
| | | $label = $this->local_label_map[$elements[$i]]; |
| | | |
| | | // special localization label |
| | | if ($label && $label[0] == '_') { |
| | | $label = substr($label, 1); |
| | | } |
| | | |
| | | if ($label && !empty($this->csv2vcard_map[$label])) { |
| | | $map2[$i] = $this->csv2vcard_map[$label]; |
| | | } |
| | |
| | | } |
| | | |
| | | $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++; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | foreach ($this->map as $idx => $name) { |
| | | $value = $data[$idx]; |
| | | if ($value !== null && $value !== '') { |
| | | $contact[$name] = $value; |
| | | if (!empty($contact[$name])) { |
| | | $contact[$name] = (array) $contact[$name]; |
| | | $contact[$name][] = $value; |
| | | } |
| | | else { |
| | | $contact[$name] = $value; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 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 !== '') { |
| | | foreach (array($type, '*') as $_type) { |
| | | if ($data_idx = $this->gmail_label_map[$key][$item_key][$_type]) { |
| | | $value = explode(' ::: ', $value); |
| | | |
| | | if (!empty($contact[$data_idx])) { |
| | | $contact[$data_idx] = array_merge((array) $contact[$data_idx], $value); |
| | | } |
| | | else { |
| | | $contact[$data_idx] = $value; |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | $contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; |
| | | } |
| | | |
| | | if (!empty($contact['groups'])) { |
| | | // categories/groups separator in vCard is ',' not ';' |
| | | $contact['groups'] = str_replace(',', '', $contact['groups']); |
| | | $contact['groups'] = str_replace(';', ',', $contact['groups']); |
| | | |
| | | if (!empty($this->gmail_map)) { |
| | | // remove "* " added by GMail |
| | | $contact['groups'] = str_replace('* ', '', $contact['groups']); |
| | | // replace strange delimiter |
| | | $contact['groups'] = str_replace(' ::: ', ',', $contact['groups']); |
| | | } |
| | | } |
| | | |
| | | // Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" |
| | | foreach (array('birthday', 'anniversary') as $key) { |
| | | if (!empty($contact[$key]) && $contact[$key] == '0/0/00') { // @TODO: localization? |
| | | unset($contact[$key]); |
| | | if (!empty($contact[$key])) { |
| | | $date = preg_replace('/[0[:^word:]]/', '', $contact[$key]); |
| | | if (empty($date)) { |
| | | unset($contact[$key]); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | $vcard = new rcube_vcard(); |
| | | foreach ($contact as $name => $value) { |
| | | $name = explode(':', $name); |
| | | $vcard->set($name[0], $value, $name[1]); |
| | | if (is_array($value) && $name[0] != 'address') { |
| | | foreach ((array) $value as $val) { |
| | | $vcard->set($name[0], $val, $name[1]); |
| | | } |
| | | } |
| | | else { |
| | | $vcard->set($name[0], $value, $name[1]); |
| | | } |
| | | } |
| | | |
| | | // add to the list |