1<?php 2/** 3 * Helper Class for the DAVCal plugin 4 * This helper does the actual work. 5 * 6 */ 7 8// must be run within Dokuwiki 9if(!defined('DOKU_INC')) die(); 10 11class helper_plugin_davcal extends DokuWiki_Plugin { 12 13 protected $sqlite = null; 14 15 /** 16 * Constructor to load the configuration and the SQLite plugin 17 */ 18 public function helper_plugin_davcal() { 19 $this->sqlite =& plugin_load('helper', 'sqlite'); 20 if(!$this->sqlite) 21 { 22 msg('This plugin requires the sqlite plugin. Please install it.'); 23 return; 24 } 25 26 if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/')) 27 { 28 return; 29 } 30 } 31 32 /** 33 * Set the calendar name and description for a given page with a given 34 * page id. 35 * If the calendar doesn't exist, the calendar is created! 36 * 37 * @param string $name The name of the new calendar 38 * @param string $description The description of the new calendar 39 * @param string $id (optional) The ID of the page 40 * @param string $userid The userid of the creating user 41 * 42 * @return boolean True on success, otherwise false. 43 */ 44 public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 45 { 46 if(is_null($id)) 47 { 48 global $ID; 49 $id = $ID; 50 } 51 if(is_null($userid)) 52 $userid = $_SERVER['REMOTE_USER']; 53 $calid = $this->getCalendarIdForPage($id); 54 if($calid === false) 55 return $this->createCalendarForPage($name, $description, $id, $userid); 56 57 $query = "UPDATE calendars SET displayname=".$this->sqlite->quote_string($name).", ". 58 "description=".$this->sqlite->quote_string($description)." WHERE ". 59 "id=".$this->sqlite->quote_string($calid); 60 $res = $this->sqlite->query($query); 61 if($res !== false) 62 return true; 63 return false; 64 } 65 66 /** 67 * Save the personal settings to the SQLite database 'calendarsettings'. 68 * 69 * @param array $settings The settings array to store 70 * @param string $userid (optional) The userid to store 71 * 72 * @param boolean True on success, otherwise false 73 */ 74 public function savePersonalSettings($settings, $userid = null) 75 { 76 if(is_null($userid)) 77 $userid = $_SERVER['REMOTE_USER']; 78 $this->sqlite->query("BEGIN TRANSACTION"); 79 80 $query = "DELETE FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid); 81 $this->sqlite->query($query); 82 83 foreach($settings as $key => $value) 84 { 85 $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (". 86 $this->sqlite->quote_string($userid).", ". 87 $this->sqlite->quote_string($key).", ". 88 $this->sqlite->quote_string($value).")"; 89 $res = $this->sqlite->query($query); 90 if($res === false) 91 return false; 92 } 93 $this->sqlite->query("COMMIT TRANSACTION"); 94 return true; 95 } 96 97 /** 98 * Retrieve the settings array for a given user id. 99 * Some sane defaults are returned, currently: 100 * 101 * timezone => local 102 * weeknumbers => 0 103 * workweek => 0 104 * 105 * @param string $userid (optional) The user id to retrieve 106 * 107 * @return array The settings array 108 */ 109 public function getPersonalSettings($userid = null) 110 { 111 if(is_null($userid)) 112 $userid = $_SERVER['REMOTE_USER']; 113 // Some sane default settings 114 $settings = array( 115 'timezone' => 'local', 116 'weeknumbers' => '0', 117 'workweek' => '0' 118 ); 119 $query = "SELECT key, value FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid); 120 $res = $this->sqlite->query($query); 121 $arr = $this->sqlite->res2arr($res); 122 foreach($arr as $row) 123 { 124 $settings[$row['key']] = $row['value']; 125 } 126 return $settings; 127 } 128 129 /** 130 * Retrieve the calendar ID based on a page ID from the SQLite table 131 * 'pagetocalendarmapping'. 132 * 133 * @param string $id (optional) The page ID to retrieve the corresponding calendar 134 * 135 * @return mixed the ID on success, otherwise false 136 */ 137 public function getCalendarIdForPage($id = null) 138 { 139 if(is_null($id)) 140 { 141 global $ID; 142 $id = $ID; 143 } 144 145 $query = "SELECT calid FROM pagetocalendarmapping WHERE page=".$this->sqlite->quote_string($id); 146 $res = $this->sqlite->query($query); 147 $row = $this->sqlite->res2row($res); 148 if(isset($row['calid'])) 149 return $row['calid']; 150 else 151 return false; 152 } 153 154 /** 155 * Retrieve the complete calendar id to page mapping. 156 * This is necessary to be able to retrieve a list of 157 * calendars for a given user and check the access rights. 158 * 159 * @return array The mapping array 160 */ 161 public function getCalendarIdToPageMapping() 162 { 163 $query = "SELECT calid, page FROM pagetocalendarmapping"; 164 $res = $this->sqlite->query($query); 165 $arr = $this->sqlite->res2arr($res); 166 return $arr; 167 } 168 169 /** 170 * Retrieve all calendar IDs a given user has access to. 171 * The user is specified by the principalUri, so the 172 * user name is actually split from the URI component. 173 * 174 * Access rights are checked against DokuWiki's ACL 175 * and applied accordingly. 176 * 177 * @param string $principalUri The principal URI to work on 178 * 179 * @return array An associative array of calendar IDs 180 */ 181 public function getCalendarIdsForUser($principalUri) 182 { 183 $user = explode('/', $principalUri); 184 $user = end($user); 185 $mapping = $this->getCalendarIdToPageMapping(); 186 $calids = array(); 187 foreach($mapping as $row) 188 { 189 $id = $row['calid']; 190 $page = $row['page']; 191 $acl = auth_quickaclcheck($page); 192 if($acl >= AUTH_READ) 193 { 194 $write = $acl > AUTH_READ; 195 $calids[$id] = array('readonly' => !$write); 196 } 197 } 198 return $calids; 199 } 200 201 /** 202 * Create a new calendar for a given page ID and set name and description 203 * accordingly. Also update the pagetocalendarmapping table on success. 204 * 205 * @param string $name The calendar's name 206 * @param string $description The calendar's description 207 * @param string $id (optional) The page ID to work on 208 * @param string $userid (optional) The user ID that created the calendar 209 * 210 * @return boolean True on success, otherwise false 211 */ 212 public function createCalendarForPage($name, $description, $id = null, $userid = null) 213 { 214 if(is_null($id)) 215 { 216 global $ID; 217 $id = $ID; 218 } 219 if(is_null($userid)) 220 $userid = $_SERVER['REMOTE_USER']; 221 $values = array('principals/'.$userid, 222 $name, 223 str_replace(array('/', ' ', ':'), '_', $id), 224 $description, 225 'VEVENT,VTODO', 226 0, 227 1); 228 $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) VALUES (".$this->sqlite->quote_and_join($values, ',').");"; 229 $res = $this->sqlite->query($query); 230 if($res === false) 231 return false; 232 233 // Get the new calendar ID 234 $query = "SELECT id FROM calendars WHERE principaluri=".$this->sqlite->quote_string($values[0])." AND ". 235 "displayname=".$this->sqlite->quote_string($values[1])." AND ". 236 "uri=".$this->sqlite->quote_string($values[2])." AND ". 237 "description=".$this->sqlite->quote_string($values[3]); 238 $res = $this->sqlite->query($query); 239 $row = $this->sqlite->res2row($res); 240 241 // Update the pagetocalendarmapping table with the new calendar ID 242 if(isset($row['id'])) 243 { 244 $values = array($id, $row['id']); 245 $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (".$this->sqlite->quote_and_join($values, ',').")"; 246 $res = $this->sqlite->query($query); 247 return ($res !== false); 248 } 249 250 return false; 251 } 252 253 /** 254 * Add a new iCal entry for a given page, i.e. a given calendar. 255 * 256 * The parameter array needs to contain 257 * timezone => The timezone of the entries 258 * detectedtz => The timezone as detected by the browser 259 * eventfrom => The event's start date 260 * eventfromtime => The event's start time 261 * eventto => The event's end date 262 * eventtotime => The event's end time 263 * eventname => The event's name 264 * eventdescription => The event's description 265 * 266 * @param string $id The page ID to work on 267 * @param string $user The user who created the calendar 268 * @param string $params A parameter array with values to create 269 * 270 * @return boolean True on success, otherwise false 271 */ 272 public function addCalendarEntryToCalendarForPage($id, $user, $params) 273 { 274 $settings = $this->getPersonalSettings($user); 275 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 276 $timezone = new \DateTimeZone($settings['timezone']); 277 elseif($settings['timezone'] === 'local') 278 $timezone = new \DateTimeZone($params['detectedtz']); 279 else 280 $timezone = new \DateTimeZone('UTC'); 281 282 // Retrieve dates from settings 283 $startDate = explode('-', $params['eventfrom']); 284 $startTime = explode(':', $params['eventfromtime']); 285 $endDate = explode('-', $params['eventto']); 286 $endTime = explode(':', $params['eventtotime']); 287 288 // Load SabreDAV 289 require_once('vendor/autoload.php'); 290 $vcalendar = new \Sabre\VObject\Component\VCalendar(); 291 292 // Add VCalendar, UID and Event Name 293 $event = $vcalendar->add('VEVENT'); 294 $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 295 $event->add('UID', $uuid); 296 $event->summary = $params['eventname']; 297 298 // Add a description if requested 299 $description = $params['eventdescription']; 300 if($description !== '') 301 $event->add('DESCRIPTION', $description); 302 303 // Create a timestamp for last modified, created and dtstamp values in UTC 304 $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 305 $event->add('DTSTAMP', $dtStamp); 306 $event->add('CREATED', $dtStamp); 307 $event->add('LAST-MODIFIED', $dtStamp); 308 309 // Adjust the start date, based on the given timezone information 310 $dtStart = new \DateTime(); 311 $dtStart->setTimezone($timezone); 312 $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 313 314 // Only add the time values if it's not an allday event 315 if($params['allday'] != '1') 316 $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 317 318 // Adjust the end date, based on the given timezone information 319 $dtEnd = new \DateTime(); 320 $dtEnd->setTimezone($timezone); 321 $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 322 323 // Only add the time values if it's not an allday event 324 if($params['allday'] != '1') 325 $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 326 327 // According to the VCal spec, we need to add a whole day here 328 if($params['allday'] == '1') 329 $dtEnd->add(new \DateInterval('P1D')); 330 331 // Really add Start and End events 332 $dtStartEv = $event->add('DTSTART', $dtStart); 333 $dtEndEv = $event->add('DTEND', $dtEnd); 334 335 // Adjust the DATE format for allday events 336 if($params['allday'] == '1') 337 { 338 $dtStartEv['VALUE'] = 'DATE'; 339 $dtEndEv['VALUE'] = 'DATE'; 340 } 341 342 // Actually add the values to the database 343 $calid = $this->getCalendarIdForPage($id); 344 $uri = uniqid('dokuwiki-').'.ics'; 345 $now = new DateTime(); 346 $eventStr = $vcalendar->serialize(); 347 348 $values = array($calid, 349 $uri, 350 $eventStr, 351 $now->getTimestamp(), 352 'VEVENT', 353 $event->DTSTART->getDateTime()->getTimeStamp(), 354 $event->DTEND->getDateTime()->getTimeStamp(), 355 strlen($eventStr), 356 md5($eventStr), 357 $uuid 358 ); 359 360 $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (".$this->sqlite->quote_and_join($values, ',').")"; 361 $res = $this->sqlite->query($query); 362 363 // If successfully, update the sync token database 364 if($res !== false) 365 { 366 $this->updateSyncTokenLog($calid, $uri, 'added'); 367 return true; 368 } 369 return false; 370 } 371 372 /** 373 * Retrieve the calendar settings of a given calendar id 374 * 375 * @param string $calid The calendar ID 376 * 377 * @return array The calendar settings array 378 */ 379 public function getCalendarSettings($calid) 380 { 381 $query = "SELECT principaluri, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id=".$this->sqlite->quote_string($calid); 382 $res = $this->sqlite->query($query); 383 $row = $this->sqlite->res2row($res); 384 return $row; 385 } 386 387 /** 388 * Retrieve all events that are within a given date range, 389 * based on the timezone setting. 390 * 391 * There is also support for retrieving recurring events, 392 * using Sabre's VObject Iterator. Recurring events are represented 393 * as individual calendar entries with the same UID. 394 * 395 * @param string $id The page ID to work with 396 * @param string $user The user ID to work with 397 * @param string $startDate The start date as a string 398 * @param string $endDate The end date as a string 399 * 400 * @return array An array containing the calendar entries. 401 */ 402 public function getEventsWithinDateRange($id, $user, $startDate, $endDate) 403 { 404 $settings = $this->getPersonalSettings($user); 405 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 406 $timezone = new \DateTimeZone($settings['timezone']); 407 else 408 $timezone = new \DateTimeZone('UTC'); 409 $data = array(); 410 411 // Load SabreDAV 412 require_once('vendor/autoload.php'); 413 $calid = $this->getCalendarIdForPage($id); 414 $startTs = new \DateTime($startDate); 415 $endTs = new \DateTime($endDate); 416 417 // Retrieve matching calendar objects 418 $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid=". 419 $this->sqlite->quote_string($calid)." AND firstoccurence < ". 420 $this->sqlite->quote_string($endTs->getTimestamp())." AND lastoccurence > ". 421 $this->sqlite->quote_string($startTs->getTimestamp()); 422 $res = $this->sqlite->query($query); 423 $arr = $this->sqlite->res2arr($res); 424 425 // Parse individual calendar entries 426 foreach($arr as $row) 427 { 428 if(isset($row['calendardata'])) 429 { 430 $entry = array(); 431 $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 432 $recurrence = $vcal->VEVENT->RRULE; 433 // If it is a recurring event, pass it through Sabre's EventIterator 434 if($recurrence != null) 435 { 436 $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 437 $rEvents->rewind(); 438 $done = false; 439 while($rEvents->valid() && !$done) 440 { 441 $event = $rEvents->getEventObject(); 442 // If we are after the given time range, exit 443 if(($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()) && 444 ($rEvents->getDtEnd()->getTimestamp() > $endTs->getTimestamp())) 445 $done = true; 446 447 // If we are before the given time range, continue 448 if($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()) 449 { 450 $rEvents->next(); 451 continue; 452 } 453 454 // If we are within the given time range, parse the event 455 $data[] = $this->convertIcalDataToEntry($event, $timezone, $row['uid']); 456 $rEvents->next(); 457 } 458 } 459 else 460 $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $timezone, $row['uid']); 461 } 462 } 463 return $data; 464 } 465 466 /** 467 * Helper function that parses the iCal data of a VEVENT to a calendar entry. 468 * 469 * @param \Sabre\VObject\VEvent $event The event to parse 470 * @param \DateTimeZone $timezone The timezone object 471 * @param string $uid The entry's UID 472 * 473 * @return array The parse calendar entry 474 */ 475 private function convertIcalDataToEntry($event, $timezone, $uid) 476 { 477 $entry = array(); 478 $start = $event->DTSTART; 479 // Parse only if the start date/time is present 480 if($start !== null) 481 { 482 $dtStart = $start->getDateTime(); 483 $dtStart->setTimezone($timezone); 484 $entry['start'] = $dtStart->format(\DateTime::ATOM); 485 if($start['VALUE'] == 'DATE') 486 $entry['allDay'] = true; 487 else 488 $entry['allDay'] = false; 489 } 490 $end = $event->DTEND; 491 // Parse onlyl if the end date/time is present 492 if($end !== null) 493 { 494 $dtEnd = $end->getDateTime(); 495 $dtEnd->setTimezone($timezone); 496 $entry['end'] = $dtEnd->format(\DateTime::ATOM); 497 } 498 $description = $event->DESCRIPTION; 499 if($description !== null) 500 $entry['description'] = (string)$description; 501 else 502 $entry['description'] = ''; 503 $entry['title'] = (string)$event->summary; 504 $entry['id'] = $uid; 505 return $entry; 506 } 507 508 /** 509 * Retrieve an event by its UID 510 * 511 * @param string $uid The event's UID 512 * 513 * @return mixed The table row with the given event 514 */ 515 public function getEventWithUid($uid) 516 { 517 $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid=". 518 $this->sqlite->quote_string($uid); 519 $res = $this->sqlite->query($query); 520 $row = $this->sqlite->res2row($res); 521 return $row; 522 } 523 524 /** 525 * Retrieve all calendar events for a given calendar ID 526 * 527 * @param string $calid The calendar's ID 528 * 529 * @return array An array containing all calendar data 530 */ 531 public function getAllCalendarEvents($calid) 532 { 533 $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid=". 534 $this->sqlite->quote_string($calid); 535 $res = $this->sqlite->query($query); 536 $arr = $this->sqlite->res2arr($res); 537 return $arr; 538 } 539 540 /** 541 * Edit a calendar entry for a page, given by its parameters. 542 * The params array has the same format as @see addCalendarEntryForPage 543 * 544 * @param string $id The page's ID to work on 545 * @param string $user The user's ID to work on 546 * @param array $params The parameter array for the edited calendar event 547 * 548 * @return boolean True on success, otherwise false 549 */ 550 public function editCalendarEntryForPage($id, $user, $params) 551 { 552 $settings = $this->getPersonalSettings($user); 553 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 554 $timezone = new \DateTimeZone($settings['timezone']); 555 elseif($settings['timezone'] === 'local') 556 $timezone = new \DateTimeZone($params['detectedtz']); 557 else 558 $timezone = new \DateTimeZone('UTC'); 559 560 // Parse dates 561 $startDate = explode('-', $params['eventfrom']); 562 $startTime = explode(':', $params['eventfromtime']); 563 $endDate = explode('-', $params['eventto']); 564 $endTime = explode(':', $params['eventtotime']); 565 566 // Retrieve the existing event based on the UID 567 $uid = $params['uid']; 568 $event = $this->getEventWithUid($uid); 569 570 // Load SabreDAV 571 require_once('vendor/autoload.php'); 572 if(!isset($event['calendardata'])) 573 return false; 574 $uri = $event['uri']; 575 $calid = $event['calendarid']; 576 577 // Parse the existing event 578 $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 579 $vevent = $vcal->VEVENT; 580 581 // Set the new event values 582 $vevent->summary = $params['eventname']; 583 $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 584 $description = $params['eventdescription']; 585 586 // Remove existing timestamps to overwrite them 587 $vevent->remove('DESCRIPTION'); 588 $vevent->remove('DTSTAMP'); 589 $vevent->remove('LAST-MODIFIED'); 590 591 // Add new time stamps and description 592 $vevent->add('DTSTAMP', $dtStamp); 593 $vevent->add('LAST-MODIFIED', $dtStamp); 594 if($description !== '') 595 $vevent->add('DESCRIPTION', $description); 596 597 // Setup DTSTART 598 $dtStart = new \DateTime(); 599 $dtStart->setTimezone($timezone); 600 $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 601 if($params['allday'] != '1') 602 $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 603 604 // Setup DETEND 605 $dtEnd = new \DateTime(); 606 $dtEnd->setTimezone($timezone); 607 $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 608 if($params['allday'] != '1') 609 $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 610 611 // According to the VCal spec, we need to add a whole day here 612 if($params['allday'] == '1') 613 $dtEnd->add(new \DateInterval('P1D')); 614 $vevent->remove('DTSTART'); 615 $vevent->remove('DTEND'); 616 $dtStartEv = $vevent->add('DTSTART', $dtStart); 617 $dtEndEv = $vevent->add('DTEND', $dtEnd); 618 619 // Remove the time for allday events 620 if($params['allday'] == '1') 621 { 622 $dtStartEv['VALUE'] = 'DATE'; 623 $dtEndEv['VALUE'] = 'DATE'; 624 } 625 $now = new DateTime(); 626 $eventStr = $vcal->serialize(); 627 628 // Actually write to the database 629 $query = "UPDATE calendarobjects SET calendardata=".$this->sqlite->quote_string($eventStr). 630 ", lastmodified=".$this->sqlite->quote_string($now->getTimestamp()). 631 ", firstoccurence=".$this->sqlite->quote_string($dtStart->getTimestamp()). 632 ", lastoccurence=".$this->sqlite->quote_string($dtEnd->getTimestamp()). 633 ", size=".strlen($eventStr). 634 ", etag=".$this->sqlite->quote_string(md5($eventStr)). 635 " WHERE uid=".$this->sqlite->quote_string($uid); 636 $res = $this->sqlite->query($query); 637 if($res !== false) 638 { 639 $this->updateSyncTokenLog($calid, $uri, 'modified'); 640 return true; 641 } 642 return false; 643 } 644 645 /** 646 * Delete a calendar entry for a given page. Actually, the event is removed 647 * based on the entry's UID, so that page ID is no used. 648 * 649 * @param string $id The page's ID (unused) 650 * @param array $params The parameter array to work with 651 * 652 * @return boolean True 653 */ 654 public function deleteCalendarEntryForPage($id, $params) 655 { 656 $uid = $params['uid']; 657 $event = $this->getEventWithUid($uid); 658 $calid = $event['calendarid']; 659 $uri = $event['uri']; 660 $query = "DELETE FROM calendarobjects WHERE uid=".$this->sqlite->quote_string($uid); 661 $res = $this->sqlite->query($query); 662 if($res !== false) 663 { 664 $this->updateSyncTokenLog($calid, $uri, 'deleted'); 665 } 666 return true; 667 } 668 669 /** 670 * Retrieve the current sync token for a calendar 671 * 672 * @param string $calid The calendar id 673 * 674 * @return mixed The synctoken or false 675 */ 676 public function getSyncTokenForCalendar($calid) 677 { 678 $row = $this->getCalendarSettings($calid); 679 if(isset($row['synctoken'])) 680 return $row['synctoken']; 681 return false; 682 } 683 684 /** 685 * Helper function to convert the operation name to 686 * an operation code as stored in the database 687 * 688 * @param string $operationName The operation name 689 * 690 * @return mixed The operation code or false 691 */ 692 public function operationNameToOperation($operationName) 693 { 694 switch($operationName) 695 { 696 case 'added': 697 return 1; 698 break; 699 case 'modified': 700 return 2; 701 break; 702 case 'deleted': 703 return 3; 704 break; 705 } 706 return false; 707 } 708 709 /** 710 * Update the sync token log based on the calendar id and the 711 * operation that was performed. 712 * 713 * @param string $calid The calendar ID that was modified 714 * @param string $uri The calendar URI that was modified 715 * @param string $operation The operation that was performed 716 * 717 * @return boolean True on success, otherwise false 718 */ 719 private function updateSyncTokenLog($calid, $uri, $operation) 720 { 721 $currentToken = $this->getSyncTokenForCalendar($calid); 722 $operationCode = $this->operationNameToOperation($operation); 723 if(($operationCode === false) || ($currentToken === false)) 724 return false; 725 $values = array($uri, 726 $currentToken, 727 $calid, 728 $operationCode 729 ); 730 $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(". 731 $this->sqlite->quote_and_join($values, ',').")"; 732 $res = $this->sqlite->query($query); 733 if($res === false) 734 return false; 735 $currentToken++; 736 $query = "UPDATE calendars SET synctoken=".$this->sqlite->quote_string($currentToken)." WHERE id=". 737 $this->sqlite->quote_string($calid); 738 $res = $this->sqlite->query($query); 739 return ($res !== false); 740 } 741 742 /** 743 * Return the sync URL for a given Page, i.e. a calendar 744 * 745 * @param string $id The page's ID 746 * @param string $user (optional) The user's ID 747 * 748 * @return mixed The sync url or false 749 */ 750 public function getSyncUrlForPage($id, $user = null) 751 { 752 if(is_null($user)) 753 $user = $_SERVER['REMOTE_USER']; 754 755 $calid = $this->getCalendarIdForPage($id); 756 if($calid === false) 757 return false; 758 759 $calsettings = $this->getCalendarSettings($calid); 760 if(!isset($calsettings['uri'])) 761 return false; 762 763 $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 764 return $syncurl; 765 } 766 767 /** 768 * Return the private calendar's URL for a given page 769 * 770 * @param string $id the page ID 771 * 772 * @return mixed The private URL or false 773 */ 774 public function getPrivateURLForPage($id) 775 { 776 $calid = $this->getCalendarIdForPage($id); 777 if($calid === false) 778 return false; 779 780 return $this->getPrivateURLForCalendar($calid); 781 } 782 783 /** 784 * Return the private calendar's URL for a given calendar ID 785 * 786 * @param string $calid The calendar's ID 787 * 788 * @return mixed The private URL or false 789 */ 790 public function getPrivateURLForCalendar($calid) 791 { 792 $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid=".$this->sqlite->quote_string($calid); 793 $res = $this->sqlite->query($query); 794 $row = $this->sqlite->res2row($res); 795 if(!isset($row['url'])) 796 { 797 $url = uniqid("dokuwiki-").".ics"; 798 $values = array( 799 $url, 800 $calid 801 ); 802 $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(". 803 $this->sqlite->quote_and_join($values, ", ").")"; 804 $res = $this->sqlite->query($query); 805 if($res === false) 806 return false; 807 } 808 else 809 { 810 $url = $row['url']; 811 } 812 return DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 813 } 814 815 /** 816 * Retrieve the calendar ID for a given private calendar URL 817 * 818 * @param string $url The private URL 819 * 820 * @return mixed The calendar ID or false 821 */ 822 public function getCalendarForPrivateURL($url) 823 { 824 $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url=".$this->sqlite->quote_string($url); 825 $res = $this->sqlite->query($query); 826 $row = $this->sqlite->res2row($res); 827 if(!isset($row['calid'])) 828 return false; 829 return $row['calid']; 830 } 831 832 /** 833 * Return a given calendar as ICS feed, i.e. all events in one ICS file. 834 * 835 * @param string $caldi The calendar ID to retrieve 836 * 837 * @return mixed The calendar events as string or false 838 */ 839 public function getCalendarAsICSFeed($calid) 840 { 841 $calSettings = $this->getCalendarSettings($calid); 842 if($calSettings === false) 843 return false; 844 $events = $this->getAllCalendarEvents($calid); 845 if($events === false) 846 return false; 847 848 $out = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\nCALSCALE:GREGORIAN\nX-WR-CALNAME:"; 849 $out .= $calSettings['displayname']."\n"; 850 foreach($events as $event) 851 { 852 $out .= rtrim($event['calendardata']); 853 $out .= "\n"; 854 } 855 $out .= "END:VCALENDAR\n"; 856 return $out; 857 } 858 859} 860