ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
VCard.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use Sabre\VObject;
6 use Sabre\Xml;
7 
18 class VCard extends VObject\Document {
19 
27  static $defaultName = 'VCARD';
28 
34  private $version = null;
35 
41  static $componentMap = [
42  'VCARD' => 'Sabre\\VObject\\Component\\VCard',
43  ];
44 
50  static $valueMap = [
51  'BINARY' => 'Sabre\\VObject\\Property\\Binary',
52  'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
53  'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
54  'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date',
55  'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime',
56  'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
57  'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue',
58  'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue',
59  'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
60  'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
61  'TEXT' => 'Sabre\\VObject\\Property\\Text',
62  'TIME' => 'Sabre\\VObject\\Property\\Time',
63  'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
64  'URI' => 'Sabre\\VObject\\Property\\Uri',
65  'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
66  'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
67  ];
68 
74  static $propertyMap = [
75 
76  // vCard 2.1 properties and up
77  'N' => 'Sabre\\VObject\\Property\\Text',
78  'FN' => 'Sabre\\VObject\\Property\\FlatText',
79  'PHOTO' => 'Sabre\\VObject\\Property\\Binary',
80  'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
81  'ADR' => 'Sabre\\VObject\\Property\\Text',
82  'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
83  'TEL' => 'Sabre\\VObject\\Property\\FlatText',
84  'EMAIL' => 'Sabre\\VObject\\Property\\FlatText',
85  'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
86  'GEO' => 'Sabre\\VObject\\Property\\FlatText',
87  'TITLE' => 'Sabre\\VObject\\Property\\FlatText',
88  'ROLE' => 'Sabre\\VObject\\Property\\FlatText',
89  'LOGO' => 'Sabre\\VObject\\Property\\Binary',
90  // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so
91  // not supported at the moment
92  'ORG' => 'Sabre\\VObject\\Property\\Text',
93  'NOTE' => 'Sabre\\VObject\\Property\\FlatText',
94  'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
95  'SOUND' => 'Sabre\\VObject\\Property\\FlatText',
96  'URL' => 'Sabre\\VObject\\Property\\Uri',
97  'UID' => 'Sabre\\VObject\\Property\\FlatText',
98  'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
99  'KEY' => 'Sabre\\VObject\\Property\\FlatText',
100  'TZ' => 'Sabre\\VObject\\Property\\Text',
101 
102  // vCard 3.0 properties
103  'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
104  'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
105  'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
106  'NICKNAME' => 'Sabre\\VObject\\Property\\Text',
107  'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
108 
109  // rfc2739 properties
110  'FBURL' => 'Sabre\\VObject\\Property\\Uri',
111  'CAPURI' => 'Sabre\\VObject\\Property\\Uri',
112  'CALURI' => 'Sabre\\VObject\\Property\\Uri',
113  'CALADRURI' => 'Sabre\\VObject\\Property\\Uri',
114 
115  // rfc4770 properties
116  'IMPP' => 'Sabre\\VObject\\Property\\Uri',
117 
118  // vCard 4.0 properties
119  'SOURCE' => 'Sabre\\VObject\\Property\\Uri',
120  'XML' => 'Sabre\\VObject\\Property\\FlatText',
121  'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
122  'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
123  'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
124  'GENDER' => 'Sabre\\VObject\\Property\\Text',
125  'KIND' => 'Sabre\\VObject\\Property\\FlatText',
126  'MEMBER' => 'Sabre\\VObject\\Property\\Uri',
127  'RELATED' => 'Sabre\\VObject\\Property\\Uri',
128 
129  // rfc6474 properties
130  'BIRTHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
131  'DEATHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
132  'DEATHDATE' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
133 
134  // rfc6715 properties
135  'EXPERTISE' => 'Sabre\\VObject\\Property\\FlatText',
136  'HOBBY' => 'Sabre\\VObject\\Property\\FlatText',
137  'INTEREST' => 'Sabre\\VObject\\Property\\FlatText',
138  'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
139 
140  ];
141 
147  function getDocumentType() {
148 
149  if (!$this->version) {
150 
151  $version = (string)$this->VERSION;
152 
153  switch ($version) {
154  case '2.1' :
155  $this->version = self::VCARD21;
156  break;
157  case '3.0' :
158  $this->version = self::VCARD30;
159  break;
160  case '4.0' :
161  $this->version = self::VCARD40;
162  break;
163  default :
164  // We don't want to cache the version if it's unknown,
165  // because we might get a version property in a bit.
166  return self::UNKNOWN;
167  }
168  }
169 
170  return $this->version;
171 
172  }
173 
188  function convert($target) {
189 
190  $converter = new VObject\VCardConverter();
191  return $converter->convert($this, $target);
192 
193  }
194 
200  const DEFAULT_VERSION = self::VCARD21;
201 
224  function validate($options = 0) {
225 
226  $warnings = [];
227 
228  $versionMap = [
229  self::VCARD21 => '2.1',
230  self::VCARD30 => '3.0',
231  self::VCARD40 => '4.0',
232  ];
233 
234  $version = $this->select('VERSION');
235  if (count($version) === 1) {
236  $version = (string)$this->VERSION;
237  if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
238  $warnings[] = [
239  'level' => 3,
240  'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
241  'node' => $this,
242  ];
243  if ($options & self::REPAIR) {
244  $this->VERSION = $versionMap[self::DEFAULT_VERSION];
245  }
246  }
247  if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
248  $warnings[] = [
249  'level' => 3,
250  'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
251  'node' => $this,
252  ];
253  }
254 
255  }
256  $uid = $this->select('UID');
257  if (count($uid) === 0) {
258  if ($options & self::PROFILE_CARDDAV) {
259  // Required for CardDAV
260  $warningLevel = 3;
261  $message = 'vCards on CardDAV servers MUST have a UID property.';
262  } else {
263  // Not required for regular vcards
264  $warningLevel = 2;
265  $message = 'Adding a UID to a vCard property is recommended.';
266  }
267  if ($options & self::REPAIR) {
268  $this->UID = VObject\UUIDUtil::getUUID();
269  $warningLevel = 1;
270  }
271  $warnings[] = [
272  'level' => $warningLevel,
273  'message' => $message,
274  'node' => $this,
275  ];
276  }
277 
278  $fn = $this->select('FN');
279  if (count($fn) !== 1) {
280 
281  $repaired = false;
282  if (($options & self::REPAIR) && count($fn) === 0) {
283  // We're going to try to see if we can use the contents of the
284  // N property.
285  if (isset($this->N)) {
286  $value = explode(';', (string)$this->N);
287  if (isset($value[1]) && $value[1]) {
288  $this->FN = $value[1] . ' ' . $value[0];
289  } else {
290  $this->FN = $value[0];
291  }
292  $repaired = true;
293 
294  // Otherwise, the ORG property may work
295  } elseif (isset($this->ORG)) {
296  $this->FN = (string)$this->ORG;
297  $repaired = true;
298 
299  // Otherwise, the EMAIL property may work
300  } elseif (isset($this->EMAIL)) {
301  $this->FN = (string)$this->EMAIL;
302  $repaired = true;
303  }
304 
305  }
306  $warnings[] = [
307  'level' => $repaired ? 1 : 3,
308  'message' => 'The FN property must appear in the VCARD component exactly 1 time',
309  'node' => $this,
310  ];
311  }
312 
313  return array_merge(
314  parent::validate($options),
315  $warnings
316  );
317 
318  }
319 
335  function getValidationRules() {
336 
337  return [
338  'ADR' => '*',
339  'ANNIVERSARY' => '?',
340  'BDAY' => '?',
341  'CALADRURI' => '*',
342  'CALURI' => '*',
343  'CATEGORIES' => '*',
344  'CLIENTPIDMAP' => '*',
345  'EMAIL' => '*',
346  'FBURL' => '*',
347  'IMPP' => '*',
348  'GENDER' => '?',
349  'GEO' => '*',
350  'KEY' => '*',
351  'KIND' => '?',
352  'LANG' => '*',
353  'LOGO' => '*',
354  'MEMBER' => '*',
355  'N' => '?',
356  'NICKNAME' => '*',
357  'NOTE' => '*',
358  'ORG' => '*',
359  'PHOTO' => '*',
360  'PRODID' => '?',
361  'RELATED' => '*',
362  'REV' => '?',
363  'ROLE' => '*',
364  'SOUND' => '*',
365  'SOURCE' => '*',
366  'TEL' => '*',
367  'TITLE' => '*',
368  'TZ' => '*',
369  'URL' => '*',
370  'VERSION' => '1',
371  'XML' => '*',
372 
373  // FN is commented out, because it's already handled by the
374  // validate function, which may also try to repair it.
375  // 'FN' => '+',
376  'UID' => '?',
377  ];
378 
379  }
380 
395  function preferred($propertyName) {
396 
397  $preferred = null;
398  $lastPref = 101;
399  foreach ($this->select($propertyName) as $field) {
400 
401  $pref = 101;
402  if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
403  $pref = 1;
404  } elseif (isset($field['PREF'])) {
405  $pref = $field['PREF']->getValue();
406  }
407 
408  if ($pref < $lastPref || is_null($preferred)) {
409  $preferred = $field;
410  $lastPref = $pref;
411  }
412 
413  }
414  return $preferred;
415 
416  }
417 
429  function getByType($propertyName, $type) {
430  foreach ($this->select($propertyName) as $field) {
431  if (isset($field['TYPE']) && $field['TYPE']->has($type)) {
432  return $field;
433  }
434  }
435  }
436 
442  protected function getDefaults() {
443 
444  return [
445  'VERSION' => '4.0',
446  'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
447  'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
448  ];
449 
450  }
451 
458  function jsonSerialize() {
459 
460  // A vcard does not have sub-components, so we're overriding this
461  // method to remove that array element.
462  $properties = [];
463 
464  foreach ($this->children() as $child) {
465  $properties[] = $child->jsonSerialize();
466  }
467 
468  return [
469  strtolower($this->name),
470  $properties,
471  ];
472 
473  }
474 
483  function xmlSerialize(Xml\Writer $writer) {
484 
485  $propertiesByGroup = [];
486 
487  foreach ($this->children() as $property) {
488 
489  $group = $property->group;
490 
491  if (!isset($propertiesByGroup[$group])) {
492  $propertiesByGroup[$group] = [];
493  }
494 
495  $propertiesByGroup[$group][] = $property;
496 
497  }
498 
499  $writer->startElement(strtolower($this->name));
500 
501  foreach ($propertiesByGroup as $group => $properties) {
502 
503  if (!empty($group)) {
504 
505  $writer->startElement('group');
506  $writer->writeAttribute('name', strtolower($group));
507 
508  }
509 
510  foreach ($properties as $property) {
511  switch ($property->name) {
512 
513  case 'VERSION':
514  continue;
515 
516  case 'XML':
517  $value = $property->getParts();
518  $fragment = new Xml\Element\XmlFragment($value[0]);
519  $writer->write($fragment);
520  break;
521 
522  default:
523  $property->xmlSerialize($writer);
524  break;
525 
526  }
527  }
528 
529  if (!empty($group)) {
530  $writer->endElement();
531  }
532 
533  }
534 
535  $writer->endElement();
536 
537  }
538 
546  function getClassNameForPropertyName($propertyName) {
547 
548  $className = parent::getClassNameForPropertyName($propertyName);
549 
550  // In vCard 4, BINARY no longer exists, and we need URI instead.
551  if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) {
552  return 'Sabre\\VObject\\Property\\Uri';
553  }
554  return $className;
555 
556  }
557 
558 }
getByType($propertyName, $type)
Returns a property with a specific TYPE value (ADR, TEL, or EMAIL).
Definition: VCard.php:429
xmlSerialize(Xml\Writer $writer)
This method serializes the data into XML.
Definition: VCard.php:483
iCalendar/vCard/jCal/jCard/xCal/xCard writer object.
Definition: Writer.php:17
This utility converts vcards from one version to another.
select($name)
Returns an array with elements that match the specified name.
Definition: Component.php:231
jsonSerialize()
This method returns an array, with the representation as it should be encoded in json.
Definition: VCard.php:458
$type
const VERSION
Full version number.
Definition: Version.php:17
convert($target)
Converts the document to a different vcard version.
Definition: VCard.php:188
getClassNameForPropertyName($propertyName)
Returns the default class for a property name.
Definition: VCard.php:546
const DEFAULT_VERSION
VCards with version 2.1, 3.0 and 4.0 are found.
Definition: VCard.php:200
catch(Exception $e) $message
preferred($propertyName)
Returns a preferred field.
Definition: VCard.php:395
count()
Returns the number of elements.
Definition: Node.php:177
getDefaults()
This method should return a list of default property values.
Definition: VCard.php:442
static getUUID()
Returns a pseudo-random v4 UUID.
Definition: UUIDUtil.php:27
static getDocumentType()
Returns the current document type.
Definition: VCard.php:147
children()
Returns a flat list of all the properties and components in this component.
Definition: Component.php:187
The VCard component.
Definition: VCard.php:18
$target
Definition: test.php:19
validate($options=0)
Validates the node for correctness.
Definition: VCard.php:224