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'], true); 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 * @param boolean $recurring (optional) Set to true to define a recurring event 473 * 474 * @return array The parse calendar entry 475 */ 476 private function convertIcalDataToEntry($event, $timezone, $uid, $recurring = false) 477 { 478 $entry = array(); 479 $start = $event->DTSTART; 480 // Parse only if the start date/time is present 481 if($start !== null) 482 { 483 $dtStart = $start->getDateTime(); 484 $dtStart->setTimezone($timezone); 485 $entry['start'] = $dtStart->format(\DateTime::ATOM); 486 if($start['VALUE'] == 'DATE') 487 $entry['allDay'] = true; 488 else 489 $entry['allDay'] = false; 490 } 491 $end = $event->DTEND; 492 // Parse onlyl if the end date/time is present 493 if($end !== null) 494 { 495 $dtEnd = $end->getDateTime(); 496 $dtEnd->setTimezone($timezone); 497 $entry['end'] = $dtEnd->format(\DateTime::ATOM); 498 } 499 $description = $event->DESCRIPTION; 500 if($description !== null) 501 $entry['description'] = (string)$description; 502 else 503 $entry['description'] = ''; 504 $entry['title'] = (string)$event->summary; 505 $entry['id'] = $uid; 506 $entry['recurring'] = $recurring; 507 return $entry; 508 } 509 510 /** 511 * Retrieve an event by its UID 512 * 513 * @param string $uid The event's UID 514 * 515 * @return mixed The table row with the given event 516 */ 517 public function getEventWithUid($uid) 518 { 519 $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid=". 520 $this->sqlite->quote_string($uid); 521 $res = $this->sqlite->query($query); 522 $row = $this->sqlite->res2row($res); 523 return $row; 524 } 525 526 /** 527 * Retrieve all calendar events for a given calendar ID 528 * 529 * @param string $calid The calendar's ID 530 * 531 * @return array An array containing all calendar data 532 */ 533 public function getAllCalendarEvents($calid) 534 { 535 $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid=". 536 $this->sqlite->quote_string($calid); 537 $res = $this->sqlite->query($query); 538 $arr = $this->sqlite->res2arr($res); 539 return $arr; 540 } 541 542 /** 543 * Edit a calendar entry for a page, given by its parameters. 544 * The params array has the same format as @see addCalendarEntryForPage 545 * 546 * @param string $id The page's ID to work on 547 * @param string $user The user's ID to work on 548 * @param array $params The parameter array for the edited calendar event 549 * 550 * @return boolean True on success, otherwise false 551 */ 552 public function editCalendarEntryForPage($id, $user, $params) 553 { 554 $settings = $this->getPersonalSettings($user); 555 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 556 $timezone = new \DateTimeZone($settings['timezone']); 557 elseif($settings['timezone'] === 'local') 558 $timezone = new \DateTimeZone($params['detectedtz']); 559 else 560 $timezone = new \DateTimeZone('UTC'); 561 562 // Parse dates 563 $startDate = explode('-', $params['eventfrom']); 564 $startTime = explode(':', $params['eventfromtime']); 565 $endDate = explode('-', $params['eventto']); 566 $endTime = explode(':', $params['eventtotime']); 567 568 // Retrieve the existing event based on the UID 569 $uid = $params['uid']; 570 $event = $this->getEventWithUid($uid); 571 572 // Load SabreDAV 573 require_once('vendor/autoload.php'); 574 if(!isset($event['calendardata'])) 575 return false; 576 $uri = $event['uri']; 577 $calid = $event['calendarid']; 578 579 // Parse the existing event 580 $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 581 $vevent = $vcal->VEVENT; 582 583 // Set the new event values 584 $vevent->summary = $params['eventname']; 585 $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 586 $description = $params['eventdescription']; 587 588 // Remove existing timestamps to overwrite them 589 $vevent->remove('DESCRIPTION'); 590 $vevent->remove('DTSTAMP'); 591 $vevent->remove('LAST-MODIFIED'); 592 593 // Add new time stamps and description 594 $vevent->add('DTSTAMP', $dtStamp); 595 $vevent->add('LAST-MODIFIED', $dtStamp); 596 if($description !== '') 597 $vevent->add('DESCRIPTION', $description); 598 599 // Setup DTSTART 600 $dtStart = new \DateTime(); 601 $dtStart->setTimezone($timezone); 602 $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 603 if($params['allday'] != '1') 604 $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 605 606 // Setup DETEND 607 $dtEnd = new \DateTime(); 608 $dtEnd->setTimezone($timezone); 609 $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 610 if($params['allday'] != '1') 611 $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 612 613 // According to the VCal spec, we need to add a whole day here 614 if($params['allday'] == '1') 615 $dtEnd->add(new \DateInterval('P1D')); 616 $vevent->remove('DTSTART'); 617 $vevent->remove('DTEND'); 618 $dtStartEv = $vevent->add('DTSTART', $dtStart); 619 $dtEndEv = $vevent->add('DTEND', $dtEnd); 620 621 // Remove the time for allday events 622 if($params['allday'] == '1') 623 { 624 $dtStartEv['VALUE'] = 'DATE'; 625 $dtEndEv['VALUE'] = 'DATE'; 626 } 627 $now = new DateTime(); 628 $eventStr = $vcal->serialize(); 629 630 // Actually write to the database 631 $query = "UPDATE calendarobjects SET calendardata=".$this->sqlite->quote_string($eventStr). 632 ", lastmodified=".$this->sqlite->quote_string($now->getTimestamp()). 633 ", firstoccurence=".$this->sqlite->quote_string($dtStart->getTimestamp()). 634 ", lastoccurence=".$this->sqlite->quote_string($dtEnd->getTimestamp()). 635 ", size=".strlen($eventStr). 636 ", etag=".$this->sqlite->quote_string(md5($eventStr)). 637 " WHERE uid=".$this->sqlite->quote_string($uid); 638 $res = $this->sqlite->query($query); 639 if($res !== false) 640 { 641 $this->updateSyncTokenLog($calid, $uri, 'modified'); 642 return true; 643 } 644 return false; 645 } 646 647 /** 648 * Delete a calendar entry for a given page. Actually, the event is removed 649 * based on the entry's UID, so that page ID is no used. 650 * 651 * @param string $id The page's ID (unused) 652 * @param array $params The parameter array to work with 653 * 654 * @return boolean True 655 */ 656 public function deleteCalendarEntryForPage($id, $params) 657 { 658 $uid = $params['uid']; 659 $event = $this->getEventWithUid($uid); 660 $calid = $event['calendarid']; 661 $uri = $event['uri']; 662 $query = "DELETE FROM calendarobjects WHERE uid=".$this->sqlite->quote_string($uid); 663 $res = $this->sqlite->query($query); 664 if($res !== false) 665 { 666 $this->updateSyncTokenLog($calid, $uri, 'deleted'); 667 } 668 return true; 669 } 670 671 /** 672 * Retrieve the current sync token for a calendar 673 * 674 * @param string $calid The calendar id 675 * 676 * @return mixed The synctoken or false 677 */ 678 public function getSyncTokenForCalendar($calid) 679 { 680 $row = $this->getCalendarSettings($calid); 681 if(isset($row['synctoken'])) 682 return $row['synctoken']; 683 return false; 684 } 685 686 /** 687 * Helper function to convert the operation name to 688 * an operation code as stored in the database 689 * 690 * @param string $operationName The operation name 691 * 692 * @return mixed The operation code or false 693 */ 694 public function operationNameToOperation($operationName) 695 { 696 switch($operationName) 697 { 698 case 'added': 699 return 1; 700 break; 701 case 'modified': 702 return 2; 703 break; 704 case 'deleted': 705 return 3; 706 break; 707 } 708 return false; 709 } 710 711 /** 712 * Update the sync token log based on the calendar id and the 713 * operation that was performed. 714 * 715 * @param string $calid The calendar ID that was modified 716 * @param string $uri The calendar URI that was modified 717 * @param string $operation The operation that was performed 718 * 719 * @return boolean True on success, otherwise false 720 */ 721 private function updateSyncTokenLog($calid, $uri, $operation) 722 { 723 $currentToken = $this->getSyncTokenForCalendar($calid); 724 $operationCode = $this->operationNameToOperation($operation); 725 if(($operationCode === false) || ($currentToken === false)) 726 return false; 727 $values = array($uri, 728 $currentToken, 729 $calid, 730 $operationCode 731 ); 732 $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(". 733 $this->sqlite->quote_and_join($values, ',').")"; 734 $res = $this->sqlite->query($query); 735 if($res === false) 736 return false; 737 $currentToken++; 738 $query = "UPDATE calendars SET synctoken=".$this->sqlite->quote_string($currentToken)." WHERE id=". 739 $this->sqlite->quote_string($calid); 740 $res = $this->sqlite->query($query); 741 return ($res !== false); 742 } 743 744 /** 745 * Return the sync URL for a given Page, i.e. a calendar 746 * 747 * @param string $id The page's ID 748 * @param string $user (optional) The user's ID 749 * 750 * @return mixed The sync url or false 751 */ 752 public function getSyncUrlForPage($id, $user = null) 753 { 754 if(is_null($user)) 755 $user = $_SERVER['REMOTE_USER']; 756 757 $calid = $this->getCalendarIdForPage($id); 758 if($calid === false) 759 return false; 760 761 $calsettings = $this->getCalendarSettings($calid); 762 if(!isset($calsettings['uri'])) 763 return false; 764 765 $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 766 return $syncurl; 767 } 768 769 /** 770 * Return the private calendar's URL for a given page 771 * 772 * @param string $id the page ID 773 * 774 * @return mixed The private URL or false 775 */ 776 public function getPrivateURLForPage($id) 777 { 778 $calid = $this->getCalendarIdForPage($id); 779 if($calid === false) 780 return false; 781 782 return $this->getPrivateURLForCalendar($calid); 783 } 784 785 /** 786 * Return the private calendar's URL for a given calendar ID 787 * 788 * @param string $calid The calendar's ID 789 * 790 * @return mixed The private URL or false 791 */ 792 public function getPrivateURLForCalendar($calid) 793 { 794 $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid=".$this->sqlite->quote_string($calid); 795 $res = $this->sqlite->query($query); 796 $row = $this->sqlite->res2row($res); 797 if(!isset($row['url'])) 798 { 799 $url = uniqid("dokuwiki-").".ics"; 800 $values = array( 801 $url, 802 $calid 803 ); 804 $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(". 805 $this->sqlite->quote_and_join($values, ", ").")"; 806 $res = $this->sqlite->query($query); 807 if($res === false) 808 return false; 809 } 810 else 811 { 812 $url = $row['url']; 813 } 814 return DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 815 } 816 817 /** 818 * Retrieve the calendar ID for a given private calendar URL 819 * 820 * @param string $url The private URL 821 * 822 * @return mixed The calendar ID or false 823 */ 824 public function getCalendarForPrivateURL($url) 825 { 826 $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url=".$this->sqlite->quote_string($url); 827 $res = $this->sqlite->query($query); 828 $row = $this->sqlite->res2row($res); 829 if(!isset($row['calid'])) 830 return false; 831 return $row['calid']; 832 } 833 834 /** 835 * Return a given calendar as ICS feed, i.e. all events in one ICS file. 836 * 837 * @param string $caldi The calendar ID to retrieve 838 * 839 * @return mixed The calendar events as string or false 840 */ 841 public function getCalendarAsICSFeed($calid) 842 { 843 $calSettings = $this->getCalendarSettings($calid); 844 if($calSettings === false) 845 return false; 846 $events = $this->getAllCalendarEvents($calid); 847 if($events === false) 848 return false; 849 850 $out = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\nCALSCALE:GREGORIAN\nX-WR-CALNAME:"; 851 $out .= $calSettings['displayname']."\n"; 852 foreach($events as $event) 853 { 854 $out .= rtrim($event['calendardata']); 855 $out .= "\n"; 856 } 857 $out .= "END:VCALENDAR\n"; 858 return $out; 859 } 860 861} 862