1<?php 2 3use \Sabre\VObject; 4use \Sabre\CalDAV; 5use \Sabre\DAV; 6use \Sabre\DAV\Exception\Forbidden; 7/** 8 * PDO CalDAV backend for DokuWiki - based on Sabre's CalDAV backend 9 * 10 * This backend is used to store calendar-data in a PDO database, such as 11 * sqlite or MySQL 12 * 13 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 14 * @author Evert Pot (http://evertpot.com/) 15 * @license http://sabre.io/license/ Modified BSD License 16 */ 17class DokuWikiSabreCalendarBackend extends \Sabre\CalDAV\Backend\AbstractBackend 18{ 19 20 21 /** 22 * DokuWiki PlugIn Helper 23 */ 24 protected $hlp = null; 25 /** 26 27 /** 28 * List of CalDAV properties, and how they map to database fieldnames 29 * Add your own properties by simply adding on to this array. 30 * 31 * Note that only string-based properties are supported here. 32 * 33 * @var array 34 */ 35 public $propertyMap = array( 36 '{DAV:}displayname' => 'displayname', 37 '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', 38 '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', 39 //'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', 40 //'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', 41 ); 42 43 /** 44 * Creates the backend 45 * 46 * @param \PDO $pdo 47 */ 48 function __construct(&$hlp) 49 { 50 51 $this->hlp = $hlp; 52 53 } 54 55 /** 56 * Returns a list of calendars for a principal. 57 * 58 * Every project is an array with the following keys: 59 * * id, a unique id that will be used by other functions to modify the 60 * calendar. This can be the same as the uri or a database key. 61 * * uri. This is just the 'base uri' or 'filename' of the calendar. 62 * * principaluri. The owner of the calendar. Almost always the same as 63 * principalUri passed to this method. 64 * 65 * Furthermore it can contain webdav properties in clark notation. A very 66 * common one is '{DAV:}displayname'. 67 * 68 * Many clients also require: 69 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set 70 * For this property, you can just return an instance of 71 * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet. 72 * 73 * If you return {http://sabredav.org/ns}read-only and set the value to 1, 74 * ACL will automatically be put in read-only mode. 75 * 76 * @param string $principalUri 77 * @return array 78 */ 79 function getCalendarsForUser($principalUri) 80 { 81 dbglog('calendarBackendDokuwiki::getCalendarsForUser: '.$principalUri); 82 $fields = array_values($this->propertyMap); 83 $fields[] = 'id'; 84 $fields[] = 'uri'; 85 $fields[] = 'synctoken'; 86 $fields[] = 'components'; 87 $fields[] = 'principaluri'; 88 $fields[] = 'transparent'; 89 90 $idInfo = $this->hlp->getCalendarIdsForUser($principalUri); 91 $calendars = array(); 92 foreach($idInfo as $id => $data) 93 { 94 $row = $this->hlp->getCalendarSettings($id); 95 $components = array(); 96 if ($row['components']) 97 { 98 $components = explode(',', $row['components']); 99 } 100 101 $calendar = array( 102 'id' => $row['id'], 103 'uri' => $row['uri'], 104 'principaluri' => $principalUri,//Overwrite principaluri from database, we actually don't need it. 105 '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'), 106 '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', 107 '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components), 108 //'{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), 109 ); 110 if($idInfo[$row['id']]['readonly'] === true) 111 $calendar['{http://sabredav.org/ns}read-only'] = '1'; 112 113 114 foreach ($this->propertyMap as $xmlName => $dbName) 115 { 116 $calendar[$xmlName] = $row[$dbName]; 117 } 118 119 $calendars[] = $calendar; 120 121 } 122 dbglog($calendars); 123 return $calendars; 124 125 } 126 127 /** 128 * Creates a new calendar for a principal. 129 * 130 * If the creation was a success, an id must be returned that can be used 131 * to reference this calendar in other methods, such as updateCalendar. 132 * 133 * @param string $principalUri 134 * @param string $calendarUri 135 * @param array $properties 136 * @return string 137 */ 138 function createCalendar($principalUri, $calendarUri, array $properties) 139 { 140 dbglog('calendarBackendDokuwiki::createCalendar called, returning false'); 141 return false; 142 } 143 144 /** 145 * Updates properties for a calendar. 146 * 147 * The list of mutations is stored in a Sabre\DAV\PropPatch object. 148 * To do the actual updates, you must tell this object which properties 149 * you're going to process with the handle() method. 150 * 151 * Calling the handle method is like telling the PropPatch object "I 152 * promise I can handle updating this property". 153 * 154 * Read the PropPatch documenation for more info and examples. 155 * 156 * @param string $calendarId 157 * @param \Sabre\DAV\PropPatch $propPatch 158 * @return void 159 */ 160 function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) 161 { 162 dbglog('calendarBackendDokuwiki::updateCalendar for calendarId '.$calendarId); 163 $supportedProperties = array_keys($this->propertyMap); 164 165 $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) 166 { 167 foreach ($mutations as $propertyName => $propertyValue) 168 { 169 170 switch ($propertyName) 171 { 172 case '{DAV:}displayname' : 173 dbglog('updateCalendarName'); 174 $this->hlp->updateCalendarName($calendarId, $propertyValue); 175 break; 176 case '{urn:ietf:params:xml:ns:caldav}calendar-description': 177 dbglog('updateCalendarDescription'); 178 $this->hlp->updateCalendarDescription($calendarId, $propertyValue); 179 break; 180 case '{urn:ietf:params:xml:ns:caldav}calendar-timezone': 181 dbglog('updateCalendarTimezone'); 182 $this->hlp->updateCalendarTimezone($calendarId, $propertyValue); 183 break; 184 default : 185 break; 186 } 187 188 } 189 return true; 190 191 }); 192 193 } 194 195 /** 196 * Delete a calendar and all it's objects 197 * 198 * @param string $calendarId 199 * @return void 200 */ 201 function deleteCalendar($calendarId) 202 { 203 dbglog('calendarBackendDokuwiki::deleteCalendar called, returning false'); 204 return; 205 } 206 207 /** 208 * Returns all calendar objects within a calendar. 209 * 210 * Every item contains an array with the following keys: 211 * * calendardata - The iCalendar-compatible calendar data 212 * * uri - a unique key which will be used to construct the uri. This can 213 * be any arbitrary string, but making sure it ends with '.ics' is a 214 * good idea. This is only the basename, or filename, not the full 215 * path. 216 * * lastmodified - a timestamp of the last modification time 217 * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: 218 * ' "abcdef"') 219 * * size - The size of the calendar objects, in bytes. 220 * * component - optional, a string containing the type of object, such 221 * as 'vevent' or 'vtodo'. If specified, this will be used to populate 222 * the Content-Type header. 223 * 224 * Note that the etag is optional, but it's highly encouraged to return for 225 * speed reasons. 226 * 227 * The calendardata is also optional. If it's not returned 228 * 'getCalendarObject' will be called later, which *is* expected to return 229 * calendardata. 230 * 231 * If neither etag or size are specified, the calendardata will be 232 * used/fetched to determine these numbers. If both are specified the 233 * amount of times this is needed is reduced by a great degree. 234 * 235 * @param string $calendarId 236 * @return array 237 */ 238 function getCalendarObjects($calendarId) 239 { 240 dbglog('calendarBackendDokuwiki::getCalendarObjects for calendarId '.$calendarId); 241 $arr = $this->hlp->getCalendarObjects($calendarId); 242 $result = array(); 243 foreach ($arr as $row) 244 { 245 $result[] = array( 246 'id' => $row['id'], 247 'uri' => $row['uri'], 248 'lastmodified' => $row['lastmodified'], 249 'etag' => '"' . $row['etag'] . '"', 250 'calendarid' => $row['calendarid'], 251 'size' => (int)$row['size'], 252 'component' => strtolower($row['componenttype']), 253 ); 254 } 255 dbglog($result); 256 return $result; 257 258 } 259 260 /** 261 * Returns information from a single calendar object, based on it's object 262 * uri. 263 * 264 * The object uri is only the basename, or filename and not a full path. 265 * 266 * The returned array must have the same keys as getCalendarObjects. The 267 * 'calendardata' object is required here though, while it's not required 268 * for getCalendarObjects. 269 * 270 * This method must return null if the object did not exist. 271 * 272 * @param string $calendarId 273 * @param string $objectUri 274 * @return array|null 275 */ 276 function getCalendarObject($calendarId, $objectUri) 277 { 278 dbglog('calendarBackendDokuwiki::getCalendarObject for calendarId '.$calendarId.' and objectUri '.$objectUri); 279 $row = $this->hlp->getCalendarObjectByUri($calendarId, $objectUri); 280 dbglog($row); 281 if (!$row) 282 return null; 283 284 return array( 285 'id' => $row['id'], 286 'uri' => $row['uri'], 287 'lastmodified' => $row['lastmodified'], 288 'etag' => '"' . $row['etag'] . '"', 289 'calendarid' => $row['calendarid'], 290 'size' => (int)$row['size'], 291 'calendardata' => $row['calendardata'], 292 'component' => strtolower($row['componenttype']), 293 ); 294 295 } 296 297 /** 298 * Returns a list of calendar objects. 299 * 300 * This method should work identical to getCalendarObject, but instead 301 * return all the calendar objects in the list as an array. 302 * 303 * If the backend supports this, it may allow for some speed-ups. 304 * 305 * @param mixed $calendarId 306 * @param array $uris 307 * @return array 308 */ 309 function getMultipleCalendarObjects($calendarId, array $uris) 310 { 311 dbglog('calendarBackendDokuwiki::getMultipleCalendarObjects for calendarId '.$calendarId); 312 dbglog($uris); 313 $arr = $this->hlp->getMultipleCalendarObjectsByUri($calendarId, $uris); 314 315 $result = array(); 316 foreach($arr as $row) 317 { 318 319 $result[] = array( 320 'id' => $row['id'], 321 'uri' => $row['uri'], 322 'lastmodified' => $row['lastmodified'], 323 'etag' => '"' . $row['etag'] . '"', 324 'calendarid' => $row['calendarid'], 325 'size' => (int)$row['size'], 326 'calendardata' => $row['calendardata'], 327 'component' => strtolower($row['componenttype']), 328 ); 329 330 } 331 dbglog($result); 332 return $result; 333 334 } 335 336 337 /** 338 * Creates a new calendar object. 339 * 340 * The object uri is only the basename, or filename and not a full path. 341 * 342 * It is possible return an etag from this function, which will be used in 343 * the response to this PUT request. Note that the ETag must be surrounded 344 * by double-quotes. 345 * 346 * However, you should only really return this ETag if you don't mangle the 347 * calendar-data. If the result of a subsequent GET to this object is not 348 * the exact same as this request body, you should omit the ETag. 349 * 350 * @param mixed $calendarId 351 * @param string $objectUri 352 * @param string $calendarData 353 * @return string|null 354 */ 355 function createCalendarObject($calendarId, $objectUri, $calendarData) 356 { 357 dbglog('calendarBackendDokuwiki::createCalendarObject for calendarId '.$calendarId.' and objectUri '.$objectUri); 358 dbglog($calendarData); 359 $etag = $this->hlp->addCalendarEntryToCalendarByICS($calendarId, $objectUri, $calendarData); 360 dbglog($etag); 361 362 return '"' . $etag . '"'; 363 } 364 365 /** 366 * Updates an existing calendarobject, based on it's uri. 367 * 368 * The object uri is only the basename, or filename and not a full path. 369 * 370 * It is possible return an etag from this function, which will be used in 371 * the response to this PUT request. Note that the ETag must be surrounded 372 * by double-quotes. 373 * 374 * However, you should only really return this ETag if you don't mangle the 375 * calendar-data. If the result of a subsequent GET to this object is not 376 * the exact same as this request body, you should omit the ETag. 377 * 378 * @param mixed $calendarId 379 * @param string $objectUri 380 * @param string $calendarData 381 * @return string|null 382 */ 383 function updateCalendarObject($calendarId, $objectUri, $calendarData) 384 { 385 dbglog('calendarBackendDokuwiki::updateCalendarObject for calendarId '.$calendarId.' and objectUri '.$objectUri); 386 dbglog($calendarData); 387 $etag = $this->hlp->editCalendarEntryToCalendarByICS($calendarId, $objectUri, $calendarData); 388 dbglog($etag); 389 return '"' . $etag. '"'; 390 391 } 392 393 394 395 /** 396 * Deletes an existing calendar object. 397 * 398 * The object uri is only the basename, or filename and not a full path. 399 * 400 * @param string $calendarId 401 * @param string $objectUri 402 * @return void 403 */ 404 function deleteCalendarObject($calendarId, $objectUri) 405 { 406 dbglog('calendarBackendDokuwiki::deleteCalendarObject for calendarId '.$calendarId.' and objectUri '.$objectUri); 407 $this->hlp->deleteCalendarEntryForCalendarByUri($calendarId, $objectUri); 408 409 } 410 411 /** 412 * Performs a calendar-query on the contents of this calendar. 413 * 414 * The calendar-query is defined in RFC4791 : CalDAV. Using the 415 * calendar-query it is possible for a client to request a specific set of 416 * object, based on contents of iCalendar properties, date-ranges and 417 * iCalendar component types (VTODO, VEVENT). 418 * 419 * This method should just return a list of (relative) urls that match this 420 * query. 421 * 422 * The list of filters are specified as an array. The exact array is 423 * documented by \Sabre\CalDAV\CalendarQueryParser. 424 * 425 * Note that it is extremely likely that getCalendarObject for every path 426 * returned from this method will be called almost immediately after. You 427 * may want to anticipate this to speed up these requests. 428 * 429 * This method provides a default implementation, which parses *all* the 430 * iCalendar objects in the specified calendar. 431 * 432 * This default may well be good enough for personal use, and calendars 433 * that aren't very large. But if you anticipate high usage, big calendars 434 * or high loads, you are strongly adviced to optimize certain paths. 435 * 436 * The best way to do so is override this method and to optimize 437 * specifically for 'common filters'. 438 * 439 * Requests that are extremely common are: 440 * * requests for just VEVENTS 441 * * requests for just VTODO 442 * * requests with a time-range-filter on a VEVENT. 443 * 444 * ..and combinations of these requests. It may not be worth it to try to 445 * handle every possible situation and just rely on the (relatively 446 * easy to use) CalendarQueryValidator to handle the rest. 447 * 448 * Note that especially time-range-filters may be difficult to parse. A 449 * time-range filter specified on a VEVENT must for instance also handle 450 * recurrence rules correctly. 451 * A good example of how to interprete all these filters can also simply 452 * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct 453 * as possible, so it gives you a good idea on what type of stuff you need 454 * to think of. 455 * 456 * This specific implementation (for the PDO) backend optimizes filters on 457 * specific components, and VEVENT time-ranges. 458 * 459 * @param string $calendarId 460 * @param array $filters 461 * @return array 462 */ 463 function calendarQuery($calendarId, array $filters) 464 { 465 dbglog('calendarBackendDokuwiki::calendarQuery for calendarId '.$calendarId); 466 dbglog($filters); 467 $result = $this->hlp->calendarQuery($calendarId, $filters); 468 dbglog($result); 469 return $result; 470 } 471 472 /** 473 * Searches through all of a users calendars and calendar objects to find 474 * an object with a specific UID. 475 * 476 * This method should return the path to this object, relative to the 477 * calendar home, so this path usually only contains two parts: 478 * 479 * calendarpath/objectpath.ics 480 * 481 * If the uid is not found, return null. 482 * 483 * This method should only consider * objects that the principal owns, so 484 * any calendars owned by other principals that also appear in this 485 * collection should be ignored. 486 * 487 * @param string $principalUri 488 * @param string $uid 489 * @return string|null 490 */ 491 function getCalendarObjectByUID($principalUri, $uid) 492 { 493 dbglog('calendarBackendDokuwiki::getCalendarObjectByUID for principalUri '.$principalUri.' and uid '.$uid); 494 $calids = array_keys($this->hlp->getCalendarIsForUser($principalUri)); 495 $event = $this->hlp->getEventWithUid($uid); 496 497 if(in_array($event['calendarid'], $calids)) 498 { 499 $settings = $this->hlp->getCalendarSettings($event['calendarid']); 500 return $settings['uri'] . '/' . $event['uri']; 501 } 502 return null; 503 } 504 505 /** 506 * The getChanges method returns all the changes that have happened, since 507 * the specified syncToken in the specified calendar. 508 * 509 * This function should return an array, such as the following: 510 * 511 * [ 512 * 'syncToken' => 'The current synctoken', 513 * 'added' => [ 514 * 'new.txt', 515 * ], 516 * 'modified' => [ 517 * 'modified.txt', 518 * ], 519 * 'deleted' => [ 520 * 'foo.php.bak', 521 * 'old.txt' 522 * ] 523 * ]; 524 * 525 * The returned syncToken property should reflect the *current* syncToken 526 * of the calendar, as reported in the {http://sabredav.org/ns}sync-token 527 * property this is needed here too, to ensure the operation is atomic. 528 * 529 * If the $syncToken argument is specified as null, this is an initial 530 * sync, and all members should be reported. 531 * 532 * The modified property is an array of nodenames that have changed since 533 * the last token. 534 * 535 * The deleted property is an array with nodenames, that have been deleted 536 * from collection. 537 * 538 * The $syncLevel argument is basically the 'depth' of the report. If it's 539 * 1, you only have to report changes that happened only directly in 540 * immediate descendants. If it's 2, it should also include changes from 541 * the nodes below the child collections. (grandchildren) 542 * 543 * The $limit argument allows a client to specify how many results should 544 * be returned at most. If the limit is not specified, it should be treated 545 * as infinite. 546 * 547 * If the limit (infinite or not) is higher than you're willing to return, 548 * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. 549 * 550 * If the syncToken is expired (due to data cleanup) or unknown, you must 551 * return null. 552 * 553 * The limit is 'suggestive'. You are free to ignore it. 554 * 555 * @param string $calendarId 556 * @param string $syncToken 557 * @param int $syncLevel 558 * @param int $limit 559 * @return array 560 */ 561 function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) 562 { 563 dbglog('calendarBackendDokuwiki::getChangesForCalendar for calendarId '.$calendarId.' and syncToken '.$syncToken.' and syncLevel '.$syncLevel); 564 $result = $this->hlp->getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit); 565 dbglog($result); 566 return $result; 567 } 568 569 /** 570 * Returns a list of subscriptions for a principal. 571 * 572 * Every subscription is an array with the following keys: 573 * * id, a unique id that will be used by other functions to modify the 574 * subscription. This can be the same as the uri or a database key. 575 * * uri. This is just the 'base uri' or 'filename' of the subscription. 576 * * principaluri. The owner of the subscription. Almost always the same as 577 * principalUri passed to this method. 578 * * source. Url to the actual feed 579 * 580 * Furthermore, all the subscription info must be returned too: 581 * 582 * 1. {DAV:}displayname 583 * 2. {http://apple.com/ns/ical/}refreshrate 584 * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos 585 * should not be stripped). 586 * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms 587 * should not be stripped). 588 * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if 589 * attachments should not be stripped). 590 * 7. {http://apple.com/ns/ical/}calendar-color 591 * 8. {http://apple.com/ns/ical/}calendar-order 592 * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set 593 * (should just be an instance of 594 * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of 595 * default components). 596 * 597 * @param string $principalUri 598 * @return array 599 */ 600 function getSubscriptionsForUser($principalUri) 601 { 602 dbglog('calendarBackendDokuwiki::getSubscriptionsForUser with principalUri '.$principalUri.', returning empty array()'); 603 return array(); 604 605 } 606 607 /** 608 * Creates a new subscription for a principal. 609 * 610 * If the creation was a success, an id must be returned that can be used to reference 611 * this subscription in other methods, such as updateSubscription. 612 * 613 * @param string $principalUri 614 * @param string $uri 615 * @param array $properties 616 * @return mixed 617 */ 618 function createSubscription($principalUri, $uri, array $properties) 619 { 620 dbglog('calendarBackendDokuwiki::createSubscription for principalUri '.$principalUri.' and uri '.$uri.', returning null'); 621 return null; 622 623 } 624 625 /** 626 * Updates a subscription 627 * 628 * The list of mutations is stored in a Sabre\DAV\PropPatch object. 629 * To do the actual updates, you must tell this object which properties 630 * you're going to process with the handle() method. 631 * 632 * Calling the handle method is like telling the PropPatch object "I 633 * promise I can handle updating this property". 634 * 635 * Read the PropPatch documenation for more info and examples. 636 * 637 * @param mixed $subscriptionId 638 * @param \Sabre\DAV\PropPatch $propPatch 639 * @return void 640 */ 641 function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) 642 { 643 dbglog('calendarBackendDokuwiki::updateSubscription with subscriptionId '.$subscriptionId.', returning false'); 644 return; 645 } 646 647 /** 648 * Deletes a subscription 649 * 650 * @param mixed $subscriptionId 651 * @return void 652 */ 653 function deleteSubscription($subscriptionId) 654 { 655 dbglog('calendarBackendDokuwiki::deleteSubscription with subscriptionId '.$subscriptionId.', returning'); 656 return; 657 658 } 659 660 /** 661 * Returns a single scheduling object. 662 * 663 * The returned array should contain the following elements: 664 * * uri - A unique basename for the object. This will be used to 665 * construct a full uri. 666 * * calendardata - The iCalendar object 667 * * lastmodified - The last modification date. Can be an int for a unix 668 * timestamp, or a PHP DateTime object. 669 * * etag - A unique token that must change if the object changed. 670 * * size - The size of the object, in bytes. 671 * 672 * @param string $principalUri 673 * @param string $objectUri 674 * @return array 675 */ 676 function getSchedulingObject($principalUri, $objectUri) 677 { 678 dbglog('calendarBackendDokuwiki::getSchedulingObject with principalUri '.$principalUri.' and objectUri '.$objectUri.', returning null'); 679 return null; 680 681 } 682 683 /** 684 * Returns all scheduling objects for the inbox collection. 685 * 686 * These objects should be returned as an array. Every item in the array 687 * should follow the same structure as returned from getSchedulingObject. 688 * 689 * The main difference is that 'calendardata' is optional. 690 * 691 * @param string $principalUri 692 * @return array 693 */ 694 function getSchedulingObjects($principalUri) 695 { 696 dbglog('calendarBackendDokuwiki::getSchedulingObjects for principalUri '.$principalUri.', returning null'); 697 return null; 698 699 } 700 701 /** 702 * Deletes a scheduling object 703 * 704 * @param string $principalUri 705 * @param string $objectUri 706 * @return void 707 */ 708 function deleteSchedulingObject($principalUri, $objectUri) 709 { 710 dbglog('calendarBackendDokuwiki::deleteSchedulingObject for principalUri '.$principalUri.' and objectUri '.$objectUri.', returning'); 711 return; 712 } 713 714 /** 715 * Creates a new scheduling object. This should land in a users' inbox. 716 * 717 * @param string $principalUri 718 * @param string $objectUri 719 * @param string $objectData 720 * @return void 721 */ 722 function createSchedulingObject($principalUri, $objectUri, $objectData) 723 { 724 dbglog('calendarBackendDokuwiki::createSchedulingObject with principalUri '.$principalUri.' and objectUri '.$objectUri.', returning'); 725 return; 726 727 } 728 729} 730