1<?php 2 3namespace Sabre\CalDAV\Xml\Property; 4 5use Sabre\Xml\Element; 6use Sabre\Xml\Reader; 7use Sabre\Xml\Writer; 8use Sabre\CalDAV\Plugin; 9use Sabre\CalDAV\SharingPlugin; 10 11/** 12 * Invite property 13 * 14 * This property encodes the 'invite' property, as defined by 15 * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/ 16 * namespace. 17 * 18 * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt 19 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 20 * @author Evert Pot (http://evertpot.com/) 21 * @license http://sabre.io/license/ Modified BSD License 22 */ 23class Invite implements Element { 24 25 /** 26 * The list of users a calendar has been shared to. 27 * 28 * @var array 29 */ 30 protected $users; 31 32 /** 33 * The organizer contains information about the person who shared the 34 * object. 35 * 36 * @var array 37 */ 38 protected $organizer; 39 40 /** 41 * Creates the property. 42 * 43 * Users is an array. Each element of the array has the following 44 * properties: 45 * 46 * * href - Often a mailto: address 47 * * commonName - Optional, for example a first and lastname for a user. 48 * * status - One of the SharingPlugin::STATUS_* constants. 49 * * readOnly - true or false 50 * * summary - Optional, description of the share 51 * 52 * The organizer key is optional to specify. It's only useful when a 53 * 'sharee' requests the sharing information. 54 * 55 * The organizer may have the following properties: 56 * * href - Often a mailto: address. 57 * * commonName - Optional human-readable name. 58 * * firstName - Optional first name. 59 * * lastName - Optional last name. 60 * 61 * If you wonder why these two structures are so different, I guess a 62 * valid answer is that the current spec is still a draft. 63 * 64 * @param array $users 65 */ 66 function __construct(array $users, array $organizer = null) { 67 68 $this->users = $users; 69 $this->organizer = $organizer; 70 71 } 72 73 /** 74 * Returns the list of users, as it was passed to the constructor. 75 * 76 * @return array 77 */ 78 function getValue() { 79 80 return $this->users; 81 82 } 83 84 /** 85 * The xmlSerialize metod is called during xml writing. 86 * 87 * Use the $writer argument to write its own xml serialization. 88 * 89 * An important note: do _not_ create a parent element. Any element 90 * implementing XmlSerializble should only ever write what's considered 91 * its 'inner xml'. 92 * 93 * The parent of the current element is responsible for writing a 94 * containing element. 95 * 96 * This allows serializers to be re-used for different element names. 97 * 98 * If you are opening new elements, you must also close them again. 99 * 100 * @param Writer $writer 101 * @return void 102 */ 103 function xmlSerialize(Writer $writer) { 104 105 $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; 106 107 if (!is_null($this->organizer)) { 108 109 $writer->startElement($cs . 'organizer'); 110 $writer->writeElement('{DAV:}href', $this->organizer['href']); 111 112 if (isset($this->organizer['commonName']) && $this->organizer['commonName']) { 113 $writer->writeElement($cs . 'common-name', $this->organizer['commonName']); 114 } 115 if (isset($this->organizer['firstName']) && $this->organizer['firstName']) { 116 $writer->writeElement($cs . 'first-name', $this->organizer['firstName']); 117 } 118 if (isset($this->organizer['lastName']) && $this->organizer['lastName']) { 119 $writer->writeElement($cs . 'last-name', $this->organizer['lastName']); 120 } 121 $writer->endElement(); // organizer 122 123 } 124 125 foreach ($this->users as $user) { 126 127 $writer->startElement($cs . 'user'); 128 $writer->writeElement('{DAV:}href', $user['href']); 129 if (isset($user['commonName']) && $user['commonName']) { 130 $writer->writeElement($cs . 'common-name', $user['commonName']); 131 } 132 switch ($user['status']) { 133 134 case SharingPlugin::STATUS_ACCEPTED : 135 $writer->writeElement($cs . 'invite-accepted'); 136 break; 137 case SharingPlugin::STATUS_DECLINED : 138 $writer->writeElement($cs . 'invite-declined'); 139 break; 140 case SharingPlugin::STATUS_NORESPONSE : 141 $writer->writeElement($cs . 'invite-noresponse'); 142 break; 143 case SharingPlugin::STATUS_INVALID : 144 $writer->writeElement($cs . 'invite-invalid'); 145 break; 146 } 147 148 $writer->startElement($cs . 'access'); 149 if ($user['readOnly']) { 150 $writer->writeElement($cs . 'read'); 151 } else { 152 $writer->writeElement($cs . 'read-write'); 153 } 154 $writer->endElement(); // access 155 156 if (isset($user['summary']) && $user['summary']) { 157 $writer->writeElement($cs . 'summary', $user['summary']); 158 } 159 160 $writer->endElement(); //user 161 162 } 163 164 } 165 166 /** 167 * The deserialize method is called during xml parsing. 168 * 169 * This method is called statictly, this is because in theory this method 170 * may be used as a type of constructor, or factory method. 171 * 172 * Often you want to return an instance of the current class, but you are 173 * free to return other data as well. 174 * 175 * You are responsible for advancing the reader to the next element. Not 176 * doing anything will result in a never-ending loop. 177 * 178 * If you just want to skip parsing for this element altogether, you can 179 * just call $reader->next(); 180 * 181 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to 182 * the next element. 183 * 184 * @param Reader $reader 185 * @return mixed 186 */ 187 static function xmlDeserialize(Reader $reader) { 188 189 $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; 190 191 $users = []; 192 193 foreach ($reader->parseInnerTree() as $elem) { 194 195 if ($elem['name'] !== $cs . 'user') 196 continue; 197 198 $user = [ 199 'href' => null, 200 'commonName' => null, 201 'readOnly' => null, 202 'summary' => null, 203 'status' => null, 204 ]; 205 206 foreach ($elem['value'] as $userElem) { 207 208 switch ($userElem['name']) { 209 case $cs . 'invite-accepted' : 210 $user['status'] = SharingPlugin::STATUS_ACCEPTED; 211 break; 212 case $cs . 'invite-declined' : 213 $user['status'] = SharingPlugin::STATUS_DECLINED; 214 break; 215 case $cs . 'invite-noresponse' : 216 $user['status'] = SharingPlugin::STATUS_NORESPONSE; 217 break; 218 case $cs . 'invite-invalid' : 219 $user['status'] = SharingPlugin::STATUS_INVALID; 220 break; 221 case '{DAV:}href' : 222 $user['href'] = $userElem['value']; 223 break; 224 case $cs . 'common-name' : 225 $user['commonName'] = $userElem['value']; 226 break; 227 case $cs . 'access' : 228 foreach ($userElem['value'] as $accessHref) { 229 if ($accessHref['name'] === $cs . 'read') { 230 $user['readOnly'] = true; 231 } 232 } 233 break; 234 case $cs . 'summary' : 235 $user['summary'] = $userElem['value']; 236 break; 237 238 } 239 240 } 241 if (!$user['status']) { 242 throw new \InvalidArgumentException('Every user must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid'); 243 } 244 245 $users[] = $user; 246 247 } 248 249 return new self($users); 250 251 } 252} 253