2fdfb80bc3350a764cd2110ec809419c1a75ee6a
[TYPO3CMS/Extensions/caldav.git] / lib / Sabre / CalDAV / Backend / TYPO3.php
1 <?php
2
3 /**
4 * TYPO3 CalDAV backend
5 *
6 * This backend is used to store calendar-data in a TYPO3 database
7 *
8 * @package Sabre
9 * @subpackage CalDAV
10 * @copyright Copyright (C) 2012-2015 Mario Matzulla. All rights reserved.
11 * @author Mario Matzulla (http://www.matzullas.de)
12 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
13 */
14 class Sabre_CalDAV_Backend_TYPO3 extends Sabre_CalDAV_Backend_Abstract {
15
16 /**
17 * pdo
18 *
19 * @var PDO
20 */
21 private $pdo;
22
23 /**
24 * The table name that will be used for calendars
25 *
26 * @var string
27 */
28 protected $calendarTableName;
29
30 /**
31 * The table name that will be used for calendar objects
32 *
33 * @var string
34 */
35 protected $calendarObjectTableName;
36
37 /**
38 * List of CalDAV properties, and how they map to database fieldnames
39 *
40 * Add your own properties by simply adding on to this array
41 *
42 * @var array
43 */
44 public $propertyMap = array (
45 '{DAV:}displayname' => 'title',
46 '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'tx_caldav_data',
47 '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
48 '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
49 '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor'
50 );
51
52 /**
53 * Creates the backend
54 *
55 * @param PDO $pdo
56 */
57 public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
58 $this->pdo = $pdo;
59 $this->calendarTableName = $calendarTableName;
60 $this->calendarObjectTableName = $calendarObjectTableName;
61 }
62
63 /**
64 * Returns a list of calendars for a principal.
65 *
66 * Every project is an array with the following keys:
67 * * id, a unique id that will be used by other functions to modify the
68 * calendar. This can be the same as the uri or a database key.
69 * * uri, which the basename of the uri with which the calendar is
70 * accessed.
71 * * principalUri. The owner of the calendar. Almost always the same as
72 * principalUri passed to this method.
73 *
74 * Furthermore it can contain webdav properties in clark notation. A very
75 * common one is '{DAV:}displayname'.
76 *
77 * @param string $principalUri
78 * @return array
79 */
80 public function getCalendarsForUser($principalUri) {
81 $principalUriParts = explode ( "/", $principalUri );
82 $stmt = $this->pdo->prepare ( "SELECT uid, tx_cal_calendar FROM fe_users WHERE username = ? AND deleted=0" );
83 $stmt->execute ( array (
84 array_pop ( $principalUriParts )
85 ) );
86
87 $calendars = array ();
88
89 while ( $user = $stmt->fetch ( PDO::FETCH_ASSOC ) ) {
90
91 $stmt = $this->pdo->prepare ( "SELECT * FROM tx_cal_calendar WHERE uid in (" . $user ['tx_cal_calendar'] . ")" );
92 $stmt->execute ();
93
94 while ( $row = $stmt->fetch ( PDO::FETCH_ASSOC ) ) {
95
96 $components = explode ( ',', 'VEVENT,VTODO' );
97
98 $calendar = array (
99 'id' => $row ['uid'],
100 'uri' => $row ['title'],
101 'principaluri' => $principalUri,
102 '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row ['tstamp'] ? $row ['tstamp'] : '0',
103 '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet ( $components ),
104 '{DAV:}displayname' => $row ['title'],
105 '{urn:ietf:params:xml:ns:caldav}calendar-description' => '',
106 '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => null,
107 '{http://apple.com/ns/ical/}calendar-order' => 0,
108 '{http://apple.com/ns/ical/}calendar-color' => null
109 );
110
111 $calendars [] = $calendar;
112 }
113 }
114
115 return $calendars;
116 }
117
118 /**
119 * Creates a new calendar for a principal.
120 *
121 * If the creation was a success, an id must be returned that can be used to reference
122 * this calendar in other methods, such as updateCalendar
123 *
124 * @param string $principalUri
125 * @param string $calendarUri
126 * @param array $properties
127 * @return mixed
128 */
129 public function createCalendar($principalUri, $calendarUri, array $properties) {
130 $fieldNames = array (
131 'principaluri',
132 'uri',
133 'ctag'
134 );
135 $values = array (
136 ':principaluri' => $principalUri,
137 ':uri' => $calendarUri,
138 ':ctag' => 1
139 );
140
141 // Default value
142 $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
143 $fieldNames [] = 'components';
144 if (! isset ( $properties [$sccs] )) {
145 $values [':components'] = 'VEVENT,VTODO';
146 } else {
147 if (! ($properties [$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
148 throw new Sabre_DAV_Exception ( 'The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet' );
149 }
150 $values [':components'] = implode ( ',', $properties [$sccs]->getValue () );
151 }
152
153 foreach ( $this->propertyMap as $xmlName => $dbName ) {
154 if (isset ( $properties [$xmlName] )) {
155
156 $myValue = $properties [$xmlName];
157 $values [':' . $dbName] = $properties [$xmlName];
158 $fieldNames [] = $dbName;
159 }
160 }
161
162 $stmt = $this->pdo->prepare ( "INSERT INTO tx_cal_calendar (" . implode ( ', ', $fieldNames ) . ") VALUES (" . implode ( ', ', array_keys ( $values ) ) . ")" );
163 $stmt->execute ( $values );
164
165 return $this->pdo->lastInsertId ();
166 }
167
168 /**
169 * Updates a calendars properties
170 *
171 * The properties array uses the propertyName in clark-notation as key,
172 * and the array value for the property value. In the case a property
173 * should be deleted, the property value will be null.
174 *
175 * This method must be atomic. If one property cannot be changed, the
176 * entire operation must fail.
177 *
178 * If the operation was successful, true can be returned.
179 * If the operation failed, false can be returned.
180 *
181 * Deletion of a non-existant property is always succesful.
182 *
183 * Lastly, it is optional to return detailed information about any
184 * failures. In this case an array should be returned with the following
185 * structure:
186 *
187 * array(
188 * 403 => array(
189 * '{DAV:}displayname' => null,
190 * ),
191 * 424 => array(
192 * '{DAV:}owner' => null,
193 * )
194 * )
195 *
196 * In this example it was forbidden to update {DAV:}displayname.
197 * (403 Forbidden), which in turn also caused {DAV:}owner to fail
198 * (424 Failed Dependency) because the request needs to be atomic.
199 *
200 * @param string $calendarId
201 * @param array $properties
202 * @return bool|array
203 */
204 public function updateCalendar($calendarId, array $properties) {
205 $newValues = array ();
206 $result = array (
207 200 => array (), // Ok
208 403 => array (), // Forbidden
209 424 => array () // Failed Dependency
210 );
211
212 $hasError = false;
213
214 foreach ( $properties as $propertyName => $propertyValue ) {
215
216 // We don't know about this property.
217 if (! isset ( $this->propertyMap [$propertyName] )) {
218 $hasError = true;
219 $result [403] [$propertyName] = null;
220 unset ( $properties [$propertyName] );
221 continue;
222 }
223
224 $fieldName = $this->propertyMap [$propertyName];
225 $newValues [$fieldName] = $propertyValue;
226 }
227
228 // If there were any errors we need to fail the request
229 if ($hasError) {
230 // Properties has the remaining properties
231 foreach ( $properties as $propertyName => $propertyValue ) {
232 $result [424] [$propertyName] = null;
233 }
234
235 // Removing unused statuscodes for cleanliness
236 foreach ( $result as $status => $properties ) {
237 if (is_array ( $properties ) && count ( $properties ) === 0)
238 unset ( $result [$status] );
239 }
240 return $result;
241 }
242
243 // Success
244
245 // Now we're generating the sql query.
246 $valuesSql = array ();
247 foreach ( $newValues as $fieldName => $value ) {
248 $valuesSql [] = $fieldName . ' = ?';
249 }
250 $valuesSql [] = time ();
251
252 $stmt = $this->pdo->prepare ( "UPDATE tx_cal_calendar SET " . implode ( ', ', $valuesSql ) . " WHERE id = ?" );
253 $newValues ['id'] = $calendarId;
254 $stmt->execute ( array_values ( $newValues ) );
255
256 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_calendar WHERE uid = ?' );
257 $stmt->execute ( array (
258 $calendarId
259 ) );
260 $calendarRow = $stmt->fetch ();
261 $this->clearCache ( $calendarRow ['pid'] );
262
263 return true;
264 }
265
266 /**
267 * Delete a calendar and all it's objects
268 *
269 * @param string $calendarId
270 * @return void
271 */
272 public function deleteCalendar($calendarId) {
273 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_calendar WHERE uid = ?' );
274 $stmt->execute ( array (
275 $calendarId
276 ) );
277 $calendarRow = $stmt->fetch ();
278
279 $stmt = $this->pdo->prepare ( 'DELETE FROM tx_cal_event WHERE calendar_id = ?' );
280 $stmt->execute ( array (
281 $calendarId
282 ) );
283
284 $stmt = $this->pdo->prepare ( 'DELETE FROM tx_cal_calendar WHERE uid = ?' );
285 $stmt->execute ( array (
286 $calendarId
287 ) );
288 $this->clearCache ( $calendarRow ['pid'] );
289 }
290
291 /**
292 * Returns all calendar objects within a calendar object.
293 *
294 * Every item contains an array with the following keys:
295 * * id - unique identifier which will be used for subsequent updates
296 * * calendardata - The iCalendar-compatible calnedar data
297 * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
298 * * lastmodified - a timestamp of the last modification time
299 *
300 * @param string $calendarId
301 * @return array
302 */
303 public function getCalendarObjects($calendarId) {
304 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_event WHERE calendar_id = ? AND deleted = 0' );
305 $stmt->execute ( array (
306 $calendarId
307 ) );
308 $eventArray = $stmt->fetchAll ();
309 $preparedArray = Array ();
310 foreach ( $eventArray as $eventRow ) {
311 if ($eventRow ['tx_caldav_uid'] == '' && $eventRow ['icsUid'] == '') {
312 $eventRow ['tx_caldav_uid'] = 'a1b2c3_' . $eventRow ['calendar_id'] . '_' . $eventRow ['uid'];
313 $eventRow ['icsUid'] = $eventRow ['tx_caldav_uid'];
314 $stmt = $this->pdo->prepare ( "UPDATE tx_cal_event SET tx_caldav_uid = ?, icsUid = ? WHERE uid = ?" );
315 $stmt->execute ( Array (
316 $eventRow ['tx_caldav_uid'],
317 $eventRow ['icsUid'],
318 $eventRow ['uid']
319 ) );
320 } else if ($eventRow ['tx_caldav_uid'] == '') {
321 $eventRow ['tx_caldav_uid'] = $eventRow ['icsUid'];
322 $stmt = $this->pdo->prepare ( "UPDATE tx_cal_event SET tx_caldav_uid = ? WHERE uid = ?" );
323 $stmt->execute ( Array (
324 $eventRow ['tx_caldav_uid'],
325 $eventRow ['uid']
326 ) );
327 } else if ($eventRow ['icsUid'] == '') {
328 $eventRow ['icsUid'] = $eventRow ['tx_caldav_uid'];
329 $stmt = $this->pdo->prepare ( "UPDATE tx_cal_event SET icsUid = ? WHERE uid = ?" );
330 $stmt->execute ( Array (
331 $eventRow ['icsUid'],
332 $eventRow ['uid']
333 ) );
334 }
335 $preparedArray [] = Array (
336 'id' => $eventRow ['uid'],
337 'displayname' => $eventRow ['title'],
338 'calendardata' => $eventRow ['tx_caldav_data'],
339 'uri' => $eventRow ['tx_caldav_uid'],
340 'calendarid' => $calendarId,
341 'lastmodified' => $eventRow ['tstamp']
342 );
343 }
344 return $preparedArray;
345 }
346
347 /**
348 * Returns information from a single calendar object, based on it's object uri.
349 *
350 * @param string $calendarId
351 * @param string $objectUri
352 * @return array
353 */
354 public function getCalendarObject($calendarId, $objectUri) {
355 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_event WHERE calendar_id = ? AND tx_caldav_uid = ? AND deleted = 0' );
356 $stmt->execute ( array (
357 $calendarId,
358 $objectUri
359 ) );
360 $eventRow = $stmt->fetch ();
361 if (empty ( $eventRow )) {
362 return Array ();
363 }
364 return Array (
365 'id' => $eventRow ['uid'],
366 'displayname' => $eventRow ['title'],
367 'calendardata' => $eventRow ['tx_caldav_data'],
368 'uri' => $eventRow ['icsUid'],
369 'calendarid' => $calendarId,
370 'lastmodified' => $eventRow ['tstamp']
371 );
372 }
373
374 /**
375 * Creates a new calendar object.
376 *
377 * @param string $calendarId
378 * @param string $objectUri
379 * @param string $calendarData
380 * @return void
381 */
382 public function createCalendarObject($calendarId, $objectUri, $calendarData) {
383 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_calendar WHERE uid = ?' );
384 $stmt->execute ( array (
385 $calendarId
386 ) );
387 $calendarRow = $stmt->fetch ();
388
389 $stmt = $this->pdo->prepare ( 'INSERT INTO tx_cal_event (pid,calendar_id, tx_caldav_uid, tx_caldav_data, tstamp) VALUES (?,?,?,?,?)' );
390 $uid = $this->pdo->lastInsertId ();
391 $stmt->execute ( array (
392 $calendarRow ['pid'],
393 $calendarId,
394 $objectUri,
395 $calendarData,
396 time ()
397 ) );
398 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0' );
399 $stmt->execute ( array (
400 $calendarId
401 ) );
402 $this->updateCalEvent ( $calendarId, $objectUri, $calendarData );
403 $this->clearCache ( $calendarRow ['pid'] );
404 }
405
406 /**
407 * Updates an existing calendarobject, based on it's uri.
408 *
409 * @param string $calendarId
410 * @param string $objectUri
411 * @param string $calendarData
412 * @return void
413 */
414 public function updateCalendarObject($calendarId, $objectUri, $calendarData) {
415 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_event WHERE calendar_id = ?' );
416 $stmt->execute ( array (
417 $calendarId
418 ) );
419 $calendarRow = $stmt->fetch ();
420 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_event SET tx_caldav_data = ?, tstamp = ? WHERE calendar_id = ? AND icsUid = ? AND deleted = 0' );
421 $stmt->execute ( array (
422 $calendarData,
423 time (),
424 $calendarId,
425 $objectUri
426 ) );
427 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0' );
428 $stmt->execute ( array (
429 $calendarId
430 ) );
431 $this->updateCalEvent ( $calendarId, $objectUri, $calendarData );
432 $this->clearCache ( $calendarRow ['pid'] );
433 }
434
435 /**
436 * Deletes an existing calendar object.
437 *
438 * @param string $calendarId
439 * @param string $objectUri
440 * @return void
441 */
442 public function deleteCalendarObject($calendarId, $objectUri) {
443 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_event WHERE calendar_id = ?' );
444 $stmt->execute ( array (
445 $calendarId
446 ) );
447 $calendarRow = $stmt->fetch ();
448
449 $stmt = $this->pdo->prepare ( 'DELETE FROM tx_cal_event WHERE calendar_id = ? AND icsUid = ? AND deleted = 0' );
450 $stmt->execute ( array (
451 $calendarId,
452 $objectUri
453 ) );
454 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_calendar SET tstamp = tstamp + 1 WHERE uid = ? AND deleted = 0' );
455 $stmt->execute ( array (
456 $calendarId
457 ) );
458 $this->clearCache ( $calendarRow ['pid'] );
459 }
460 private function updateCalEvent($calendarId, $objectUri, $calendarData) {
461 $stmt = $this->pdo->prepare ( 'SELECT * FROM tx_cal_calendar WHERE uid = ?' );
462 $stmt->execute ( array (
463 $calendarId
464 ) );
465 $calendarRow = $stmt->fetch ();
466 $pageTSConf = \TYPO3\CMS\Backend\Utility\BackendUtility::getPagesTSconfig ( $calendarRow ['pid'] );
467 $pageIDForPlugin = $calendarRow ['pid'];
468 if ($pageTSConf ['options.'] ['tx_cal_controller.'] ['pageIDForPlugin']) {
469 $pageIDForPlugin = $pageTSConf ['options.'] ['tx_cal_controller.'] ['pageIDForPlugin'];
470 }
471 $calAPI = new \TYPO3\CMS\Cal\Controller\Api ();
472 $calAPI = &$calAPI->tx_cal_api_without ( $pageIDForPlugin );
473
474 $service = new \TYPO3\CMS\Cal\Service\ICalendarService ();
475 $components = $service->getiCalendarFromIcsFile ( $calendarData );
476 $logger = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__);
477
478 foreach ( $components->_components as $component ) {
479 if ($component->getType() == 'vEvent') {
480 $logger->info('is_a ' . $component->getType());
481 $logger->info($component->getAttribute('UID'));
482 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_event SET icsUid = ?, type = ? WHERE tx_caldav_uid = ?' );
483 $stmt->execute ( array (
484 $component->getAttribute ( 'UID' ),
485 0,
486 $objectUri
487 ) );
488 } else if ($component->getType() == 'vTodo') {
489 $logger->info('is_a ' . $component->getType());
490 $logger->info($component->getAttribute('UID'));
491 $stmt = $this->pdo->prepare ( 'UPDATE tx_cal_event SET icsUid = ?, type = ? WHERE tx_caldav_uid = ?' );
492 $stmt->execute ( array (
493 $component->getAttribute ( 'UID' ),
494 4,
495 $objectUri
496 ) );
497 }
498 }
499 $service->insertCalEventsIntoDB ( $components->_components, $calendarId, $calendarRow ['pid'], 0, 0, FALSE );
500 $this->clearCache ( $calendarRow ['pid'] );
501 }
502
503 private function clearCache($pid) {
504 $pageTSConf = \TYPO3\CMS\Backend\Utility\BackendUtility::getPagesTSconfig ( $pid );
505 $pageIDForPlugin = $pid;
506
507 if ($pageTSConf ['TCEMAIN.'] ['clearCacheCmd']) {
508 $pageIDForPlugin = $pageTSConf ['TCEMAIN.'] ['clearCacheCmd'];
509 }
510
511 $tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance ( 'TYPO3\\CMS\\Core\\DataHandling\\DataHandler' );
512 $tce->stripslashes_values = FALSE;
513 $tce->start(array(), array());
514 //$tce->clear_cacheCmd ( $pageIDForPlugin ); // ID of the page for which to clear the cache
515 }
516 }