commit | author | age
|
383379
|
1 |
<?php |
AM |
2 |
|
a95874
|
3 |
/** |
383379
|
4 |
+-----------------------------------------------------------------------+ |
AM |
5 |
| This file is part of the Roundcube Webmail client | |
|
6 |
| Copyright (C) 2008-2012, The Roundcube Dev Team | |
|
7 |
| | |
|
8 |
| Licensed under the GNU General Public License version 3 or | |
|
9 |
| any later version with exceptions for skins & plugins. | |
|
10 |
| See the README file for a full license statement. | |
|
11 |
| | |
|
12 |
| PURPOSE: | |
|
13 |
| CSV to vCard data conversion | |
|
14 |
+-----------------------------------------------------------------------+ |
|
15 |
| Author: Aleksander Machniak <alec@alec.pl> | |
|
16 |
+-----------------------------------------------------------------------+ |
|
17 |
*/ |
|
18 |
|
|
19 |
/** |
|
20 |
* CSV to vCard data converter |
|
21 |
* |
9ab346
|
22 |
* @package Framework |
AM |
23 |
* @subpackage Addressbook |
|
24 |
* @author Aleksander Machniak <alec@alec.pl> |
383379
|
25 |
*/ |
AM |
26 |
class rcube_csv2vcard |
|
27 |
{ |
|
28 |
/** |
|
29 |
* CSV to vCard fields mapping |
|
30 |
* |
|
31 |
* @var array |
|
32 |
*/ |
|
33 |
protected $csv2vcard_map = array( |
|
34 |
// MS Outlook 2010 |
|
35 |
'anniversary' => 'anniversary', |
|
36 |
'assistants_name' => 'assistant', |
|
37 |
'assistants_phone' => 'phone:assistant', |
|
38 |
'birthday' => 'birthday', |
|
39 |
'business_city' => 'locality:work', |
|
40 |
'business_countryregion' => 'country:work', |
|
41 |
'business_fax' => 'phone:work,fax', |
|
42 |
'business_phone' => 'phone:work', |
|
43 |
'business_phone_2' => 'phone:work2', |
|
44 |
'business_postal_code' => 'zipcode:work', |
|
45 |
'business_state' => 'region:work', |
|
46 |
'business_street' => 'street:work', |
|
47 |
//'business_street_2' => '', |
|
48 |
//'business_street_3' => '', |
|
49 |
'car_phone' => 'phone:car', |
5983ee
|
50 |
'categories' => 'groups', |
383379
|
51 |
//'children' => '', |
AM |
52 |
'company' => 'organization', |
|
53 |
//'company_main_phone' => '', |
|
54 |
'department' => 'department', |
f86449
|
55 |
'email_2_address' => 'email:other', |
c66b60
|
56 |
//'email_2_type' => '', |
f86449
|
57 |
'email_3_address' => 'email:other', |
c66b60
|
58 |
//'email_3_type' => '', |
b58abd
|
59 |
'email_address' => 'email:pref', |
c66b60
|
60 |
//'email_type' => '', |
383379
|
61 |
'first_name' => 'firstname', |
AM |
62 |
'gender' => 'gender', |
|
63 |
'home_city' => 'locality:home', |
|
64 |
'home_countryregion' => 'country:home', |
|
65 |
'home_fax' => 'phone:home,fax', |
|
66 |
'home_phone' => 'phone:home', |
|
67 |
'home_phone_2' => 'phone:home2', |
|
68 |
'home_postal_code' => 'zipcode:home', |
|
69 |
'home_state' => 'region:home', |
|
70 |
'home_street' => 'street:home', |
|
71 |
//'home_street_2' => '', |
|
72 |
//'home_street_3' => '', |
|
73 |
//'initials' => '', |
|
74 |
//'isdn' => '', |
|
75 |
'job_title' => 'jobtitle', |
|
76 |
//'keywords' => '', |
|
77 |
//'language' => '', |
|
78 |
'last_name' => 'surname', |
|
79 |
//'location' => '', |
|
80 |
'managers_name' => 'manager', |
|
81 |
'middle_name' => 'middlename', |
|
82 |
//'mileage' => '', |
|
83 |
'mobile_phone' => 'phone:cell', |
|
84 |
'notes' => 'notes', |
|
85 |
//'office_location' => '', |
|
86 |
'other_city' => 'locality:other', |
|
87 |
'other_countryregion' => 'country:other', |
|
88 |
'other_fax' => 'phone:other,fax', |
|
89 |
'other_phone' => 'phone:other', |
|
90 |
'other_postal_code' => 'zipcode:other', |
|
91 |
'other_state' => 'region:other', |
|
92 |
'other_street' => 'street:other', |
|
93 |
//'other_street_2' => '', |
|
94 |
//'other_street_3' => '', |
|
95 |
'pager' => 'phone:pager', |
|
96 |
'primary_phone' => 'phone:pref', |
|
97 |
//'profession' => '', |
|
98 |
//'radio_phone' => '', |
|
99 |
'spouse' => 'spouse', |
|
100 |
'suffix' => 'suffix', |
|
101 |
'title' => 'title', |
|
102 |
'web_page' => 'website:homepage', |
|
103 |
|
|
104 |
// Thunderbird |
|
105 |
'birth_day' => 'birthday-d', |
|
106 |
'birth_month' => 'birthday-m', |
|
107 |
'birth_year' => 'birthday-y', |
|
108 |
'display_name' => 'displayname', |
|
109 |
'fax_number' => 'phone:fax', |
|
110 |
'home_address' => 'street:home', |
|
111 |
//'home_address_2' => '', |
|
112 |
'home_country' => 'country:home', |
|
113 |
'home_zipcode' => 'zipcode:home', |
|
114 |
'mobile_number' => 'phone:cell', |
|
115 |
'nickname' => 'nickname', |
|
116 |
'organization' => 'organization', |
|
117 |
'pager_number' => 'phone:pager', |
|
118 |
'primary_email' => 'email:pref', |
|
119 |
'secondary_email' => 'email:other', |
|
120 |
'web_page_1' => 'website:homepage', |
|
121 |
'web_page_2' => 'website:other', |
|
122 |
'work_phone' => 'phone:work', |
|
123 |
'work_address' => 'street:work', |
|
124 |
//'work_address_2' => '', |
|
125 |
'work_country' => 'country:work', |
|
126 |
'work_zipcode' => 'zipcode:work', |
c59ef9
|
127 |
'last' => 'surname', |
AM |
128 |
'first' => 'firstname', |
|
129 |
'work_city' => 'locality:work', |
|
130 |
'work_state' => 'region:work', |
|
131 |
'home_city_short' => 'locality:home', |
|
132 |
'home_state_short' => 'region:home', |
609483
|
133 |
|
AM |
134 |
// Atmail |
|
135 |
'date_of_birth' => 'birthday', |
|
136 |
'email' => 'email:pref', |
|
137 |
'home_mobile' => 'phone:cell', |
|
138 |
'home_zip' => 'zipcode:home', |
|
139 |
'info' => 'notes', |
|
140 |
'user_photo' => 'photo', |
|
141 |
'url' => 'website:homepage', |
|
142 |
'work_company' => 'organization', |
|
143 |
'work_dept' => 'departament', |
|
144 |
'work_fax' => 'phone:work,fax', |
|
145 |
'work_mobile' => 'phone:work,cell', |
|
146 |
'work_title' => 'jobtitle', |
|
147 |
'work_zip' => 'zipcode:work', |
027208
|
148 |
'group' => 'groups', |
5983ee
|
149 |
|
AM |
150 |
// GMail |
|
151 |
'groups' => 'groups', |
5f1765
|
152 |
'group_membership' => 'groups', |
AM |
153 |
'given_name' => 'firstname', |
|
154 |
'additional_name' => 'middlename', |
|
155 |
'family_name' => 'surname', |
|
156 |
'name' => 'displayname', |
|
157 |
'name_prefix' => 'prefix', |
|
158 |
'name_suffix' => 'suffix', |
383379
|
159 |
); |
AM |
160 |
|
|
161 |
/** |
|
162 |
* CSV label to text mapping for English |
|
163 |
* |
|
164 |
* @var array |
|
165 |
*/ |
|
166 |
protected $label_map = array( |
|
167 |
// MS Outlook 2010 |
|
168 |
'anniversary' => "Anniversary", |
|
169 |
'assistants_name' => "Assistant's Name", |
|
170 |
'assistants_phone' => "Assistant's Phone", |
|
171 |
'birthday' => "Birthday", |
|
172 |
'business_city' => "Business City", |
|
173 |
'business_countryregion' => "Business Country/Region", |
|
174 |
'business_fax' => "Business Fax", |
|
175 |
'business_phone' => "Business Phone", |
|
176 |
'business_phone_2' => "Business Phone 2", |
|
177 |
'business_postal_code' => "Business Postal Code", |
|
178 |
'business_state' => "Business State", |
|
179 |
'business_street' => "Business Street", |
|
180 |
//'business_street_2' => "Business Street 2", |
|
181 |
//'business_street_3' => "Business Street 3", |
|
182 |
'car_phone' => "Car Phone", |
|
183 |
'categories' => "Categories", |
|
184 |
//'children' => "Children", |
|
185 |
'company' => "Company", |
|
186 |
//'company_main_phone' => "Company Main Phone", |
|
187 |
'department' => "Department", |
|
188 |
//'directory_server' => "Directory Server", |
f86449
|
189 |
'email_2_address' => "E-mail 2 Address", |
c66b60
|
190 |
//'email_2_type' => "E-mail 2 Type", |
f86449
|
191 |
'email_3_address' => "E-mail 3 Address", |
c66b60
|
192 |
//'email_3_type' => "E-mail 3 Type", |
383379
|
193 |
'email_address' => "E-mail Address", |
c66b60
|
194 |
//'email_type' => "E-mail Type", |
383379
|
195 |
'first_name' => "First Name", |
AM |
196 |
'gender' => "Gender", |
|
197 |
'home_city' => "Home City", |
|
198 |
'home_countryregion' => "Home Country/Region", |
|
199 |
'home_fax' => "Home Fax", |
|
200 |
'home_phone' => "Home Phone", |
|
201 |
'home_phone_2' => "Home Phone 2", |
|
202 |
'home_postal_code' => "Home Postal Code", |
|
203 |
'home_state' => "Home State", |
|
204 |
'home_street' => "Home Street", |
|
205 |
//'home_street_2' => "Home Street 2", |
|
206 |
//'home_street_3' => "Home Street 3", |
|
207 |
//'initials' => "Initials", |
|
208 |
//'isdn' => "ISDN", |
|
209 |
'job_title' => "Job Title", |
|
210 |
//'keywords' => "Keywords", |
|
211 |
//'language' => "Language", |
|
212 |
'last_name' => "Last Name", |
|
213 |
//'location' => "Location", |
|
214 |
'managers_name' => "Manager's Name", |
|
215 |
'middle_name' => "Middle Name", |
|
216 |
//'mileage' => "Mileage", |
|
217 |
'mobile_phone' => "Mobile Phone", |
|
218 |
'notes' => "Notes", |
|
219 |
//'office_location' => "Office Location", |
|
220 |
'other_city' => "Other City", |
|
221 |
'other_countryregion' => "Other Country/Region", |
|
222 |
'other_fax' => "Other Fax", |
|
223 |
'other_phone' => "Other Phone", |
|
224 |
'other_postal_code' => "Other Postal Code", |
|
225 |
'other_state' => "Other State", |
|
226 |
'other_street' => "Other Street", |
|
227 |
//'other_street_2' => "Other Street 2", |
|
228 |
//'other_street_3' => "Other Street 3", |
|
229 |
'pager' => "Pager", |
|
230 |
'primary_phone' => "Primary Phone", |
|
231 |
//'profession' => "Profession", |
|
232 |
//'radio_phone' => "Radio Phone", |
|
233 |
'spouse' => "Spouse", |
|
234 |
'suffix' => "Suffix", |
|
235 |
'title' => "Title", |
|
236 |
'web_page' => "Web Page", |
|
237 |
|
|
238 |
// Thunderbird |
|
239 |
'birth_day' => "Birth Day", |
|
240 |
'birth_month' => "Birth Month", |
|
241 |
'birth_year' => "Birth Year", |
|
242 |
'display_name' => "Display Name", |
|
243 |
'fax_number' => "Fax Number", |
|
244 |
'home_address' => "Home Address", |
|
245 |
//'home_address_2' => "Home Address 2", |
|
246 |
'home_country' => "Home Country", |
|
247 |
'home_zipcode' => "Home ZipCode", |
|
248 |
'mobile_number' => "Mobile Number", |
|
249 |
'nickname' => "Nickname", |
|
250 |
'organization' => "Organization", |
|
251 |
'pager_number' => "Pager Namber", |
|
252 |
'primary_email' => "Primary Email", |
|
253 |
'secondary_email' => "Secondary Email", |
|
254 |
'web_page_1' => "Web Page 1", |
|
255 |
'web_page_2' => "Web Page 2", |
|
256 |
'work_phone' => "Work Phone", |
|
257 |
'work_address' => "Work Address", |
|
258 |
//'work_address_2' => "Work Address 2", |
38c19a
|
259 |
'work_city' => "Work City", |
383379
|
260 |
'work_country' => "Work Country", |
38c19a
|
261 |
'work_state' => "Work State", |
383379
|
262 |
'work_zipcode' => "Work ZipCode", |
609483
|
263 |
|
AM |
264 |
// Atmail |
|
265 |
'date_of_birth' => "Date of Birth", |
|
266 |
'email' => "Email", |
|
267 |
//'email_2' => "Email2", |
|
268 |
//'email_3' => "Email3", |
|
269 |
//'email_4' => "Email4", |
|
270 |
//'email_5' => "Email5", |
|
271 |
'home_mobile' => "Home Mobile", |
|
272 |
'home_zip' => "Home Zip", |
|
273 |
'info' => "Info", |
|
274 |
'user_photo' => "User Photo", |
|
275 |
'url' => "URL", |
|
276 |
'work_company' => "Work Company", |
|
277 |
'work_dept' => "Work Dept", |
|
278 |
'work_fax' => "Work Fax", |
|
279 |
'work_mobile' => "Work Mobile", |
|
280 |
'work_title' => "Work Title", |
|
281 |
'work_zip' => "Work Zip", |
5f1765
|
282 |
'group' => "Group", |
AM |
283 |
|
|
284 |
// GMail |
|
285 |
'groups' => "Groups", |
|
286 |
'group_membership' => "Group Membership", |
|
287 |
'given_name' => "Given Name", |
|
288 |
'additional_name' => "Additional Name", |
|
289 |
'family_name' => "Family Name", |
|
290 |
'name' => "Name", |
|
291 |
'name_prefix' => "Name Prefix", |
|
292 |
'name_suffix' => "Name Suffix", |
383379
|
293 |
); |
AM |
294 |
|
5f1765
|
295 |
/** |
AM |
296 |
* Special fields map for GMail format |
|
297 |
* |
|
298 |
* @var array |
|
299 |
*/ |
|
300 |
protected $gmail_label_map = array( |
|
301 |
'E-mail' => array( |
|
302 |
'Value' => array( |
|
303 |
'home' => 'email:home', |
|
304 |
'work' => 'email:work', |
25fb97
|
305 |
'*' => 'email:other', |
5f1765
|
306 |
), |
AM |
307 |
), |
|
308 |
'Phone' => array( |
|
309 |
'Value' => array( |
|
310 |
'home' => 'phone:home', |
|
311 |
'homefax' => 'phone:homefax', |
|
312 |
'main' => 'phone:pref', |
|
313 |
'pager' => 'phone:pager', |
|
314 |
'mobile' => 'phone:cell', |
|
315 |
'work' => 'phone:work', |
|
316 |
'workfax' => 'phone:workfax', |
|
317 |
), |
|
318 |
), |
|
319 |
'Relation' => array( |
|
320 |
'Value' => array( |
|
321 |
'spouse' => 'spouse', |
|
322 |
), |
|
323 |
), |
|
324 |
'Website' => array( |
|
325 |
'Value' => array( |
|
326 |
'profile' => 'website:profile', |
|
327 |
'blog' => 'website:blog', |
|
328 |
'homepage' => 'website:homepage', |
|
329 |
'work' => 'website:work', |
|
330 |
), |
|
331 |
), |
|
332 |
'Address' => array( |
|
333 |
'Street' => array( |
|
334 |
'home' => 'street:home', |
|
335 |
'work' => 'street:work', |
|
336 |
), |
|
337 |
'City' => array( |
|
338 |
'home' => 'locality:home', |
|
339 |
'work' => 'locality:work', |
|
340 |
), |
|
341 |
'Region' => array( |
|
342 |
'home' => 'region:home', |
|
343 |
'work' => 'region:work', |
|
344 |
), |
|
345 |
'Postal Code' => array( |
|
346 |
'home' => 'zipcode:home', |
|
347 |
'work' => 'zipcode:work', |
|
348 |
), |
|
349 |
'Country' => array( |
|
350 |
'home' => 'country:home', |
|
351 |
'work' => 'country:work', |
|
352 |
), |
|
353 |
), |
|
354 |
'Organization' => array( |
|
355 |
'Name' => array( |
|
356 |
'' => 'organization', |
|
357 |
), |
|
358 |
'Title' => array( |
|
359 |
'' => 'jobtitle', |
|
360 |
), |
|
361 |
'Department' => array( |
|
362 |
'' => 'department', |
|
363 |
), |
|
364 |
), |
|
365 |
); |
|
366 |
|
|
367 |
|
92bd3a
|
368 |
protected $local_label_map = array(); |
5f1765
|
369 |
protected $vcards = array(); |
AM |
370 |
protected $map = array(); |
|
371 |
protected $gmail_map = array(); |
383379
|
372 |
|
AM |
373 |
|
|
374 |
/** |
|
375 |
* Class constructor |
|
376 |
* |
|
377 |
* @param string $lang File language |
|
378 |
*/ |
|
379 |
public function __construct($lang = 'en_US') |
|
380 |
{ |
|
381 |
// Localize fields map |
|
382 |
if ($lang && $lang != 'en_US') { |
9be2f4
|
383 |
if (file_exists(RCUBE_LOCALIZATION_DIR . "$lang/csv2vcard.inc")) { |
TB |
384 |
include RCUBE_LOCALIZATION_DIR . "$lang/csv2vcard.inc"; |
383379
|
385 |
} |
AM |
386 |
|
|
387 |
if (!empty($map)) { |
92bd3a
|
388 |
$this->local_label_map = array_merge($this->label_map, $map); |
383379
|
389 |
} |
AM |
390 |
} |
|
391 |
|
|
392 |
$this->label_map = array_flip($this->label_map); |
92bd3a
|
393 |
$this->local_label_map = array_flip($this->local_label_map); |
383379
|
394 |
} |
AM |
395 |
|
|
396 |
/** |
a95874
|
397 |
* Import contacts from CSV file |
383379
|
398 |
* |
a95874
|
399 |
* @param string $csv Content of the CSV file |
383379
|
400 |
*/ |
AM |
401 |
public function import($csv) |
|
402 |
{ |
|
403 |
// convert to UTF-8 |
5f1765
|
404 |
$head = substr($csv, 0, 4096); |
AM |
405 |
$charset = rcube_charset::detect($head, RCUBE_CHARSET); |
|
406 |
$csv = rcube_charset::convert($csv, $charset); |
|
407 |
$csv = preg_replace(array('/^[\xFE\xFF]{2}/', '/^\xEF\xBB\xBF/', '/^\x00+/'), '', $csv); // also remove BOM |
|
408 |
$head = ''; |
|
409 |
$prev_line = false; |
383379
|
410 |
|
5f1765
|
411 |
$this->map = array(); |
AM |
412 |
$this->gmail_map = array(); |
383379
|
413 |
|
AM |
414 |
// Parse file |
3725cf
|
415 |
foreach (preg_split("/[\r\n]+/", $csv) as $line) { |
5f1765
|
416 |
if (!empty($prev_line)) { |
AM |
417 |
$line = '"' . $line; |
|
418 |
} |
|
419 |
|
745d86
|
420 |
$elements = $this->parse_line($line); |
5f1765
|
421 |
|
383379
|
422 |
if (empty($elements)) { |
AM |
423 |
continue; |
|
424 |
} |
|
425 |
|
|
426 |
// Parse header |
|
427 |
if (empty($this->map)) { |
|
428 |
$this->parse_header($elements); |
|
429 |
if (empty($this->map)) { |
|
430 |
break; |
|
431 |
} |
|
432 |
} |
|
433 |
// Parse data row |
|
434 |
else { |
5f1765
|
435 |
// handle multiline elements (e.g. Gmail) |
AM |
436 |
if (!empty($prev_line)) { |
|
437 |
$first = array_shift($elements); |
|
438 |
|
|
439 |
if ($first[0] == '"') { |
|
440 |
$prev_line[count($prev_line)-1] = '"' . $prev_line[count($prev_line)-1] . "\n" . substr($first, 1); |
|
441 |
} |
|
442 |
else { |
|
443 |
$prev_line[count($prev_line)-1] .= "\n" . $first; |
|
444 |
} |
|
445 |
|
|
446 |
$elements = array_merge($prev_line, $elements); |
|
447 |
} |
|
448 |
|
|
449 |
$last_element = $elements[count($elements)-1]; |
|
450 |
if ($last_element[0] == '"') { |
|
451 |
$elements[count($elements)-1] = substr($last_element, 1); |
|
452 |
$prev_line = $elements; |
|
453 |
continue; |
|
454 |
} |
383379
|
455 |
$this->csv_to_vcard($elements); |
5f1765
|
456 |
$prev_line = false; |
383379
|
457 |
} |
AM |
458 |
} |
|
459 |
} |
|
460 |
|
|
461 |
/** |
a95874
|
462 |
* Export vCards |
AM |
463 |
* |
383379
|
464 |
* @return array rcube_vcard List of vcards |
AM |
465 |
*/ |
|
466 |
public function export() |
|
467 |
{ |
|
468 |
return $this->vcards; |
|
469 |
} |
|
470 |
|
|
471 |
/** |
745d86
|
472 |
* Parse CSV file line |
AM |
473 |
*/ |
|
474 |
protected function parse_line($line) |
|
475 |
{ |
|
476 |
$line = trim($line); |
|
477 |
if (empty($line)) { |
|
478 |
return null; |
|
479 |
} |
|
480 |
|
|
481 |
$fields = rcube_utils::explode_quoted_string(',', $line); |
|
482 |
|
|
483 |
// remove quotes if needed |
|
484 |
if (!empty($fields)) { |
|
485 |
foreach ($fields as $idx => $value) { |
|
486 |
if (($len = strlen($value)) > 1 && $value[0] == '"' && $value[$len-1] == '"') { |
|
487 |
// remove surrounding quotes |
|
488 |
$value = substr($value, 1, -1); |
|
489 |
// replace doubled quotes inside the string with single quote |
|
490 |
$value = str_replace('""', '"', $value); |
|
491 |
|
|
492 |
$fields[$idx] = $value; |
|
493 |
} |
|
494 |
} |
|
495 |
} |
|
496 |
|
|
497 |
return $fields; |
|
498 |
} |
|
499 |
|
|
500 |
/** |
383379
|
501 |
* Parse CSV header line, detect fields mapping |
AM |
502 |
*/ |
|
503 |
protected function parse_header($elements) |
92bd3a
|
504 |
{ |
AM |
505 |
$map1 = array(); |
|
506 |
$map2 = array(); |
|
507 |
$size = count($elements); |
|
508 |
|
|
509 |
// check English labels |
|
510 |
for ($i = 0; $i < $size; $i++) { |
383379
|
511 |
$label = $this->label_map[$elements[$i]]; |
AM |
512 |
if ($label && !empty($this->csv2vcard_map[$label])) { |
92bd3a
|
513 |
$map1[$i] = $this->csv2vcard_map[$label]; |
383379
|
514 |
} |
AM |
515 |
} |
5f1765
|
516 |
|
92bd3a
|
517 |
// check localized labels |
AM |
518 |
if (!empty($this->local_label_map)) { |
|
519 |
for ($i = 0; $i < $size; $i++) { |
|
520 |
$label = $this->local_label_map[$elements[$i]]; |
0b0cae
|
521 |
|
AM |
522 |
// special localization label |
|
523 |
if ($label && $label[0] == '_') { |
|
524 |
$label = substr($label, 1); |
|
525 |
} |
|
526 |
|
92bd3a
|
527 |
if ($label && !empty($this->csv2vcard_map[$label])) { |
AM |
528 |
$map2[$i] = $this->csv2vcard_map[$label]; |
|
529 |
} |
|
530 |
} |
|
531 |
} |
|
532 |
|
|
533 |
$this->map = count($map1) >= count($map2) ? $map1 : $map2; |
5f1765
|
534 |
|
AM |
535 |
// support special Gmail format |
|
536 |
foreach ($this->gmail_label_map as $key => $items) { |
|
537 |
$num = 1; |
|
538 |
while (($_key = "$key $num - Type") && ($found = array_search($_key, $elements)) !== false) { |
|
539 |
$this->gmail_map["$key:$num"] = array('_key' => $key, '_idx' => $found); |
|
540 |
foreach (array_keys($items) as $item_key) { |
|
541 |
$_key = "$key $num - $item_key"; |
|
542 |
if (($found = array_search($_key, $elements)) !== false) { |
|
543 |
$this->gmail_map["$key:$num"][$item_key] = $found; |
|
544 |
} |
|
545 |
} |
|
546 |
|
|
547 |
$num++; |
|
548 |
} |
|
549 |
} |
383379
|
550 |
} |
AM |
551 |
|
|
552 |
/** |
|
553 |
* Convert CSV data row to vCard |
|
554 |
*/ |
|
555 |
protected function csv_to_vcard($data) |
|
556 |
{ |
|
557 |
$contact = array(); |
|
558 |
foreach ($this->map as $idx => $name) { |
|
559 |
$value = $data[$idx]; |
|
560 |
if ($value !== null && $value !== '') { |
f86449
|
561 |
if (!empty($contact[$name])) { |
AM |
562 |
$contact[$name] = (array) $contact[$name]; |
|
563 |
$contact[$name][] = $value; |
|
564 |
} |
|
565 |
else { |
|
566 |
$contact[$name] = $value; |
|
567 |
} |
383379
|
568 |
} |
AM |
569 |
} |
|
570 |
|
5f1765
|
571 |
// Gmail format support |
AM |
572 |
foreach ($this->gmail_map as $idx => $item) { |
|
573 |
$type = preg_replace('/[^a-z]/', '', strtolower($data[$item['_idx']])); |
|
574 |
$key = $item['_key']; |
|
575 |
|
|
576 |
unset($item['_idx']); |
|
577 |
unset($item['_key']); |
|
578 |
|
|
579 |
foreach ($item as $item_key => $item_idx) { |
|
580 |
$value = $data[$item_idx]; |
25fb97
|
581 |
if ($value !== null && $value !== '') { |
AM |
582 |
foreach (array($type, '*') as $_type) { |
|
583 |
if ($data_idx = $this->gmail_label_map[$key][$item_key][$_type]) { |
b262e1
|
584 |
$value = explode(' ::: ', $value); |
AM |
585 |
|
25fb97
|
586 |
if (!empty($contact[$data_idx])) { |
b262e1
|
587 |
$contact[$data_idx] = array_merge((array) $contact[$data_idx], $value); |
25fb97
|
588 |
} |
AM |
589 |
else { |
|
590 |
$contact[$data_idx] = $value; |
|
591 |
} |
|
592 |
break; |
|
593 |
} |
|
594 |
} |
5f1765
|
595 |
} |
AM |
596 |
} |
|
597 |
} |
|
598 |
|
383379
|
599 |
if (empty($contact)) { |
AM |
600 |
return; |
|
601 |
} |
|
602 |
|
|
603 |
// Handle special values |
|
604 |
if (!empty($contact['birthday-d']) && !empty($contact['birthday-m']) && !empty($contact['birthday-y'])) { |
|
605 |
$contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; |
|
606 |
} |
|
607 |
|
5983ee
|
608 |
if (!empty($contact['groups'])) { |
5f1765
|
609 |
// categories/groups separator in vCard is ',' not ';' |
bb1398
|
610 |
$contact['groups'] = str_replace(',', '', $contact['groups']); |
5983ee
|
611 |
$contact['groups'] = str_replace(';', ',', $contact['groups']); |
5f1765
|
612 |
|
AM |
613 |
if (!empty($this->gmail_map)) { |
bb1398
|
614 |
// remove "* " added by GMail |
5f1765
|
615 |
$contact['groups'] = str_replace('* ', '', $contact['groups']); |
bb1398
|
616 |
// replace strange delimiter |
AM |
617 |
$contact['groups'] = str_replace(' ::: ', ',', $contact['groups']); |
5f1765
|
618 |
} |
5983ee
|
619 |
} |
AM |
620 |
|
609483
|
621 |
// Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" |
c66b60
|
622 |
foreach (array('birthday', 'anniversary') as $key) { |
609483
|
623 |
if (!empty($contact[$key])) { |
AM |
624 |
$date = preg_replace('/[0[:^word:]]/', '', $contact[$key]); |
|
625 |
if (empty($date)) { |
|
626 |
unset($contact[$key]); |
|
627 |
} |
c66b60
|
628 |
} |
AM |
629 |
} |
|
630 |
|
|
631 |
if (!empty($contact['gender']) && ($gender = strtolower($contact['gender']))) { |
|
632 |
if (!in_array($gender, array('male', 'female'))) { |
|
633 |
unset($contact['gender']); |
|
634 |
} |
|
635 |
} |
|
636 |
|
acf851
|
637 |
// Convert address(es) to rcube_vcard data |
AM |
638 |
foreach ($contact as $idx => $value) { |
|
639 |
$name = explode(':', $idx); |
|
640 |
if (in_array($name[0], array('street', 'locality', 'region', 'zipcode', 'country'))) { |
|
641 |
$contact['address:'.$name[1]][$name[0]] = $value; |
|
642 |
unset($contact[$idx]); |
|
643 |
} |
|
644 |
} |
|
645 |
|
383379
|
646 |
// Create vcard object |
AM |
647 |
$vcard = new rcube_vcard(); |
|
648 |
foreach ($contact as $name => $value) { |
|
649 |
$name = explode(':', $name); |
25fb97
|
650 |
if (is_array($value) && $name[0] != 'address') { |
AM |
651 |
foreach ((array) $value as $val) { |
|
652 |
$vcard->set($name[0], $val, $name[1]); |
|
653 |
} |
|
654 |
} |
|
655 |
else { |
|
656 |
$vcard->set($name[0], $value, $name[1]); |
|
657 |
} |
383379
|
658 |
} |
AM |
659 |
|
|
660 |
// add to the list |
|
661 |
$this->vcards[] = $vcard; |
|
662 |
} |
|
663 |
} |