Skip to content

Commit 2be7359

Browse files
committed
use single field map for csv2vcard imports, remove hardcoded version
1 parent 7599443 commit 2be7359

File tree

14 files changed

+850
-1031
lines changed

14 files changed

+850
-1031
lines changed

program/actions/contacts/import.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function run($args = [])
101101
$map = rcube_utils::get_input_value('_map', rcube_utils::INPUT_GPC);
102102
$map = array_filter($map);
103103

104-
$csv->set_map($map);
104+
$csv->set_map($map, array_keys(self::list_fields($with_groups)));
105105
$csv->import($file_content, false, $skip_head);
106106

107107
unlink($filepath);

program/lib/Roundcube/rcube_csv2vcard.php

Lines changed: 29 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -22,169 +22,6 @@
2222
*/
2323
class rcube_csv2vcard
2424
{
25-
/**
26-
* CSV to vCard fields mapping
27-
*
28-
* @var array
29-
*/
30-
protected $csv2vcard_map = [
31-
// MS Outlook 2010
32-
'anniversary' => 'anniversary',
33-
'assistants_name' => 'assistant',
34-
'assistants_phone' => 'phone:assistant',
35-
'birthday' => 'birthday',
36-
'business_city' => 'locality:work',
37-
'business_countryregion' => 'country:work',
38-
'business_fax' => 'phone:workfax',
39-
'business_phone' => 'phone:work',
40-
'business_phone_2' => 'phone:work2',
41-
'business_postal_code' => 'zipcode:work',
42-
'business_state' => 'region:work',
43-
'business_street' => 'street:work',
44-
// 'business_street_2' => '',
45-
// 'business_street_3' => '',
46-
'car_phone' => 'phone:car',
47-
'categories' => 'groups',
48-
// 'children' => '',
49-
'company' => 'organization',
50-
// 'company_main_phone' => '',
51-
'department' => 'department',
52-
'email_2_address' => 'email:other',
53-
// 'email_2_type' => '',
54-
'email_3_address' => 'email:other',
55-
// 'email_3_type' => '',
56-
'email_address' => 'email:other',
57-
// 'email_type' => '',
58-
'first_name' => 'firstname',
59-
'gender' => 'gender',
60-
'home_city' => 'locality:home',
61-
'home_countryregion' => 'country:home',
62-
'home_fax' => 'phone:homefax',
63-
'home_phone' => 'phone:home',
64-
'home_phone_2' => 'phone:home2',
65-
'home_postal_code' => 'zipcode:home',
66-
'home_state' => 'region:home',
67-
'home_street' => 'street:home',
68-
// 'home_street_2' => '',
69-
// 'home_street_3' => '',
70-
// 'initials' => '',
71-
// 'isdn' => '',
72-
'job_title' => 'jobtitle',
73-
// 'keywords' => '',
74-
// 'language' => '',
75-
'last_name' => 'surname',
76-
// 'location' => '',
77-
'managers_name' => 'manager',
78-
'middle_name' => 'middlename',
79-
// 'mileage' => '',
80-
'mobile_phone' => 'phone:mobile',
81-
'notes' => 'notes',
82-
// 'office_location' => '',
83-
'other_city' => 'locality:other',
84-
'other_countryregion' => 'country:other',
85-
'other_fax' => 'phone:other',
86-
'other_phone' => 'phone:other',
87-
'other_postal_code' => 'zipcode:other',
88-
'other_state' => 'region:other',
89-
'other_street' => 'street:other',
90-
// 'other_street_2' => '',
91-
// 'other_street_3' => '',
92-
'pager' => 'phone:pager',
93-
'primary_phone' => 'phone:main',
94-
// 'profession' => '',
95-
// 'radio_phone' => '',
96-
'spouse' => 'spouse',
97-
'suffix' => 'suffix',
98-
'title' => 'prefix',
99-
'web_page' => 'website:homepage',
100-
101-
// Thunderbird
102-
'birth_day' => 'birthday-d',
103-
'birth_month' => 'birthday-m',
104-
'birth_year' => 'birthday-y',
105-
'display_name' => 'name',
106-
'fax_number' => 'phone:homefax',
107-
'home_address' => 'street:home',
108-
// 'home_address_2' => '',
109-
'home_country' => 'country:home',
110-
'home_zipcode' => 'zipcode:home',
111-
'mobile_number' => 'phone:mobile',
112-
'nickname' => 'nickname',
113-
'organization' => 'organization',
114-
'pager_number' => 'phone:pager',
115-
'primary_email' => 'email:home',
116-
'secondary_email' => 'email:other',
117-
'web_page_1' => 'website:homepage',
118-
'web_page_2' => 'website:other',
119-
'work_phone' => 'phone:work',
120-
'work_address' => 'street:work',
121-
// 'work_address_2' => '',
122-
'work_country' => 'country:work',
123-
'work_zipcode' => 'zipcode:work',
124-
'last' => 'surname',
125-
'first' => 'firstname',
126-
'work_city' => 'locality:work',
127-
'work_state' => 'region:work',
128-
'home_city_short' => 'locality:home',
129-
'home_state_short' => 'region:home',
130-
131-
// Atmail
132-
'date_of_birth' => 'birthday',
133-
// 'email' => 'email:pref',
134-
'home_mobile' => 'phone:mobile',
135-
'home_zip' => 'zipcode:home',
136-
'info' => 'notes',
137-
'user_photo' => 'photo',
138-
'url' => 'website:homepage',
139-
'work_company' => 'organization',
140-
'work_dept' => 'department',
141-
'work_fax' => 'phone:workfax',
142-
'work_mobile' => 'phone:other',
143-
'work_title' => 'jobtitle',
144-
'work_zip' => 'zipcode:work',
145-
'group' => 'groups',
146-
147-
// GMail
148-
'groups' => 'groups',
149-
'group_membership' => 'groups',
150-
'given_name' => 'firstname',
151-
'additional_name' => 'middlename',
152-
'family_name' => 'surname',
153-
'name' => 'name',
154-
'name_prefix' => 'prefix',
155-
'name_suffix' => 'suffix',
156-
157-
// Format of Letter Hub test files from
158-
// https://letterhub.com/sample-csv-file-with-contacts/
159-
'company_name' => 'organization',
160-
'address' => 'street:home',
161-
'city' => 'locality:home',
162-
// 'county' => '',
163-
'state' => 'region:home',
164-
'zip' => 'zipcode:home',
165-
'phone1' => 'phone:home',
166-
'phone' => 'phone:work',
167-
'email' => 'email:home',
168-
169-
// roundcube fields
170-
'email_home' => 'email:home',
171-
'email_work' => 'email:work',
172-
'email_other' => 'email:other',
173-
'phone_video' => 'phone:video',
174-
'maidenname' => 'maidenname',
175-
'im_aim' => 'im:aim',
176-
'im_icq' => 'im:icq',
177-
'im_jabber' => 'im:jabber',
178-
'im_msn' => 'im:msn',
179-
'im_skype' => 'im:skype',
180-
'im_yahoo' => 'im:yahoo',
181-
'web_blog' => 'website:blog',
182-
'web_home' => 'website:homepage',
183-
'web_other' => 'website:other',
184-
'web_profile' => 'website:profile',
185-
'web_work' => 'website:work',
186-
];
187-
18825
/**
18926
* CSV label to text mapping for English read from localization
19027
*
@@ -265,9 +102,6 @@ class rcube_csv2vcard
265102
],
266103
];
267104

268-
/** @var array Localized labels map */
269-
protected $local_label_map = [];
270-
271105
/** @var rcube_vcard[] List of contacts as vCards */
272106
protected $vcards = [];
273107

@@ -288,7 +122,7 @@ public function __construct($lang = 'en_US')
288122
$map = self::read_localization_file(RCUBE_LOCALIZATION_DIR . $lang . '/csv2vcard.inc');
289123

290124
if (!empty($map)) {
291-
$this->local_label_map = array_merge($this->label_map, $map);
125+
$this->label_map = array_merge_recursive($this->label_map, $map);
292126
}
293127
}
294128
}
@@ -347,11 +181,11 @@ public function import($csv, $dry_run = false, $skip_head = true)
347181
*
348182
* @param array $elements Field mapping
349183
*/
350-
public function set_map($elements)
184+
public function set_map($elements, $available)
351185
{
352186
// sanitize input
353-
$elements = array_filter($elements, function ($val) {
354-
return in_array($val, $this->csv2vcard_map);
187+
$elements = array_filter($elements, function ($val) use ($available) {
188+
return in_array($val, $available);
355189
});
356190

357191
$this->map = $elements;
@@ -395,56 +229,20 @@ protected function parse_header($lines)
395229
{
396230
$elements = $this->parse_line($lines[0]);
397231

398-
$label_map = array_flip($this->label_map);
399-
$local_label_map = array_flip($this->local_label_map);
400-
401232
if (count($lines) == 2) {
402233
// first line of contents needed to properly identify fields in gmail CSV
403234
$contents = $this->parse_line($lines[1]);
404235
}
405236

406-
$map1 = [];
407-
$map2 = [];
408237
$size = count($elements);
409238

410-
// check English labels
239+
// check labels
411240
for ($i = 0; $i < $size; $i++) {
412-
if (!empty($label_map[$elements[$i]])) {
413-
$label = $label_map[$elements[$i]];
414-
if (!empty($this->csv2vcard_map[$label])) {
415-
$map1[$i] = $this->csv2vcard_map[$label];
416-
}
417-
}
418-
}
419-
420-
// check localized labels
421-
if (!empty($local_label_map)) {
422-
for ($i = 0; $i < $size; $i++) {
423-
$label = $local_label_map[$elements[$i]];
424-
425-
// special localization label
426-
if ($label && $label[0] == '_') {
427-
$label = substr($label, 1);
428-
}
429-
430-
if ($label && !empty($this->csv2vcard_map[$label])) {
431-
$map2[$i] = $this->csv2vcard_map[$label];
432-
}
241+
if ($field = self::search_map($elements[$i], $this->label_map)) {
242+
$this->map[$i] = $field;
433243
}
434244
}
435245

436-
// If nothing recognized fallback to simple non-localized labels
437-
if (empty($map1) && empty($map2)) {
438-
for ($i = 0; $i < $size; $i++) {
439-
$label = str_replace(' ', '_', strtolower($elements[$i]));
440-
if (!empty($this->csv2vcard_map[$label])) {
441-
$map1[$i] = $this->csv2vcard_map[$label];
442-
}
443-
}
444-
}
445-
446-
$this->map = count($map1) >= count($map2) ? $map1 : $map2;
447-
448246
if (!empty($contents)) {
449247
foreach ($this->gmail_label_map as $key => $items) {
450248
$num = 1;
@@ -572,10 +370,31 @@ protected static function read_localization_file($file, $texts = [])
572370

573371
// @phpstan-ignore-next-line
574372
if (!empty($map)) {
575-
$texts = array_merge($texts, $map);
373+
$texts = array_merge_recursive($texts, $map);
576374
}
577375
}
578376

579377
return $texts;
580378
}
379+
380+
/**
381+
* Search csv2vcard mapping array
382+
*
383+
* @param string $needle Field name to search for
384+
* @param array $map Field map to be searched
385+
*
386+
* @return string|null vcard field id
387+
*/
388+
protected static function search_map($needle, $map)
389+
{
390+
$result = null;
391+
foreach ($map as $key => $headings) {
392+
if (array_search($needle, $headings) !== false) {
393+
$result = $key;
394+
break;
395+
}
396+
}
397+
398+
return $result;
399+
}
581400
}

0 commit comments

Comments
 (0)