1<?php 2 3namespace Sabre\CalDAV; 4 5/** 6 * The CalendarObject represents a single VEVENT or VTODO within a Calendar. 7 * 8 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 9 * @author Evert Pot (http://evertpot.com/) 10 * @license http://sabre.io/license/ Modified BSD License 11 */ 12class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\DAVACL\IACL { 13 14 /** 15 * Sabre\CalDAV\Backend\BackendInterface 16 * 17 * @var Sabre\CalDAV\Backend\AbstractBackend 18 */ 19 protected $caldavBackend; 20 21 /** 22 * Array with information about this CalendarObject 23 * 24 * @var array 25 */ 26 protected $objectData; 27 28 /** 29 * Array with information about the containing calendar 30 * 31 * @var array 32 */ 33 protected $calendarInfo; 34 35 /** 36 * Constructor 37 * 38 * The following properties may be passed within $objectData: 39 * 40 * * calendarid - This must refer to a calendarid from a caldavBackend 41 * * uri - A unique uri. Only the 'basename' must be passed. 42 * * calendardata (optional) - The iCalendar data 43 * * etag - (optional) The etag for this object, MUST be encloded with 44 * double-quotes. 45 * * size - (optional) The size of the data in bytes. 46 * * lastmodified - (optional) format as a unix timestamp. 47 * * acl - (optional) Use this to override the default ACL for the node. 48 * 49 * @param Backend\BackendInterface $caldavBackend 50 * @param array $calendarInfo 51 * @param array $objectData 52 */ 53 function __construct(Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData) { 54 55 $this->caldavBackend = $caldavBackend; 56 57 if (!isset($objectData['uri'])) { 58 throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); 59 } 60 61 $this->calendarInfo = $calendarInfo; 62 $this->objectData = $objectData; 63 64 } 65 66 /** 67 * Returns the uri for this object 68 * 69 * @return string 70 */ 71 function getName() { 72 73 return $this->objectData['uri']; 74 75 } 76 77 /** 78 * Returns the ICalendar-formatted object 79 * 80 * @return string 81 */ 82 function get() { 83 84 // Pre-populating the 'calendardata' is optional, if we don't have it 85 // already we fetch it from the backend. 86 if (!isset($this->objectData['calendardata'])) { 87 $this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); 88 } 89 return $this->objectData['calendardata']; 90 91 } 92 93 /** 94 * Updates the ICalendar-formatted object 95 * 96 * @param string|resource $calendarData 97 * @return string 98 */ 99 function put($calendarData) { 100 101 if (is_resource($calendarData)) { 102 $calendarData = stream_get_contents($calendarData); 103 } 104 $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData); 105 $this->objectData['calendardata'] = $calendarData; 106 $this->objectData['etag'] = $etag; 107 108 return $etag; 109 110 } 111 112 /** 113 * Deletes the calendar object 114 * 115 * @return void 116 */ 117 function delete() { 118 119 $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); 120 121 } 122 123 /** 124 * Returns the mime content-type 125 * 126 * @return string 127 */ 128 function getContentType() { 129 130 $mime = 'text/calendar; charset=utf-8'; 131 if (isset($this->objectData['component']) && $this->objectData['component']) { 132 $mime .= '; component=' . $this->objectData['component']; 133 } 134 return $mime; 135 136 } 137 138 /** 139 * Returns an ETag for this object. 140 * 141 * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. 142 * 143 * @return string 144 */ 145 function getETag() { 146 147 if (isset($this->objectData['etag'])) { 148 return $this->objectData['etag']; 149 } else { 150 return '"' . md5($this->get()) . '"'; 151 } 152 153 } 154 155 /** 156 * Returns the last modification date as a unix timestamp 157 * 158 * @return int 159 */ 160 function getLastModified() { 161 162 return $this->objectData['lastmodified']; 163 164 } 165 166 /** 167 * Returns the size of this object in bytes 168 * 169 * @return int 170 */ 171 function getSize() { 172 173 if (array_key_exists('size', $this->objectData)) { 174 return $this->objectData['size']; 175 } else { 176 return strlen($this->get()); 177 } 178 179 } 180 181 /** 182 * Returns the owner principal 183 * 184 * This must be a url to a principal, or null if there's no owner 185 * 186 * @return string|null 187 */ 188 function getOwner() { 189 190 return $this->calendarInfo['principaluri']; 191 192 } 193 194 /** 195 * Returns a group principal 196 * 197 * This must be a url to a principal, or null if there's no owner 198 * 199 * @return string|null 200 */ 201 function getGroup() { 202 203 return null; 204 205 } 206 207 /** 208 * Returns a list of ACE's for this node. 209 * 210 * Each ACE has the following properties: 211 * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 212 * currently the only supported privileges 213 * * 'principal', a url to the principal who owns the node 214 * * 'protected' (optional), indicating that this ACE is not allowed to 215 * be updated. 216 * 217 * @return array 218 */ 219 function getACL() { 220 221 // An alternative acl may be specified in the object data. 222 if (isset($this->objectData['acl'])) { 223 return $this->objectData['acl']; 224 } 225 226 // The default ACL 227 return [ 228 [ 229 'privilege' => '{DAV:}read', 230 'principal' => $this->calendarInfo['principaluri'], 231 'protected' => true, 232 ], 233 [ 234 'privilege' => '{DAV:}write', 235 'principal' => $this->calendarInfo['principaluri'], 236 'protected' => true, 237 ], 238 [ 239 'privilege' => '{DAV:}read', 240 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', 241 'protected' => true, 242 ], 243 [ 244 'privilege' => '{DAV:}write', 245 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', 246 'protected' => true, 247 ], 248 [ 249 'privilege' => '{DAV:}read', 250 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', 251 'protected' => true, 252 ], 253 254 ]; 255 256 } 257 258 /** 259 * Updates the ACL 260 * 261 * This method will receive a list of new ACE's. 262 * 263 * @param array $acl 264 * @return void 265 */ 266 function setACL(array $acl) { 267 268 throw new \Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); 269 270 } 271 272 /** 273 * Returns the list of supported privileges for this node. 274 * 275 * The returned data structure is a list of nested privileges. 276 * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple 277 * standard structure. 278 * 279 * If null is returned from this method, the default privilege set is used, 280 * which is fine for most common usecases. 281 * 282 * @return array|null 283 */ 284 function getSupportedPrivilegeSet() { 285 286 return null; 287 288 } 289 290} 291