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 |
