|
|
|
@ -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"
|
|
|
|
|