xref: /plugin/davcal/vendor/sabre/vobject/lib/Component/VCard.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
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