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