2222 */
2323class 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