Mercurial > hg > rc1
comparison vendor/sabre/vobject/lib/Component/VCard.php @ 7:430dbd5346f7
vendor sabre as distributed
author | Charlie Root |
---|---|
date | Sat, 13 Jan 2018 09:06:10 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:cec75ba50afc | 7:430dbd5346f7 |
---|---|
1 <?php | |
2 | |
3 namespace Sabre\VObject\Component; | |
4 | |
5 use | |
6 Sabre\VObject; | |
7 | |
8 /** | |
9 * The VCard component | |
10 * | |
11 * This component represents the BEGIN:VCARD and END:VCARD found in every | |
12 * vcard. | |
13 * | |
14 * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/). | |
15 * @author Evert Pot (http://evertpot.com/) | |
16 * @license http://sabre.io/license/ Modified BSD License | |
17 */ | |
18 class VCard extends VObject\Document { | |
19 | |
20 /** | |
21 * The default name for this component. | |
22 * | |
23 * This should be 'VCALENDAR' or 'VCARD'. | |
24 * | |
25 * @var string | |
26 */ | |
27 static $defaultName = 'VCARD'; | |
28 | |
29 /** | |
30 * Caching the version number | |
31 * | |
32 * @var int | |
33 */ | |
34 private $version = null; | |
35 | |
36 /** | |
37 * List of value-types, and which classes they map to. | |
38 * | |
39 * @var array | |
40 */ | |
41 static $valueMap = array( | |
42 'BINARY' => 'Sabre\\VObject\\Property\\Binary', | |
43 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean', | |
44 'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only | |
45 'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date', | |
46 'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime', | |
47 'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only | |
48 'FLOAT' => 'Sabre\\VObject\\Property\\Float', | |
49 'INTEGER' => 'Sabre\\VObject\\Property\\Integer', | |
50 'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', | |
51 'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', | |
52 'TEXT' => 'Sabre\\VObject\\Property\\Text', | |
53 'TIME' => 'Sabre\\VObject\\Property\\Time', | |
54 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only. | |
55 'URI' => 'Sabre\\VObject\\Property\\Uri', | |
56 'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only | |
57 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset', | |
58 ); | |
59 | |
60 /** | |
61 * List of properties, and which classes they map to. | |
62 * | |
63 * @var array | |
64 */ | |
65 static $propertyMap = array( | |
66 | |
67 // vCard 2.1 properties and up | |
68 'N' => 'Sabre\\VObject\\Property\\Text', | |
69 'FN' => 'Sabre\\VObject\\Property\\FlatText', | |
70 'PHOTO' => 'Sabre\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values. | |
71 'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', | |
72 'ADR' => 'Sabre\\VObject\\Property\\Text', | |
73 'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 | |
74 'TEL' => 'Sabre\\VObject\\Property\\FlatText', | |
75 'EMAIL' => 'Sabre\\VObject\\Property\\FlatText', | |
76 'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 | |
77 'GEO' => 'Sabre\\VObject\\Property\\FlatText', | |
78 'TITLE' => 'Sabre\\VObject\\Property\\FlatText', | |
79 'ROLE' => 'Sabre\\VObject\\Property\\FlatText', | |
80 'LOGO' => 'Sabre\\VObject\\Property\\Binary', | |
81 // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so | |
82 // not supported at the moment | |
83 'ORG' => 'Sabre\\VObject\\Property\\Text', | |
84 'NOTE' => 'Sabre\\VObject\\Property\\FlatText', | |
85 'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp', | |
86 'SOUND' => 'Sabre\\VObject\\Property\\FlatText', | |
87 'URL' => 'Sabre\\VObject\\Property\\Uri', | |
88 'UID' => 'Sabre\\VObject\\Property\\FlatText', | |
89 'VERSION' => 'Sabre\\VObject\\Property\\FlatText', | |
90 'KEY' => 'Sabre\\VObject\\Property\\FlatText', | |
91 'TZ' => 'Sabre\\VObject\\Property\\Text', | |
92 | |
93 // vCard 3.0 properties | |
94 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text', | |
95 'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText', | |
96 'PRODID' => 'Sabre\\VObject\\Property\\FlatText', | |
97 'NICKNAME' => 'Sabre\\VObject\\Property\\Text', | |
98 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0 | |
99 | |
100 // rfc2739 properties | |
101 'FBURL' => 'Sabre\\VObject\\Property\\Uri', | |
102 'CAPURI' => 'Sabre\\VObject\\Property\\Uri', | |
103 'CALURI' => 'Sabre\\VObject\\Property\\Uri', | |
104 | |
105 // rfc4770 properties | |
106 'IMPP' => 'Sabre\\VObject\\Property\\Uri', | |
107 | |
108 // vCard 4.0 properties | |
109 'XML' => 'Sabre\\VObject\\Property\\FlatText', | |
110 'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', | |
111 'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text', | |
112 'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag', | |
113 'GENDER' => 'Sabre\\VObject\\Property\\Text', | |
114 'KIND' => 'Sabre\\VObject\\Property\\FlatText', | |
115 | |
116 // rfc6474 properties | |
117 'BIRTHPLACE' => 'Sabre\\VObject\\Property\\FlatText', | |
118 'DEATHPLACE' => 'Sabre\\VObject\\Property\\FlatText', | |
119 'DEATHDATE' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', | |
120 | |
121 // rfc6715 properties | |
122 'EXPERTISE' => 'Sabre\\VObject\\Property\\FlatText', | |
123 'HOBBY' => 'Sabre\\VObject\\Property\\FlatText', | |
124 'INTEREST' => 'Sabre\\VObject\\Property\\FlatText', | |
125 'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText', | |
126 | |
127 ); | |
128 | |
129 /** | |
130 * Returns the current document type. | |
131 * | |
132 * @return void | |
133 */ | |
134 function getDocumentType() { | |
135 | |
136 if (!$this->version) { | |
137 $version = (string)$this->VERSION; | |
138 switch($version) { | |
139 case '2.1' : | |
140 $this->version = self::VCARD21; | |
141 break; | |
142 case '3.0' : | |
143 $this->version = self::VCARD30; | |
144 break; | |
145 case '4.0' : | |
146 $this->version = self::VCARD40; | |
147 break; | |
148 default : | |
149 $this->version = self::UNKNOWN; | |
150 break; | |
151 | |
152 } | |
153 } | |
154 | |
155 return $this->version; | |
156 | |
157 } | |
158 | |
159 /** | |
160 * Converts the document to a different vcard version. | |
161 * | |
162 * Use one of the VCARD constants for the target. This method will return | |
163 * a copy of the vcard in the new version. | |
164 * | |
165 * At the moment the only supported conversion is from 3.0 to 4.0. | |
166 * | |
167 * If input and output version are identical, a clone is returned. | |
168 * | |
169 * @param int $target | |
170 * @return VCard | |
171 */ | |
172 function convert($target) { | |
173 | |
174 $converter = new VObject\VCardConverter(); | |
175 return $converter->convert($this, $target); | |
176 | |
177 } | |
178 | |
179 /** | |
180 * VCards with version 2.1, 3.0 and 4.0 are found. | |
181 * | |
182 * If the VCARD doesn't know its version, 2.1 is assumed. | |
183 */ | |
184 const DEFAULT_VERSION = self::VCARD21; | |
185 | |
186 /** | |
187 * Validates the node for correctness. | |
188 * | |
189 * The following options are supported: | |
190 * Node::REPAIR - May attempt to automatically repair the problem. | |
191 * | |
192 * This method returns an array with detected problems. | |
193 * Every element has the following properties: | |
194 * | |
195 * * level - problem level. | |
196 * * message - A human-readable string describing the issue. | |
197 * * node - A reference to the problematic node. | |
198 * | |
199 * The level means: | |
200 * 1 - The issue was repaired (only happens if REPAIR was turned on) | |
201 * 2 - An inconsequential issue | |
202 * 3 - A severe issue. | |
203 * | |
204 * @param int $options | |
205 * @return array | |
206 */ | |
207 function validate($options = 0) { | |
208 | |
209 $warnings = array(); | |
210 | |
211 $versionMap = array( | |
212 self::VCARD21 => '2.1', | |
213 self::VCARD30 => '3.0', | |
214 self::VCARD40 => '4.0', | |
215 ); | |
216 | |
217 $version = $this->select('VERSION'); | |
218 if (count($version)===1) { | |
219 $version = (string)$this->VERSION; | |
220 if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') { | |
221 $warnings[] = array( | |
222 'level' => 3, | |
223 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', | |
224 'node' => $this, | |
225 ); | |
226 if ($options & self::REPAIR) { | |
227 $this->VERSION = $versionMap[self::DEFAULT_VERSION]; | |
228 } | |
229 } | |
230 if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) { | |
231 $warnings[] = array( | |
232 'level' => 3, | |
233 'message' => 'CardDAV servers are not allowed to accept vCard 2.1.', | |
234 'node' => $this, | |
235 ); | |
236 } | |
237 | |
238 } | |
239 $uid = $this->select('UID'); | |
240 if (count($uid) === 0) { | |
241 if ($options & self::PROFILE_CARDDAV) { | |
242 // Required for CardDAV | |
243 $warningLevel = 3; | |
244 $message = 'vCards on CardDAV servers MUST have a UID property.'; | |
245 } else { | |
246 // Not required for regular vcards | |
247 $warningLevel = 2; | |
248 $message = 'Adding a UID to a vCard property is recommended.'; | |
249 } | |
250 if ($options & self::REPAIR) { | |
251 $this->UID = VObject\UUIDUtil::getUUID(); | |
252 $warningLevel = 1; | |
253 } | |
254 $warnings[] = array( | |
255 'level' => $warningLevel, | |
256 'message' => $message, | |
257 'node' => $this, | |
258 ); | |
259 } | |
260 | |
261 $fn = $this->select('FN'); | |
262 if (count($fn)!==1) { | |
263 | |
264 $repaired = false; | |
265 if (($options & self::REPAIR) && count($fn) === 0) { | |
266 // We're going to try to see if we can use the contents of the | |
267 // N property. | |
268 if (isset($this->N)) { | |
269 $value = explode(';', (string)$this->N); | |
270 if (isset($value[1]) && $value[1]) { | |
271 $this->FN = $value[1] . ' ' . $value[0]; | |
272 } else { | |
273 $this->FN = $value[0]; | |
274 } | |
275 $repaired = true; | |
276 | |
277 // Otherwise, the ORG property may work | |
278 } elseif (isset($this->ORG)) { | |
279 $this->FN = (string)$this->ORG; | |
280 $repaired = true; | |
281 } | |
282 | |
283 } | |
284 $warnings[] = array( | |
285 'level' => $repaired?1:3, | |
286 'message' => 'The FN property must appear in the VCARD component exactly 1 time', | |
287 'node' => $this, | |
288 ); | |
289 } | |
290 | |
291 return array_merge( | |
292 parent::validate($options), | |
293 $warnings | |
294 ); | |
295 | |
296 } | |
297 | |
298 /** | |
299 * A simple list of validation rules. | |
300 * | |
301 * This is simply a list of properties, and how many times they either | |
302 * must or must not appear. | |
303 * | |
304 * Possible values per property: | |
305 * * 0 - Must not appear. | |
306 * * 1 - Must appear exactly once. | |
307 * * + - Must appear at least once. | |
308 * * * - Can appear any number of times. | |
309 * | |
310 * @var array | |
311 */ | |
312 function getValidationRules() { | |
313 | |
314 return array( | |
315 'ADR' => '*', | |
316 'ANNIVERSARY' => '?', | |
317 'BDAY' => '?', | |
318 'CALADRURI' => '*', | |
319 'CALURI' => '*', | |
320 'CATEGORIES' => '*', | |
321 'CLIENTPIDMAP' => '*', | |
322 'EMAIL' => '*', | |
323 'FBURL' => '*', | |
324 'IMPP' => '*', | |
325 'GENDER' => '?', | |
326 'GEO' => '*', | |
327 'KEY' => '*', | |
328 'KIND' => '?', | |
329 'LANG' => '*', | |
330 'LOGO' => '*', | |
331 'MEMBER' => '*', | |
332 'N' => '?', | |
333 'NICKNAME' => '*', | |
334 'NOTE' => '*', | |
335 'ORG' => '*', | |
336 'PHOTO' => '*', | |
337 'PRODID' => '?', | |
338 'RELATED' => '*', | |
339 'REV' => '?', | |
340 'ROLE' => '*', | |
341 'SOUND' => '*', | |
342 'SOURCE' => '*', | |
343 'TEL' => '*', | |
344 'TITLE' => '*', | |
345 'TZ' => '*', | |
346 'URL' => '*', | |
347 'VERSION' => '1', | |
348 'XML' => '*', | |
349 | |
350 // FN is commented out, because it's already handled by the | |
351 // validate function, which may also try to repair it. | |
352 // 'FN' => '+', | |
353 | |
354 'UID' => '?', | |
355 ); | |
356 | |
357 } | |
358 | |
359 /** | |
360 * Returns a preferred field. | |
361 * | |
362 * VCards can indicate wether a field such as ADR, TEL or EMAIL is | |
363 * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x | |
364 * being a number between 1 and 100). | |
365 * | |
366 * If neither of those parameters are specified, the first is returned, if | |
367 * a field with that name does not exist, null is returned. | |
368 * | |
369 * @param string $fieldName | |
370 * @return VObject\Property|null | |
371 */ | |
372 function preferred($propertyName) { | |
373 | |
374 $preferred = null; | |
375 $lastPref = 101; | |
376 foreach($this->select($propertyName) as $field) { | |
377 | |
378 $pref = 101; | |
379 if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) { | |
380 $pref = 1; | |
381 } elseif (isset($field['PREF'])) { | |
382 $pref = $field['PREF']->getValue(); | |
383 } | |
384 | |
385 if ($pref < $lastPref || is_null($preferred)) { | |
386 $preferred = $field; | |
387 $lastPref = $pref; | |
388 } | |
389 | |
390 } | |
391 return $preferred; | |
392 | |
393 } | |
394 | |
395 /** | |
396 * This method should return a list of default property values. | |
397 * | |
398 * @return array | |
399 */ | |
400 protected function getDefaults() { | |
401 | |
402 return array( | |
403 'VERSION' => '3.0', | |
404 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN', | |
405 ); | |
406 | |
407 } | |
408 | |
409 /** | |
410 * This method returns an array, with the representation as it should be | |
411 * encoded in json. This is used to create jCard or jCal documents. | |
412 * | |
413 * @return array | |
414 */ | |
415 function jsonSerialize() { | |
416 | |
417 // A vcard does not have sub-components, so we're overriding this | |
418 // method to remove that array element. | |
419 $properties = array(); | |
420 | |
421 foreach($this->children as $child) { | |
422 $properties[] = $child->jsonSerialize(); | |
423 } | |
424 | |
425 return array( | |
426 strtolower($this->name), | |
427 $properties, | |
428 ); | |
429 | |
430 } | |
431 | |
432 /** | |
433 * Returns the default class for a property name. | |
434 * | |
435 * @param string $propertyName | |
436 * @return string | |
437 */ | |
438 function getClassNameForPropertyName($propertyName) { | |
439 | |
440 $className = parent::getClassNameForPropertyName($propertyName); | |
441 // In vCard 4, BINARY no longer exists, and we need URI instead. | |
442 | |
443 if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) { | |
444 return 'Sabre\\VObject\\Property\\Uri'; | |
445 } | |
446 return $className; | |
447 | |
448 } | |
449 | |
450 } | |
451 |