1a1a3b679SAndreas Boehler<?php 2a1a3b679SAndreas Boehler/** 3cb71a62aSAndreas Boehler * Helper Class for the DAVCal plugin 4a1a3b679SAndreas Boehler * This helper does the actual work. 5a1a3b679SAndreas Boehler * 6a1a3b679SAndreas Boehler */ 7a1a3b679SAndreas Boehler 8a1a3b679SAndreas Boehler// must be run within Dokuwiki 9a1a3b679SAndreas Boehlerif(!defined('DOKU_INC')) die(); 10a1a3b679SAndreas Boehler 11a1a3b679SAndreas Boehlerclass helper_plugin_davcal extends DokuWiki_Plugin { 12a1a3b679SAndreas Boehler 13a1a3b679SAndreas Boehler protected $sqlite = null; 14185e2535SAndreas Boehler protected $cachedValues = array(); 15a1a3b679SAndreas Boehler 16a1a3b679SAndreas Boehler /** 17cb71a62aSAndreas Boehler * Constructor to load the configuration and the SQLite plugin 18a1a3b679SAndreas Boehler */ 19a1a3b679SAndreas Boehler public function helper_plugin_davcal() { 20a1a3b679SAndreas Boehler $this->sqlite =& plugin_load('helper', 'sqlite'); 2121d04f73SAndreas Boehler global $conf; 2221d04f73SAndreas Boehler if($conf['allowdebug']) 2321d04f73SAndreas Boehler dbglog('---- DAVCAL helper.php init'); 24a1a3b679SAndreas Boehler if(!$this->sqlite) 25a1a3b679SAndreas Boehler { 2621d04f73SAndreas Boehler if($conf['allowdebug']) 2721d04f73SAndreas Boehler dbglog('This plugin requires the sqlite plugin. Please install it.'); 28a1a3b679SAndreas Boehler msg('This plugin requires the sqlite plugin. Please install it.'); 29a1a3b679SAndreas Boehler return; 30a1a3b679SAndreas Boehler } 31a1a3b679SAndreas Boehler 32a1a3b679SAndreas Boehler if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/')) 33a1a3b679SAndreas Boehler { 3421d04f73SAndreas Boehler if($conf['allowdebug']) 3521d04f73SAndreas Boehler dbglog('Error initialising the SQLite DB for DAVCal'); 36a1a3b679SAndreas Boehler return; 37a1a3b679SAndreas Boehler } 38a1a3b679SAndreas Boehler } 39a1a3b679SAndreas Boehler 40cb71a62aSAndreas Boehler /** 41185e2535SAndreas Boehler * Retrieve meta data for a given page 42185e2535SAndreas Boehler * 43185e2535SAndreas Boehler * @param string $id optional The page ID 44185e2535SAndreas Boehler * @return array The metadata 45185e2535SAndreas Boehler */ 46185e2535SAndreas Boehler private function getMeta($id = null) { 47185e2535SAndreas Boehler global $ID; 48185e2535SAndreas Boehler global $INFO; 49185e2535SAndreas Boehler 50185e2535SAndreas Boehler if ($id === null) $id = $ID; 51185e2535SAndreas Boehler 52185e2535SAndreas Boehler if($ID === $id && $INFO['meta']) { 53185e2535SAndreas Boehler $meta = $INFO['meta']; 54185e2535SAndreas Boehler } else { 55185e2535SAndreas Boehler $meta = p_get_metadata($id); 56185e2535SAndreas Boehler } 57185e2535SAndreas Boehler 58185e2535SAndreas Boehler return $meta; 59185e2535SAndreas Boehler } 60185e2535SAndreas Boehler 61185e2535SAndreas Boehler /** 62185e2535SAndreas Boehler * Retrieve the meta data for a given page 63185e2535SAndreas Boehler * 64185e2535SAndreas Boehler * @param string $id optional The page ID 65185e2535SAndreas Boehler * @return array with meta data 66185e2535SAndreas Boehler */ 67185e2535SAndreas Boehler public function getCalendarMetaForPage($id = null) 68185e2535SAndreas Boehler { 69185e2535SAndreas Boehler if(is_null($id)) 70185e2535SAndreas Boehler { 71185e2535SAndreas Boehler global $ID; 72185e2535SAndreas Boehler $id = $ID; 73185e2535SAndreas Boehler } 74185e2535SAndreas Boehler 75185e2535SAndreas Boehler $meta = $this->getMeta($id); 76185e2535SAndreas Boehler if(isset($meta['plugin_davcal'])) 77185e2535SAndreas Boehler return $meta['plugin_davcal']; 78185e2535SAndreas Boehler else 79185e2535SAndreas Boehler return array(); 80185e2535SAndreas Boehler } 81185e2535SAndreas Boehler 82185e2535SAndreas Boehler /** 83*d71c9934SAndreas Boehler * Check the permission of a user for a given calendar ID 84*d71c9934SAndreas Boehler * 85*d71c9934SAndreas Boehler * @param string $id The calendar ID to check 86*d71c9934SAndreas Boehler * @return int AUTH_* constants 87*d71c9934SAndreas Boehler */ 88*d71c9934SAndreas Boehler public function checkCalendarPermission($id) 89*d71c9934SAndreas Boehler { 90*d71c9934SAndreas Boehler if(strpos($page, 'webdav://') === 0) 91*d71c9934SAndreas Boehler { 92*d71c9934SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 93*d71c9934SAndreas Boehler if(is_null($wdc)) 94*d71c9934SAndreas Boehler return AUTH_NONE; 95*d71c9934SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 96*d71c9934SAndreas Boehler $settings = $wdc->getConnection($connectionId); 97*d71c9934SAndreas Boehler if($settings === false) 98*d71c9934SAndreas Boehler return AUTH_NONE; 99*d71c9934SAndreas Boehler if($settings['write'] === '1') 100*d71c9934SAndreas Boehler return AUTH_CREATE; 101*d71c9934SAndreas Boehler return AUTH_READ; 102*d71c9934SAndreas Boehler } 103*d71c9934SAndreas Boehler else 104*d71c9934SAndreas Boehler { 105*d71c9934SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 106*d71c9934SAndreas Boehler // We return AUTH_READ if the calendar does not exist. This makes 107*d71c9934SAndreas Boehler // davcal happy when there are just included calendars 108*d71c9934SAndreas Boehler if($calid === false) 109*d71c9934SAndreas Boehler return AUTH_READ; 110*d71c9934SAndreas Boehler return auth_quickaclcheck($id); 111*d71c9934SAndreas Boehler } 112*d71c9934SAndreas Boehler } 113*d71c9934SAndreas Boehler 114*d71c9934SAndreas Boehler /** 11580e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 11680e1ddf7SAndreas Boehler * user has at least read permission. 11780e1ddf7SAndreas Boehler * 11880e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 11980e1ddf7SAndreas Boehler * @return array with filtered calendar pages 12080e1ddf7SAndreas Boehler */ 12180e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 12280e1ddf7SAndreas Boehler { 12380e1ddf7SAndreas Boehler $retList = array(); 12480e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 12580e1ddf7SAndreas Boehler { 1260b805092SAndreas Boehler // WebDAV Connections are always readable 1270b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1280b805092SAndreas Boehler { 1290b805092SAndreas Boehler $retList[$page] = $data; 1300b805092SAndreas Boehler } 1310b805092SAndreas Boehler elseif(auth_quickaclcheck($page) >= AUTH_READ) 13280e1ddf7SAndreas Boehler { 13380e1ddf7SAndreas Boehler $retList[$page] = $data; 13480e1ddf7SAndreas Boehler } 13580e1ddf7SAndreas Boehler } 13680e1ddf7SAndreas Boehler return $retList; 13780e1ddf7SAndreas Boehler } 13880e1ddf7SAndreas Boehler 13980e1ddf7SAndreas Boehler /** 140185e2535SAndreas Boehler * Get all calendar pages used by a given page 141185e2535SAndreas Boehler * based on the stored metadata 142185e2535SAndreas Boehler * 143185e2535SAndreas Boehler * @param string $id optional The page id 144185e2535SAndreas Boehler * @return mixed The pages as array or false 145185e2535SAndreas Boehler */ 146185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 147185e2535SAndreas Boehler { 148185e2535SAndreas Boehler if(is_null($id)) 149185e2535SAndreas Boehler { 150185e2535SAndreas Boehler global $ID; 151185e2535SAndreas Boehler $id = $ID; 152185e2535SAndreas Boehler } 153185e2535SAndreas Boehler 154185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 1550b805092SAndreas Boehler 156185e2535SAndreas Boehler if(isset($meta['id'])) 157ed764890SAndreas Boehler { 158ed764890SAndreas Boehler // Filter the list of pages by permission 15980e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 16080e1ddf7SAndreas Boehler if(empty($pages)) 161ed764890SAndreas Boehler return false; 16280e1ddf7SAndreas Boehler return $pages; 163ed764890SAndreas Boehler } 164185e2535SAndreas Boehler return false; 165185e2535SAndreas Boehler } 166185e2535SAndreas Boehler 167185e2535SAndreas Boehler /** 168185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 169185e2535SAndreas Boehler * for an array of page ids 170185e2535SAndreas Boehler * 171185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 172185e2535SAndreas Boehler * @return array The list 173185e2535SAndreas Boehler */ 174185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 175185e2535SAndreas Boehler { 176185e2535SAndreas Boehler $data = array(); 1774a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 178185e2535SAndreas Boehler { 1790b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1800b805092SAndreas Boehler { 1810b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1820b805092SAndreas Boehler if(is_null($wdc)) 1830b805092SAndreas Boehler continue; 1840b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1850b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 1862393a702SAndreas Boehler if($settings === false) 1872393a702SAndreas Boehler continue; 1880b805092SAndreas Boehler $name = $settings['displayname']; 189*d71c9934SAndreas Boehler $write = ($settings['write'] === '1'); 1900b805092SAndreas Boehler $calid = $connectionId; 1910b805092SAndreas Boehler } 1920b805092SAndreas Boehler else 1930b805092SAndreas Boehler { 194185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 195185e2535SAndreas Boehler if($calid !== false) 196185e2535SAndreas Boehler { 197185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 198185e2535SAndreas Boehler $name = $settings['displayname']; 1990b805092SAndreas Boehler //$color = $settings['calendarcolor']; 200ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 2010b805092SAndreas Boehler } 2020b805092SAndreas Boehler else 2030b805092SAndreas Boehler { 2040b805092SAndreas Boehler continue; 2050b805092SAndreas Boehler } 2060b805092SAndreas Boehler } 207185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 208ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 209185e2535SAndreas Boehler } 210185e2535SAndreas Boehler return $data; 211185e2535SAndreas Boehler } 212185e2535SAndreas Boehler 213185e2535SAndreas Boehler /** 214185e2535SAndreas Boehler * Get the saved calendar color for a given page. 215185e2535SAndreas Boehler * 216185e2535SAndreas Boehler * @param string $id optional The page ID 217185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 218185e2535SAndreas Boehler */ 219185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 220185e2535SAndreas Boehler { 221185e2535SAndreas Boehler if(is_null($id)) 222185e2535SAndreas Boehler { 223185e2535SAndreas Boehler global $ID; 224185e2535SAndreas Boehler $id = $ID; 225185e2535SAndreas Boehler } 226185e2535SAndreas Boehler 227185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 228185e2535SAndreas Boehler if($calid === false) 229185e2535SAndreas Boehler return false; 230185e2535SAndreas Boehler 231185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 232185e2535SAndreas Boehler } 233185e2535SAndreas Boehler 234185e2535SAndreas Boehler /** 235185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 236185e2535SAndreas Boehler * 237185e2535SAndreas Boehler * @param string $id optional The calendar ID 238185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 239185e2535SAndreas Boehler */ 240185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 241185e2535SAndreas Boehler { 242185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 243185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 244185e2535SAndreas Boehler 245185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 246185e2535SAndreas Boehler 247185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 248185e2535SAndreas Boehler return false; 249185e2535SAndreas Boehler 250185e2535SAndreas Boehler $color = $row['calendarcolor']; 251185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 252185e2535SAndreas Boehler return $color; 253185e2535SAndreas Boehler } 254185e2535SAndreas Boehler 255185e2535SAndreas Boehler /** 256e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 257e86c8dd3SAndreas Boehler * @param string $user the user name 258e86c8dd3SAndreas Boehler * @return the URL to the principal sync 259e86c8dd3SAndreas Boehler */ 260e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 261e86c8dd3SAndreas Boehler { 262e86c8dd3SAndreas Boehler if(is_null($user)) 263e86c8dd3SAndreas Boehler return false; 264e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 265e86c8dd3SAndreas Boehler return $url; 266e86c8dd3SAndreas Boehler } 267e86c8dd3SAndreas Boehler 268e86c8dd3SAndreas Boehler /** 269185e2535SAndreas Boehler * Set the calendar color for a given page. 270185e2535SAndreas Boehler * 271185e2535SAndreas Boehler * @param string $color The color definition 272185e2535SAndreas Boehler * @param string $id optional The page ID 273185e2535SAndreas Boehler * @return boolean True on success, otherwise false 274185e2535SAndreas Boehler */ 275185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 276185e2535SAndreas Boehler { 277185e2535SAndreas Boehler if(is_null($id)) 278185e2535SAndreas Boehler { 279185e2535SAndreas Boehler global $ID; 280185e2535SAndreas Boehler $id = $ID; 281185e2535SAndreas Boehler } 282185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 283185e2535SAndreas Boehler if($calid === false) 284185e2535SAndreas Boehler return false; 285185e2535SAndreas Boehler 28651f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 28751f4febbSAndreas Boehler " WHERE id = ?"; 28851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $color, $calid); 289185e2535SAndreas Boehler if($res !== false) 290185e2535SAndreas Boehler { 291185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 292185e2535SAndreas Boehler return true; 293185e2535SAndreas Boehler } 294185e2535SAndreas Boehler return false; 295185e2535SAndreas Boehler } 296185e2535SAndreas Boehler 297185e2535SAndreas Boehler /** 298cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 299cb71a62aSAndreas Boehler * page id. 300cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 301cb71a62aSAndreas Boehler * 302cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 303cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 304cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 305cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 306cb71a62aSAndreas Boehler * 307cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 308cb71a62aSAndreas Boehler */ 309a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 310a1a3b679SAndreas Boehler { 311a1a3b679SAndreas Boehler if(is_null($id)) 312a1a3b679SAndreas Boehler { 313a1a3b679SAndreas Boehler global $ID; 314a1a3b679SAndreas Boehler $id = $ID; 315a1a3b679SAndreas Boehler } 316a1a3b679SAndreas Boehler if(is_null($userid)) 31734a47953SAndreas Boehler { 31834a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 31934a47953SAndreas Boehler { 320a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 32134a47953SAndreas Boehler } 32234a47953SAndreas Boehler else 32334a47953SAndreas Boehler { 32434a47953SAndreas Boehler $userid = uniqid('davcal-'); 32534a47953SAndreas Boehler } 32634a47953SAndreas Boehler } 327a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 328a1a3b679SAndreas Boehler if($calid === false) 329a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 330a1a3b679SAndreas Boehler 33151f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 33251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $name, $description, $calid); 333b269830cSAndreas Boehler if($res !== false) 334b269830cSAndreas Boehler return true; 335b269830cSAndreas Boehler return false; 336a1a3b679SAndreas Boehler } 337a1a3b679SAndreas Boehler 338cb71a62aSAndreas Boehler /** 339d5703f5aSAndreas Boehler * Update a calendar's displayname 340d5703f5aSAndreas Boehler * 341d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 342d5703f5aSAndreas Boehler * @param string $name The new calendar name 343d5703f5aSAndreas Boehler * 344d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 345d5703f5aSAndreas Boehler */ 346d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 347d5703f5aSAndreas Boehler { 348d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 349d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $name); 350d5703f5aSAndreas Boehler if($res !== false) 351d5703f5aSAndreas Boehler { 352d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 353d5703f5aSAndreas Boehler return true; 354d5703f5aSAndreas Boehler } 355d5703f5aSAndreas Boehler return false; 356d5703f5aSAndreas Boehler } 357d5703f5aSAndreas Boehler 358d5703f5aSAndreas Boehler /** 359d5703f5aSAndreas Boehler * Update the calendar description 360d5703f5aSAndreas Boehler * 361d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 362d5703f5aSAndreas Boehler * @param string $description The new calendar's description 363d5703f5aSAndreas Boehler * 364d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 365d5703f5aSAndreas Boehler */ 366d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 367d5703f5aSAndreas Boehler { 368d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 369d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $description); 370d5703f5aSAndreas Boehler if($res !== false) 371d5703f5aSAndreas Boehler { 372d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 373d5703f5aSAndreas Boehler return true; 374d5703f5aSAndreas Boehler } 375d5703f5aSAndreas Boehler return false; 376d5703f5aSAndreas Boehler } 377d5703f5aSAndreas Boehler 378d5703f5aSAndreas Boehler /** 379d5703f5aSAndreas Boehler * Update a calendar's timezone information 380d5703f5aSAndreas Boehler * 381d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 382d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 383d5703f5aSAndreas Boehler * 384d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 385d5703f5aSAndreas Boehler */ 386d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 387d5703f5aSAndreas Boehler { 388d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 389d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $timezone); 390d5703f5aSAndreas Boehler if($res !== false) 391d5703f5aSAndreas Boehler { 392d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 393d5703f5aSAndreas Boehler return true; 394d5703f5aSAndreas Boehler } 395d5703f5aSAndreas Boehler return false; 396d5703f5aSAndreas Boehler } 397d5703f5aSAndreas Boehler 398d5703f5aSAndreas Boehler /** 399cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 400cb71a62aSAndreas Boehler * 401cb71a62aSAndreas Boehler * @param array $settings The settings array to store 402cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 403cb71a62aSAndreas Boehler * 404cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 405cb71a62aSAndreas Boehler */ 406a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 407a495d34cSAndreas Boehler { 408a495d34cSAndreas Boehler if(is_null($userid)) 40934a47953SAndreas Boehler { 41034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 41134a47953SAndreas Boehler { 412a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 41334a47953SAndreas Boehler } 41434a47953SAndreas Boehler else 41534a47953SAndreas Boehler { 41634a47953SAndreas Boehler return false; 41734a47953SAndreas Boehler } 41834a47953SAndreas Boehler } 419a495d34cSAndreas Boehler $this->sqlite->query("BEGIN TRANSACTION"); 420a495d34cSAndreas Boehler 42151f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 42251f4febbSAndreas Boehler $this->sqlite->query($query, $userid); 423bd883736SAndreas Boehler 424a495d34cSAndreas Boehler foreach($settings as $key => $value) 425a495d34cSAndreas Boehler { 42651f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 42751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid, $key, $value); 428a495d34cSAndreas Boehler if($res === false) 429a495d34cSAndreas Boehler return false; 430a495d34cSAndreas Boehler } 431a495d34cSAndreas Boehler $this->sqlite->query("COMMIT TRANSACTION"); 432185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 433a495d34cSAndreas Boehler return true; 434a495d34cSAndreas Boehler } 435a495d34cSAndreas Boehler 436cb71a62aSAndreas Boehler /** 437cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 438cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 439cb71a62aSAndreas Boehler * 440cb71a62aSAndreas Boehler * timezone => local 441cb71a62aSAndreas Boehler * weeknumbers => 0 442cb71a62aSAndreas Boehler * workweek => 0 443cb71a62aSAndreas Boehler * 444cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 445cb71a62aSAndreas Boehler * 446cb71a62aSAndreas Boehler * @return array The settings array 447cb71a62aSAndreas Boehler */ 448a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 449a495d34cSAndreas Boehler { 450bd883736SAndreas Boehler // Some sane default settings 451bd883736SAndreas Boehler $settings = array( 452fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 453fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 454fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4551d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4561d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 457bd883736SAndreas Boehler ); 45834a47953SAndreas Boehler if(is_null($userid)) 45934a47953SAndreas Boehler { 46034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 46134a47953SAndreas Boehler { 46234a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 46334a47953SAndreas Boehler } 46434a47953SAndreas Boehler else 46534a47953SAndreas Boehler { 46634a47953SAndreas Boehler return $settings; 46734a47953SAndreas Boehler } 46834a47953SAndreas Boehler } 46934a47953SAndreas Boehler 47034a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 47134a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 47251f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 47351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid); 474a495d34cSAndreas Boehler $arr = $this->sqlite->res2arr($res); 475a495d34cSAndreas Boehler foreach($arr as $row) 476a495d34cSAndreas Boehler { 477a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 478a495d34cSAndreas Boehler } 479185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 480a495d34cSAndreas Boehler return $settings; 481a495d34cSAndreas Boehler } 482a495d34cSAndreas Boehler 483cb71a62aSAndreas Boehler /** 484cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 485cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 486cb71a62aSAndreas Boehler * 487cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 488cb71a62aSAndreas Boehler * 489cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 490cb71a62aSAndreas Boehler */ 491a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 492a1a3b679SAndreas Boehler { 493a1a3b679SAndreas Boehler if(is_null($id)) 494a1a3b679SAndreas Boehler { 495a1a3b679SAndreas Boehler global $ID; 496a1a3b679SAndreas Boehler $id = $ID; 497a1a3b679SAndreas Boehler } 498a1a3b679SAndreas Boehler 499185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 500185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 501185e2535SAndreas Boehler 50251f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 50351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id); 504a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 505a1a3b679SAndreas Boehler if(isset($row['calid'])) 506185e2535SAndreas Boehler { 507185e2535SAndreas Boehler $calid = $row['calid']; 508185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 509185e2535SAndreas Boehler return $calid; 510185e2535SAndreas Boehler } 511a1a3b679SAndreas Boehler return false; 512a1a3b679SAndreas Boehler } 513a1a3b679SAndreas Boehler 514cb71a62aSAndreas Boehler /** 515cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 516cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 517cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 518cb71a62aSAndreas Boehler * 519cb71a62aSAndreas Boehler * @return array The mapping array 520cb71a62aSAndreas Boehler */ 521a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 522a1a3b679SAndreas Boehler { 523a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 524a1a3b679SAndreas Boehler $res = $this->sqlite->query($query); 525a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 526a1a3b679SAndreas Boehler return $arr; 527a1a3b679SAndreas Boehler } 528a1a3b679SAndreas Boehler 529cb71a62aSAndreas Boehler /** 530cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 531cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 532cb71a62aSAndreas Boehler * user name is actually split from the URI component. 533cb71a62aSAndreas Boehler * 534cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 535cb71a62aSAndreas Boehler * and applied accordingly. 536cb71a62aSAndreas Boehler * 537cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 538cb71a62aSAndreas Boehler * 539cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 540cb71a62aSAndreas Boehler */ 541a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 542a1a3b679SAndreas Boehler { 54334a47953SAndreas Boehler global $auth; 544a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 545a1a3b679SAndreas Boehler $user = end($user); 546a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 547a1a3b679SAndreas Boehler $calids = array(); 54834a47953SAndreas Boehler $ud = $auth->getUserData($user); 54934a47953SAndreas Boehler $groups = $ud['grps']; 550a1a3b679SAndreas Boehler foreach($mapping as $row) 551a1a3b679SAndreas Boehler { 552a1a3b679SAndreas Boehler $id = $row['calid']; 553a1a3b679SAndreas Boehler $page = $row['page']; 55434a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 555a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 556a1a3b679SAndreas Boehler { 557a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 558a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 559a1a3b679SAndreas Boehler } 560a1a3b679SAndreas Boehler } 561a1a3b679SAndreas Boehler return $calids; 562a1a3b679SAndreas Boehler } 563a1a3b679SAndreas Boehler 564cb71a62aSAndreas Boehler /** 565cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 566cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 567cb71a62aSAndreas Boehler * 568cb71a62aSAndreas Boehler * @param string $name The calendar's name 569cb71a62aSAndreas Boehler * @param string $description The calendar's description 570cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 571cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 572cb71a62aSAndreas Boehler * 573cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 574cb71a62aSAndreas Boehler */ 575a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 576a1a3b679SAndreas Boehler { 577a1a3b679SAndreas Boehler if(is_null($id)) 578a1a3b679SAndreas Boehler { 579a1a3b679SAndreas Boehler global $ID; 580a1a3b679SAndreas Boehler $id = $ID; 581a1a3b679SAndreas Boehler } 582a1a3b679SAndreas Boehler if(is_null($userid)) 58334a47953SAndreas Boehler { 58434a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 58534a47953SAndreas Boehler { 586a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 58734a47953SAndreas Boehler } 58834a47953SAndreas Boehler else 58934a47953SAndreas Boehler { 59034a47953SAndreas Boehler $userid = uniqid('davcal-'); 59134a47953SAndreas Boehler } 59234a47953SAndreas Boehler } 593a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 594a1a3b679SAndreas Boehler $name, 595a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 596a1a3b679SAndreas Boehler $description, 597a1a3b679SAndreas Boehler 'VEVENT,VTODO', 59855a741c0SAndreas Boehler 0, 59955a741c0SAndreas Boehler 1); 60051f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 60151f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 60251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 60355a741c0SAndreas Boehler if($res === false) 60455a741c0SAndreas Boehler return false; 605cb71a62aSAndreas Boehler 606cb71a62aSAndreas Boehler // Get the new calendar ID 60751f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 60851f4febbSAndreas Boehler "uri = ? AND description = ?"; 60951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 610a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 611cb71a62aSAndreas Boehler 612cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 613a1a3b679SAndreas Boehler if(isset($row['id'])) 614a1a3b679SAndreas Boehler { 61551f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 61651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id, $row['id']); 61755a741c0SAndreas Boehler return ($res !== false); 618a1a3b679SAndreas Boehler } 619a1a3b679SAndreas Boehler 620a1a3b679SAndreas Boehler return false; 621a1a3b679SAndreas Boehler } 622a1a3b679SAndreas Boehler 623cb71a62aSAndreas Boehler /** 624d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 625d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 626d5703f5aSAndreas Boehler * 627d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 628d5703f5aSAndreas Boehler * 629d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 630d5703f5aSAndreas Boehler * @param string $uri The new object URI 631d5703f5aSAndreas Boehler * @param string $ics The ICS file 632d5703f5aSAndreas Boehler * 633d5703f5aSAndreas Boehler * @return mixed The etag. 634d5703f5aSAndreas Boehler */ 635d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 636d5703f5aSAndreas Boehler { 637d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 638d5703f5aSAndreas Boehler 639d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 640d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 641d5703f5aSAndreas Boehler $calid, 642d5703f5aSAndreas Boehler $uri, 643d5703f5aSAndreas Boehler $ics, 644d5703f5aSAndreas Boehler time(), 645d5703f5aSAndreas Boehler $extraData['etag'], 646d5703f5aSAndreas Boehler $extraData['size'], 647d5703f5aSAndreas Boehler $extraData['componentType'], 648d5703f5aSAndreas Boehler $extraData['firstOccurence'], 649d5703f5aSAndreas Boehler $extraData['lastOccurence'], 650d5703f5aSAndreas Boehler $extraData['uid']); 651d5703f5aSAndreas Boehler // If successfully, update the sync token database 652d5703f5aSAndreas Boehler if($res !== false) 653d5703f5aSAndreas Boehler { 654d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 655d5703f5aSAndreas Boehler } 656d5703f5aSAndreas Boehler return $extraData['etag']; 657d5703f5aSAndreas Boehler } 658d5703f5aSAndreas Boehler 659d5703f5aSAndreas Boehler /** 660d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 661d5703f5aSAndreas Boehler * needed for the sync support. 662d5703f5aSAndreas Boehler * 663d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 664d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 665d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 666d5703f5aSAndreas Boehler */ 667d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 668d5703f5aSAndreas Boehler { 669d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 670d5703f5aSAndreas Boehler 671d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 672d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 673d5703f5aSAndreas Boehler $ics, 674d5703f5aSAndreas Boehler time(), 675d5703f5aSAndreas Boehler $extraData['etag'], 676d5703f5aSAndreas Boehler $extraData['size'], 677d5703f5aSAndreas Boehler $extraData['componentType'], 678d5703f5aSAndreas Boehler $extraData['firstOccurence'], 679d5703f5aSAndreas Boehler $extraData['lastOccurence'], 680d5703f5aSAndreas Boehler $extraData['uid'], 681d5703f5aSAndreas Boehler $calid, 682d5703f5aSAndreas Boehler $uri 683d5703f5aSAndreas Boehler ); 684d5703f5aSAndreas Boehler if($res !== false) 685d5703f5aSAndreas Boehler { 686d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 687d5703f5aSAndreas Boehler } 688d5703f5aSAndreas Boehler return $extraData['etag']; 689d5703f5aSAndreas Boehler } 690d5703f5aSAndreas Boehler 691d5703f5aSAndreas Boehler /** 692cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 693cb71a62aSAndreas Boehler * 694cb71a62aSAndreas Boehler * The parameter array needs to contain 695cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 69682a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 697cb71a62aSAndreas Boehler * eventfrom => The event's start date 698cb71a62aSAndreas Boehler * eventfromtime => The event's start time 699cb71a62aSAndreas Boehler * eventto => The event's end date 700cb71a62aSAndreas Boehler * eventtotime => The event's end time 701cb71a62aSAndreas Boehler * eventname => The event's name 702cb71a62aSAndreas Boehler * eventdescription => The event's description 703cb71a62aSAndreas Boehler * 704cb71a62aSAndreas Boehler * @param string $id The page ID to work on 705cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 706cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 707cb71a62aSAndreas Boehler * 708cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 709cb71a62aSAndreas Boehler */ 710a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 711a1a3b679SAndreas Boehler { 71282a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 71382a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 71482a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 715a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 716bd883736SAndreas Boehler else 717bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 718cb71a62aSAndreas Boehler 719cb71a62aSAndreas Boehler // Retrieve dates from settings 720b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 721b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 722b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 723b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 724cb71a62aSAndreas Boehler 725cb71a62aSAndreas Boehler // Load SabreDAV 7269bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 727a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 728cb71a62aSAndreas Boehler 729cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 730a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 731b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 732b269830cSAndreas Boehler $event->add('UID', $uuid); 733a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 734cb71a62aSAndreas Boehler 735cb71a62aSAndreas Boehler // Add a description if requested 7360eebc909SAndreas Boehler $description = $params['eventdescription']; 7370eebc909SAndreas Boehler if($description !== '') 7380eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 739cb71a62aSAndreas Boehler 7404ecb526cSAndreas Boehler // Add attachments 7414ecb526cSAndreas Boehler $attachments = $params['attachments']; 74282a48dfbSAndreas Boehler if(!is_null($attachments)) 7434ecb526cSAndreas Boehler foreach($attachments as $attachment) 7444ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 7454ecb526cSAndreas Boehler 746cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 747b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 748b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 749b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 750b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 751cb71a62aSAndreas Boehler 752cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 753b269830cSAndreas Boehler $dtStart = new \DateTime(); 754a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 755b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 756cb71a62aSAndreas Boehler 757cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 758b269830cSAndreas Boehler if($params['allday'] != '1') 759b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 760cb71a62aSAndreas Boehler 761cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 762b269830cSAndreas Boehler $dtEnd = new \DateTime(); 763a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 764b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 765cb71a62aSAndreas Boehler 766cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 767b269830cSAndreas Boehler if($params['allday'] != '1') 768b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 769cb71a62aSAndreas Boehler 770b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 771b269830cSAndreas Boehler if($params['allday'] == '1') 772b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 773cb71a62aSAndreas Boehler 774cb71a62aSAndreas Boehler // Really add Start and End events 775b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 776b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 777cb71a62aSAndreas Boehler 778cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 779b269830cSAndreas Boehler if($params['allday'] == '1') 780b269830cSAndreas Boehler { 781b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 782b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 783b269830cSAndreas Boehler } 784cb71a62aSAndreas Boehler 785809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 786809cb0faSAndreas Boehler 787809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 788809cb0faSAndreas Boehler { 789809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 790809cb0faSAndreas Boehler if(is_null($wdc)) 791809cb0faSAndreas Boehler return false; 792809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 793809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 794809cb0faSAndreas Boehler } 795809cb0faSAndreas Boehler else 796809cb0faSAndreas Boehler { 797cb71a62aSAndreas Boehler // Actually add the values to the database 798a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 799a1a3b679SAndreas Boehler $uri = uniqid('dokuwiki-').'.ics'; 8001bb22c2bSAndreas Boehler $now = new \DateTime(); 801a1a3b679SAndreas Boehler 80251f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 80351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 80451f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 80551f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 806cb71a62aSAndreas Boehler 807cb71a62aSAndreas Boehler // If successfully, update the sync token database 80855a741c0SAndreas Boehler if($res !== false) 80955a741c0SAndreas Boehler { 81055a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 811a1a3b679SAndreas Boehler return true; 812a1a3b679SAndreas Boehler } 813809cb0faSAndreas Boehler } 81455a741c0SAndreas Boehler return false; 81555a741c0SAndreas Boehler } 816a1a3b679SAndreas Boehler 817cb71a62aSAndreas Boehler /** 818cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 819cb71a62aSAndreas Boehler * 820cb71a62aSAndreas Boehler * @param string $calid The calendar ID 821cb71a62aSAndreas Boehler * 822cb71a62aSAndreas Boehler * @return array The calendar settings array 823cb71a62aSAndreas Boehler */ 824b269830cSAndreas Boehler public function getCalendarSettings($calid) 825b269830cSAndreas Boehler { 826d5703f5aSAndreas Boehler $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? "; 82751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 828b269830cSAndreas Boehler $row = $this->sqlite->res2row($res); 829b269830cSAndreas Boehler return $row; 830b269830cSAndreas Boehler } 831b269830cSAndreas Boehler 832cb71a62aSAndreas Boehler /** 833cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 834cb71a62aSAndreas Boehler * based on the timezone setting. 835cb71a62aSAndreas Boehler * 836cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 837cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 838cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 839cb71a62aSAndreas Boehler * 840cb71a62aSAndreas Boehler * @param string $id The page ID to work with 841cb71a62aSAndreas Boehler * @param string $user The user ID to work with 842cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 843cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 8444a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 845cb71a62aSAndreas Boehler * 846cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 847cb71a62aSAndreas Boehler */ 8484a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 849a1a3b679SAndreas Boehler { 85082a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 85182a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 852bd883736SAndreas Boehler else 853bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 854a1a3b679SAndreas Boehler $data = array(); 855cb71a62aSAndreas Boehler 856a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 857a469597cSAndreas Boehler $startTs = null; 858a469597cSAndreas Boehler $endTs = null; 859a469597cSAndreas Boehler if($startDate !== null) 860a469597cSAndreas Boehler { 861a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 862a469597cSAndreas Boehler $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp()); 863a469597cSAndreas Boehler } 864a469597cSAndreas Boehler if($endDate !== null) 865a469597cSAndreas Boehler { 866a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 867a469597cSAndreas Boehler $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp()); 868a469597cSAndreas Boehler } 869cb71a62aSAndreas Boehler 8700b805092SAndreas Boehler // Load SabreDAV 8710b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 8720b805092SAndreas Boehler 8730b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 8740b805092SAndreas Boehler { 8750b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 8760b805092SAndreas Boehler if(is_null($wdc)) 8770b805092SAndreas Boehler return $data; 8780b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 8790b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 8800b805092SAndreas Boehler } 8810b805092SAndreas Boehler else 8820b805092SAndreas Boehler { 8830b805092SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 8840b805092SAndreas Boehler if(is_null($color)) 8850b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 8860b805092SAndreas Boehler 887cb71a62aSAndreas Boehler // Retrieve matching calendar objects 888a469597cSAndreas Boehler $res = $this->sqlite->query($query, $calid); 889a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 8900b805092SAndreas Boehler } 891cb71a62aSAndreas Boehler 892cb71a62aSAndreas Boehler // Parse individual calendar entries 893a1a3b679SAndreas Boehler foreach($arr as $row) 894a1a3b679SAndreas Boehler { 895a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 896a1a3b679SAndreas Boehler { 897b269830cSAndreas Boehler $entry = array(); 898a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 899ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 900cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 901ebc4eb57SAndreas Boehler if($recurrence != null) 902ebc4eb57SAndreas Boehler { 903ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 904ebc4eb57SAndreas Boehler $rEvents->rewind(); 905e9b7d302SAndreas Boehler while($rEvents->valid()) 906ebc4eb57SAndreas Boehler { 907ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 908cb71a62aSAndreas Boehler // If we are after the given time range, exit 909a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 910e9b7d302SAndreas Boehler break; 911cb71a62aSAndreas Boehler 912cb71a62aSAndreas Boehler // If we are before the given time range, continue 913a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 914ebc4eb57SAndreas Boehler { 915ebc4eb57SAndreas Boehler $rEvents->next(); 916ebc4eb57SAndreas Boehler continue; 917ebc4eb57SAndreas Boehler } 918cb71a62aSAndreas Boehler 919cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 920185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true); 921ebc4eb57SAndreas Boehler $rEvents->next(); 922ebc4eb57SAndreas Boehler } 923ebc4eb57SAndreas Boehler } 924ebc4eb57SAndreas Boehler else 925185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color); 926ebc4eb57SAndreas Boehler } 927ebc4eb57SAndreas Boehler } 928ebc4eb57SAndreas Boehler return $data; 929ebc4eb57SAndreas Boehler } 930ebc4eb57SAndreas Boehler 931cb71a62aSAndreas Boehler /** 932cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 933cb71a62aSAndreas Boehler * 934cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 935cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 936cb71a62aSAndreas Boehler * @param string $uid The entry's UID 9373c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 938cb71a62aSAndreas Boehler * 939cb71a62aSAndreas Boehler * @return array The parse calendar entry 940cb71a62aSAndreas Boehler */ 941185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 942ebc4eb57SAndreas Boehler { 943ebc4eb57SAndreas Boehler $entry = array(); 944ebc4eb57SAndreas Boehler $start = $event->DTSTART; 945cb71a62aSAndreas Boehler // Parse only if the start date/time is present 946b269830cSAndreas Boehler if($start !== null) 947b269830cSAndreas Boehler { 948b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 949b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 950bf0ad2b4SAndreas Boehler 951bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 952bf0ad2b4SAndreas Boehler // allDay is set to true 953bf0ad2b4SAndreas Boehler // This should fix T23 954b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 955bf0ad2b4SAndreas Boehler { 956b269830cSAndreas Boehler $entry['allDay'] = true; 957bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 958bf0ad2b4SAndreas Boehler } 959b269830cSAndreas Boehler else 960bf0ad2b4SAndreas Boehler { 961b269830cSAndreas Boehler $entry['allDay'] = false; 962bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 963bf0ad2b4SAndreas Boehler } 964b269830cSAndreas Boehler } 965ebc4eb57SAndreas Boehler $end = $event->DTEND; 966bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 967b269830cSAndreas Boehler if($end !== null) 968b269830cSAndreas Boehler { 969b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 970b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 971bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 972bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 973bf0ad2b4SAndreas Boehler else 974b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 975b269830cSAndreas Boehler } 976ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 9770eebc909SAndreas Boehler if($description !== null) 9780eebc909SAndreas Boehler $entry['description'] = (string)$description; 9790eebc909SAndreas Boehler else 9800eebc909SAndreas Boehler $entry['description'] = ''; 9814ecb526cSAndreas Boehler $attachments = $event->ATTACH; 9824ecb526cSAndreas Boehler if($attachments !== null) 9834ecb526cSAndreas Boehler { 9844ecb526cSAndreas Boehler $entry['attachments'] = array(); 9854ecb526cSAndreas Boehler foreach($attachments as $attachment) 9864ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 9874ecb526cSAndreas Boehler } 988ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 989ebc4eb57SAndreas Boehler $entry['id'] = $uid; 990185e2535SAndreas Boehler $entry['page'] = $page; 991185e2535SAndreas Boehler $entry['color'] = $color; 9923c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 993185e2535SAndreas Boehler 994ebc4eb57SAndreas Boehler return $entry; 995a1a3b679SAndreas Boehler } 996a1a3b679SAndreas Boehler 997cb71a62aSAndreas Boehler /** 998cb71a62aSAndreas Boehler * Retrieve an event by its UID 999cb71a62aSAndreas Boehler * 1000cb71a62aSAndreas Boehler * @param string $uid The event's UID 1001cb71a62aSAndreas Boehler * 1002cb71a62aSAndreas Boehler * @return mixed The table row with the given event 1003cb71a62aSAndreas Boehler */ 1004a1a3b679SAndreas Boehler public function getEventWithUid($uid) 1005a1a3b679SAndreas Boehler { 100651f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 100751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 1008a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 1009a1a3b679SAndreas Boehler return $row; 1010a1a3b679SAndreas Boehler } 1011a1a3b679SAndreas Boehler 1012cb71a62aSAndreas Boehler /** 1013d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 1014d5703f5aSAndreas Boehler * calendar data! This is mainly neede for the sync support. 1015d5703f5aSAndreas Boehler * 1016d5703f5aSAndreas Boehler * @param int $calid The calendar ID 1017d5703f5aSAndreas Boehler * 1018d5703f5aSAndreas Boehler * @return mixed The result 1019d5703f5aSAndreas Boehler */ 1020d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 1021d5703f5aSAndreas Boehler { 1022d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 1023d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1024d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1025d5703f5aSAndreas Boehler return $arr; 1026d5703f5aSAndreas Boehler } 1027d5703f5aSAndreas Boehler 1028d5703f5aSAndreas Boehler /** 1029d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 1030d5703f5aSAndreas Boehler * 1031d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1032d5703f5aSAndreas Boehler * @param string $uri The object's URI 1033d5703f5aSAndreas Boehler * 1034d5703f5aSAndreas Boehler * @return mixed The result 1035d5703f5aSAndreas Boehler */ 1036d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1037d5703f5aSAndreas Boehler { 1038d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1039d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1040d5703f5aSAndreas Boehler $row = $this->sqlite->res2row($res); 1041d5703f5aSAndreas Boehler return $row; 1042d5703f5aSAndreas Boehler } 1043d5703f5aSAndreas Boehler 1044d5703f5aSAndreas Boehler /** 1045d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1046d5703f5aSAndreas Boehler * This is mainly neede for sync. 1047d5703f5aSAndreas Boehler * 1048d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1049d5703f5aSAndreas Boehler * @param array $uris An array of URIs 1050d5703f5aSAndreas Boehler * 1051d5703f5aSAndreas Boehler * @return mixed The result 1052d5703f5aSAndreas Boehler */ 1053d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1054d5703f5aSAndreas Boehler { 1055d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1056d5703f5aSAndreas Boehler // Inserting a whole bunch of question marks 1057d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1058d5703f5aSAndreas Boehler $query .= ')'; 1059d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 1060d5703f5aSAndreas Boehler 1061d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $vals); 1062d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1063d5703f5aSAndreas Boehler return $arr; 1064d5703f5aSAndreas Boehler } 1065d5703f5aSAndreas Boehler 1066d5703f5aSAndreas Boehler /** 1067cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1068cb71a62aSAndreas Boehler * 1069cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1070cb71a62aSAndreas Boehler * 1071cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1072cb71a62aSAndreas Boehler */ 1073f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1074f69bb449SAndreas Boehler { 10757e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 107651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1077f69bb449SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1078f69bb449SAndreas Boehler return $arr; 1079f69bb449SAndreas Boehler } 1080f69bb449SAndreas Boehler 1081cb71a62aSAndreas Boehler /** 1082cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1083cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1084cb71a62aSAndreas Boehler * 1085cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1086cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1087cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1088cb71a62aSAndreas Boehler * 1089cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1090cb71a62aSAndreas Boehler */ 1091a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1092a1a3b679SAndreas Boehler { 109382a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 109482a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 109582a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1096a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1097bd883736SAndreas Boehler else 1098bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1099cb71a62aSAndreas Boehler 1100cb71a62aSAndreas Boehler // Parse dates 1101b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1102b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1103b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1104b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1105cb71a62aSAndreas Boehler 1106cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 110755a741c0SAndreas Boehler $uid = $params['uid']; 1108809cb0faSAndreas Boehler 1109809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1110809cb0faSAndreas Boehler { 1111809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1112809cb0faSAndreas Boehler if(is_null($wdc)) 1113809cb0faSAndreas Boehler return false; 1114809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1115809cb0faSAndreas Boehler } 1116809cb0faSAndreas Boehler else 1117809cb0faSAndreas Boehler { 111855a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1119809cb0faSAndreas Boehler } 1120cb71a62aSAndreas Boehler 1121cb71a62aSAndreas Boehler // Load SabreDAV 11229bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1123a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1124a1a3b679SAndreas Boehler return false; 112555a741c0SAndreas Boehler $uri = $event['uri']; 112655a741c0SAndreas Boehler $calid = $event['calendarid']; 1127cb71a62aSAndreas Boehler 1128cb71a62aSAndreas Boehler // Parse the existing event 1129a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1130b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1131cb71a62aSAndreas Boehler 1132cb71a62aSAndreas Boehler // Set the new event values 1133b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1134b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 11350eebc909SAndreas Boehler $description = $params['eventdescription']; 1136cb71a62aSAndreas Boehler 1137cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 11380eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1139b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1140b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 11414ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 1142cb71a62aSAndreas Boehler 1143cb71a62aSAndreas Boehler // Add new time stamps and description 1144b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1145b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 11460eebc909SAndreas Boehler if($description !== '') 11470eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 1148cb71a62aSAndreas Boehler 11494ecb526cSAndreas Boehler // Add attachments 11504ecb526cSAndreas Boehler $attachments = $params['attachments']; 115182a48dfbSAndreas Boehler if(!is_null($attachments)) 11524ecb526cSAndreas Boehler foreach($attachments as $attachment) 11534ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 11544ecb526cSAndreas Boehler 1155cb71a62aSAndreas Boehler // Setup DTSTART 1156b269830cSAndreas Boehler $dtStart = new \DateTime(); 1157a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1158b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1159b269830cSAndreas Boehler if($params['allday'] != '1') 1160b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1161cb71a62aSAndreas Boehler 11624ecb526cSAndreas Boehler // Setup DTEND 1163b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1164a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1165b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1166b269830cSAndreas Boehler if($params['allday'] != '1') 1167b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1168cb71a62aSAndreas Boehler 1169b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1170b269830cSAndreas Boehler if($params['allday'] == '1') 1171b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1172b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1173b269830cSAndreas Boehler $vevent->remove('DTEND'); 1174b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1175b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1176cb71a62aSAndreas Boehler 1177cb71a62aSAndreas Boehler // Remove the time for allday events 1178b269830cSAndreas Boehler if($params['allday'] == '1') 1179b269830cSAndreas Boehler { 1180b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1181b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1182b269830cSAndreas Boehler } 1183a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1184809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1185809cb0faSAndreas Boehler { 1186809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1187809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1188809cb0faSAndreas Boehler } 1189809cb0faSAndreas Boehler else 1190809cb0faSAndreas Boehler { 1191809cb0faSAndreas Boehler $now = new DateTime(); 1192cb71a62aSAndreas Boehler // Actually write to the database 119351f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 119451f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 119551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 119651f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 119755a741c0SAndreas Boehler if($res !== false) 119855a741c0SAndreas Boehler { 119955a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1200a1a3b679SAndreas Boehler return true; 1201a1a3b679SAndreas Boehler } 1202809cb0faSAndreas Boehler } 120355a741c0SAndreas Boehler return false; 120455a741c0SAndreas Boehler } 1205a1a3b679SAndreas Boehler 1206cb71a62aSAndreas Boehler /** 1207d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1208d5703f5aSAndreas Boehler * 1209d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1210d5703f5aSAndreas Boehler * @param string $uri The object's URI 1211d5703f5aSAndreas Boehler * 1212d5703f5aSAndreas Boehler * @return true 1213d5703f5aSAndreas Boehler */ 1214d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1215d5703f5aSAndreas Boehler { 1216d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1217d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1218d5703f5aSAndreas Boehler if($res !== false) 1219d5703f5aSAndreas Boehler { 1220d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1221d5703f5aSAndreas Boehler } 1222d5703f5aSAndreas Boehler return true; 1223d5703f5aSAndreas Boehler } 1224d5703f5aSAndreas Boehler 1225d5703f5aSAndreas Boehler /** 1226cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1227cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1228cb71a62aSAndreas Boehler * 1229cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1230cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1231cb71a62aSAndreas Boehler * 1232cb71a62aSAndreas Boehler * @return boolean True 1233cb71a62aSAndreas Boehler */ 1234a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1235a1a3b679SAndreas Boehler { 1236a1a3b679SAndreas Boehler $uid = $params['uid']; 1237809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1238809cb0faSAndreas Boehler { 1239809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1240809cb0faSAndreas Boehler if(is_null($wdc)) 1241809cb0faSAndreas Boehler return false; 1242809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1243809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1244809cb0faSAndreas Boehler return $result; 1245809cb0faSAndreas Boehler } 124655a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 12472c14b82bSAndreas Boehler $calid = $event['calendarid']; 124855a741c0SAndreas Boehler $uri = $event['uri']; 124951f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 125051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 125155a741c0SAndreas Boehler if($res !== false) 125255a741c0SAndreas Boehler { 125355a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 125455a741c0SAndreas Boehler } 1255a1a3b679SAndreas Boehler return true; 1256a1a3b679SAndreas Boehler } 1257a1a3b679SAndreas Boehler 1258cb71a62aSAndreas Boehler /** 1259cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1260cb71a62aSAndreas Boehler * 1261cb71a62aSAndreas Boehler * @param string $calid The calendar id 1262cb71a62aSAndreas Boehler * 1263cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1264cb71a62aSAndreas Boehler */ 126555a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 126655a741c0SAndreas Boehler { 1267b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 126855a741c0SAndreas Boehler if(isset($row['synctoken'])) 126955a741c0SAndreas Boehler return $row['synctoken']; 127055a741c0SAndreas Boehler return false; 127155a741c0SAndreas Boehler } 127255a741c0SAndreas Boehler 1273cb71a62aSAndreas Boehler /** 1274cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1275cb71a62aSAndreas Boehler * an operation code as stored in the database 1276cb71a62aSAndreas Boehler * 1277cb71a62aSAndreas Boehler * @param string $operationName The operation name 1278cb71a62aSAndreas Boehler * 1279cb71a62aSAndreas Boehler * @return mixed The operation code or false 1280cb71a62aSAndreas Boehler */ 128155a741c0SAndreas Boehler public function operationNameToOperation($operationName) 128255a741c0SAndreas Boehler { 128355a741c0SAndreas Boehler switch($operationName) 128455a741c0SAndreas Boehler { 128555a741c0SAndreas Boehler case 'added': 128655a741c0SAndreas Boehler return 1; 128755a741c0SAndreas Boehler break; 128855a741c0SAndreas Boehler case 'modified': 128955a741c0SAndreas Boehler return 2; 129055a741c0SAndreas Boehler break; 129155a741c0SAndreas Boehler case 'deleted': 129255a741c0SAndreas Boehler return 3; 129355a741c0SAndreas Boehler break; 129455a741c0SAndreas Boehler } 129555a741c0SAndreas Boehler return false; 129655a741c0SAndreas Boehler } 129755a741c0SAndreas Boehler 1298cb71a62aSAndreas Boehler /** 1299cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1300cb71a62aSAndreas Boehler * operation that was performed. 1301cb71a62aSAndreas Boehler * 1302cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1303cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1304cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1305cb71a62aSAndreas Boehler * 1306cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1307cb71a62aSAndreas Boehler */ 130855a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 130955a741c0SAndreas Boehler { 131055a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 131155a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 131255a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 131355a741c0SAndreas Boehler return false; 131455a741c0SAndreas Boehler $values = array($uri, 131555a741c0SAndreas Boehler $currentToken, 131655a741c0SAndreas Boehler $calid, 131755a741c0SAndreas Boehler $operationCode 131855a741c0SAndreas Boehler ); 131951f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 132051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 132155a741c0SAndreas Boehler if($res === false) 132255a741c0SAndreas Boehler return false; 132355a741c0SAndreas Boehler $currentToken++; 132451f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 132551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $currentToken, $calid); 132655a741c0SAndreas Boehler return ($res !== false); 132755a741c0SAndreas Boehler } 132855a741c0SAndreas Boehler 1329cb71a62aSAndreas Boehler /** 1330cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1331cb71a62aSAndreas Boehler * 1332cb71a62aSAndreas Boehler * @param string $id The page's ID 1333cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1334cb71a62aSAndreas Boehler * 1335cb71a62aSAndreas Boehler * @return mixed The sync url or false 1336cb71a62aSAndreas Boehler */ 1337b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1338b269830cSAndreas Boehler { 133934a47953SAndreas Boehler if(is_null($userid)) 134034a47953SAndreas Boehler { 134134a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 134234a47953SAndreas Boehler { 134334a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 134434a47953SAndreas Boehler } 134534a47953SAndreas Boehler else 134634a47953SAndreas Boehler { 134734a47953SAndreas Boehler return false; 134834a47953SAndreas Boehler } 134934a47953SAndreas Boehler } 1350b269830cSAndreas Boehler 1351b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1352b269830cSAndreas Boehler if($calid === false) 1353b269830cSAndreas Boehler return false; 1354b269830cSAndreas Boehler 1355b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1356b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1357b269830cSAndreas Boehler return false; 1358b269830cSAndreas Boehler 1359b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1360b269830cSAndreas Boehler return $syncurl; 1361b269830cSAndreas Boehler } 1362b269830cSAndreas Boehler 1363cb71a62aSAndreas Boehler /** 1364cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1365cb71a62aSAndreas Boehler * 1366cb71a62aSAndreas Boehler * @param string $id the page ID 1367cb71a62aSAndreas Boehler * 1368cb71a62aSAndreas Boehler * @return mixed The private URL or false 1369cb71a62aSAndreas Boehler */ 1370f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1371f69bb449SAndreas Boehler { 1372f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1373f69bb449SAndreas Boehler if($calid === false) 1374f69bb449SAndreas Boehler return false; 1375f69bb449SAndreas Boehler 1376f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1377f69bb449SAndreas Boehler } 1378f69bb449SAndreas Boehler 1379cb71a62aSAndreas Boehler /** 1380cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1381cb71a62aSAndreas Boehler * 1382cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1383cb71a62aSAndreas Boehler * 1384cb71a62aSAndreas Boehler * @return mixed The private URL or false 1385cb71a62aSAndreas Boehler */ 1386f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1387f69bb449SAndreas Boehler { 1388185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1389185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 139051f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 139151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1392f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1393f69bb449SAndreas Boehler if(!isset($row['url'])) 1394f69bb449SAndreas Boehler { 1395f69bb449SAndreas Boehler $url = uniqid("dokuwiki-").".ics"; 139651f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 139751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url, $calid); 1398f69bb449SAndreas Boehler if($res === false) 1399f69bb449SAndreas Boehler return false; 1400f69bb449SAndreas Boehler } 1401f69bb449SAndreas Boehler else 1402f69bb449SAndreas Boehler { 1403f69bb449SAndreas Boehler $url = $row['url']; 1404f69bb449SAndreas Boehler } 1405185e2535SAndreas Boehler 1406185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1407185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1408185e2535SAndreas Boehler return $url; 1409f69bb449SAndreas Boehler } 1410f69bb449SAndreas Boehler 1411cb71a62aSAndreas Boehler /** 1412cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1413cb71a62aSAndreas Boehler * 1414cb71a62aSAndreas Boehler * @param string $url The private URL 1415cb71a62aSAndreas Boehler * 1416cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1417cb71a62aSAndreas Boehler */ 1418f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1419f69bb449SAndreas Boehler { 142051f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 142151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url); 1422f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1423f69bb449SAndreas Boehler if(!isset($row['calid'])) 1424f69bb449SAndreas Boehler return false; 1425f69bb449SAndreas Boehler return $row['calid']; 1426f69bb449SAndreas Boehler } 1427f69bb449SAndreas Boehler 1428cb71a62aSAndreas Boehler /** 1429cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1430cb71a62aSAndreas Boehler * 14317e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1432cb71a62aSAndreas Boehler * 1433cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1434cb71a62aSAndreas Boehler */ 1435f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1436f69bb449SAndreas Boehler { 1437f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1438f69bb449SAndreas Boehler if($calSettings === false) 1439f69bb449SAndreas Boehler return false; 1440f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1441f69bb449SAndreas Boehler if($events === false) 1442f69bb449SAndreas Boehler return false; 1443f69bb449SAndreas Boehler 14447e0b8590SAndreas Boehler // Load SabreDAV 14457e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 14467e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 14477e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1448f69bb449SAndreas Boehler foreach($events as $event) 1449f69bb449SAndreas Boehler { 14507e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 14517e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 14527e0b8590SAndreas Boehler $out .= $evt->serialize(); 1453f69bb449SAndreas Boehler } 14547e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1455f69bb449SAndreas Boehler return $out; 1456f69bb449SAndreas Boehler } 1457f69bb449SAndreas Boehler 14587c7c6b0bSAndreas Boehler /** 14597c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 14607c7c6b0bSAndreas Boehler * 14617c7c6b0bSAndreas Boehler * @param string $key The key to query 146221d04f73SAndreas Boehler * @return mixed The option set, null if not found 14637c7c6b0bSAndreas Boehler */ 14647c7c6b0bSAndreas Boehler public function getConfig($key) 14657c7c6b0bSAndreas Boehler { 14667c7c6b0bSAndreas Boehler return $this->getConf($key); 14677c7c6b0bSAndreas Boehler } 14687c7c6b0bSAndreas Boehler 1469d5703f5aSAndreas Boehler /** 1470d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1471d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1472d5703f5aSAndreas Boehler * 1473d5703f5aSAndreas Boehler * Returns an array with the following keys: 1474d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1475d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1476d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1477d5703f5aSAndreas Boehler * * firstOccurence 1478d5703f5aSAndreas Boehler * * lastOccurence 1479d5703f5aSAndreas Boehler * * uid - value of the UID property 1480d5703f5aSAndreas Boehler * 1481d5703f5aSAndreas Boehler * @param string $calendarData 1482d5703f5aSAndreas Boehler * @return array 1483d5703f5aSAndreas Boehler */ 1484d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1485d5703f5aSAndreas Boehler { 1486d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1487d5703f5aSAndreas Boehler 1488d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1489d5703f5aSAndreas Boehler $componentType = null; 1490d5703f5aSAndreas Boehler $component = null; 1491d5703f5aSAndreas Boehler $firstOccurence = null; 1492d5703f5aSAndreas Boehler $lastOccurence = null; 1493d5703f5aSAndreas Boehler $uid = null; 1494d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1495d5703f5aSAndreas Boehler { 1496d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1497d5703f5aSAndreas Boehler { 1498d5703f5aSAndreas Boehler $componentType = $component->name; 1499d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1500d5703f5aSAndreas Boehler break; 1501d5703f5aSAndreas Boehler } 1502d5703f5aSAndreas Boehler } 1503d5703f5aSAndreas Boehler if (!$componentType) 1504d5703f5aSAndreas Boehler { 1505d5703f5aSAndreas Boehler return false; 1506d5703f5aSAndreas Boehler } 1507d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1508d5703f5aSAndreas Boehler { 1509d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1510d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1511d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1512d5703f5aSAndreas Boehler { 1513d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1514d5703f5aSAndreas Boehler { 1515d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1516d5703f5aSAndreas Boehler } 1517d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1518d5703f5aSAndreas Boehler { 1519d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1520d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1521d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1522d5703f5aSAndreas Boehler } 1523d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1524d5703f5aSAndreas Boehler { 1525d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1526d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1527d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1528d5703f5aSAndreas Boehler } 1529d5703f5aSAndreas Boehler else 1530d5703f5aSAndreas Boehler { 1531d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1532d5703f5aSAndreas Boehler } 1533d5703f5aSAndreas Boehler } 1534d5703f5aSAndreas Boehler else 1535d5703f5aSAndreas Boehler { 1536d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1537d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1538d5703f5aSAndreas Boehler if ($it->isInfinite()) 1539d5703f5aSAndreas Boehler { 1540d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1541d5703f5aSAndreas Boehler } 1542d5703f5aSAndreas Boehler else 1543d5703f5aSAndreas Boehler { 1544d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1545d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1546d5703f5aSAndreas Boehler { 1547d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1548d5703f5aSAndreas Boehler $it->next(); 1549d5703f5aSAndreas Boehler } 1550d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1551d5703f5aSAndreas Boehler } 1552d5703f5aSAndreas Boehler } 1553d5703f5aSAndreas Boehler } 1554d5703f5aSAndreas Boehler 1555d5703f5aSAndreas Boehler return array( 1556d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1557d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1558d5703f5aSAndreas Boehler 'componentType' => $componentType, 1559d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1560d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1561d5703f5aSAndreas Boehler 'uid' => $uid, 1562d5703f5aSAndreas Boehler ); 1563d5703f5aSAndreas Boehler 1564d5703f5aSAndreas Boehler } 1565d5703f5aSAndreas Boehler 1566d5703f5aSAndreas Boehler /** 1567d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1568d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1569d5703f5aSAndreas Boehler * 1570d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1571d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1572d5703f5aSAndreas Boehler * 1573d5703f5aSAndreas Boehler * @return mixed The result 1574d5703f5aSAndreas Boehler */ 1575d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1576d5703f5aSAndreas Boehler { 1577d5703f5aSAndreas Boehler $componentType = null; 1578d5703f5aSAndreas Boehler $requirePostFilter = true; 1579d5703f5aSAndreas Boehler $timeRange = null; 1580d5703f5aSAndreas Boehler 1581d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1582d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1583d5703f5aSAndreas Boehler { 1584d5703f5aSAndreas Boehler $requirePostFilter = false; 1585d5703f5aSAndreas Boehler } 1586d5703f5aSAndreas Boehler 1587d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1588d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1589d5703f5aSAndreas Boehler { 1590d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1591d5703f5aSAndreas Boehler 1592d5703f5aSAndreas Boehler // Checking if we need post-filters 1593d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1594d5703f5aSAndreas Boehler { 1595d5703f5aSAndreas Boehler $requirePostFilter = false; 1596d5703f5aSAndreas Boehler } 1597d5703f5aSAndreas Boehler // There was a time-range filter 1598d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1599d5703f5aSAndreas Boehler { 1600d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1601d5703f5aSAndreas Boehler 1602d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1603d5703f5aSAndreas Boehler // 100% accurate mysql query. 1604d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1605d5703f5aSAndreas Boehler { 1606d5703f5aSAndreas Boehler $requirePostFilter = false; 1607d5703f5aSAndreas Boehler } 1608d5703f5aSAndreas Boehler } 1609d5703f5aSAndreas Boehler 1610d5703f5aSAndreas Boehler } 1611d5703f5aSAndreas Boehler 1612d5703f5aSAndreas Boehler if ($requirePostFilter) 1613d5703f5aSAndreas Boehler { 1614d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1615d5703f5aSAndreas Boehler } 1616d5703f5aSAndreas Boehler else 1617d5703f5aSAndreas Boehler { 1618d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1619d5703f5aSAndreas Boehler } 1620d5703f5aSAndreas Boehler 1621d5703f5aSAndreas Boehler $values = array( 1622d5703f5aSAndreas Boehler $calendarId 1623d5703f5aSAndreas Boehler ); 1624d5703f5aSAndreas Boehler 1625d5703f5aSAndreas Boehler if ($componentType) 1626d5703f5aSAndreas Boehler { 1627d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1628d5703f5aSAndreas Boehler $values[] = $componentType; 1629d5703f5aSAndreas Boehler } 1630d5703f5aSAndreas Boehler 1631d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1632d5703f5aSAndreas Boehler { 1633d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1634d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1635d5703f5aSAndreas Boehler } 1636d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1637d5703f5aSAndreas Boehler { 1638d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1639d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1640d5703f5aSAndreas Boehler } 1641d5703f5aSAndreas Boehler 1642d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $values); 1643d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1644d5703f5aSAndreas Boehler 1645d5703f5aSAndreas Boehler $result = array(); 1646d5703f5aSAndreas Boehler foreach($arr as $row) 1647d5703f5aSAndreas Boehler { 1648d5703f5aSAndreas Boehler if ($requirePostFilter) 1649d5703f5aSAndreas Boehler { 1650d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1651d5703f5aSAndreas Boehler { 1652d5703f5aSAndreas Boehler continue; 1653d5703f5aSAndreas Boehler } 1654d5703f5aSAndreas Boehler } 1655d5703f5aSAndreas Boehler $result[] = $row['uri']; 1656d5703f5aSAndreas Boehler 1657d5703f5aSAndreas Boehler } 1658d5703f5aSAndreas Boehler 1659d5703f5aSAndreas Boehler return $result; 1660d5703f5aSAndreas Boehler } 1661d5703f5aSAndreas Boehler 1662d5703f5aSAndreas Boehler /** 1663d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1664d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1665d5703f5aSAndreas Boehler * 1666d5703f5aSAndreas Boehler * @param array $object 1667d5703f5aSAndreas Boehler * @param array $filters 1668d5703f5aSAndreas Boehler * @return bool 1669d5703f5aSAndreas Boehler */ 1670d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 1671d5703f5aSAndreas Boehler { 1672d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1673d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1674d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 1675d5703f5aSAndreas Boehler // well. 1676d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 1677d5703f5aSAndreas Boehler { 1678d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1679d5703f5aSAndreas Boehler } 1680d5703f5aSAndreas Boehler 1681d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1682d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1683d5703f5aSAndreas Boehler 1684d5703f5aSAndreas Boehler return $validator->validate($vObject, $filters); 1685d5703f5aSAndreas Boehler 1686d5703f5aSAndreas Boehler } 1687d5703f5aSAndreas Boehler 1688d5703f5aSAndreas Boehler /** 1689d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1690d5703f5aSAndreas Boehler * 1691d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1692d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 1693d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 1694d5703f5aSAndreas Boehler * @param int $limit The limit of changes 1695d5703f5aSAndreas Boehler * 1696d5703f5aSAndreas Boehler * @return array The result 1697d5703f5aSAndreas Boehler */ 1698d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1699d5703f5aSAndreas Boehler { 1700d5703f5aSAndreas Boehler // Current synctoken 1701d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1702d5703f5aSAndreas Boehler 1703d5703f5aSAndreas Boehler if ($currentToken === false) return null; 1704d5703f5aSAndreas Boehler 1705d5703f5aSAndreas Boehler $result = array( 1706d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 1707d5703f5aSAndreas Boehler 'added' => array(), 1708d5703f5aSAndreas Boehler 'modified' => array(), 1709d5703f5aSAndreas Boehler 'deleted' => array(), 1710d5703f5aSAndreas Boehler ); 1711d5703f5aSAndreas Boehler 1712d5703f5aSAndreas Boehler if ($syncToken) 1713d5703f5aSAndreas Boehler { 1714d5703f5aSAndreas Boehler 1715d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 1716d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 1717d5703f5aSAndreas Boehler 1718d5703f5aSAndreas Boehler // Fetching all changes 1719d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid); 1720d5703f5aSAndreas Boehler if($res === false) 1721d5703f5aSAndreas Boehler return null; 1722d5703f5aSAndreas Boehler 1723d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1724d5703f5aSAndreas Boehler $changes = array(); 1725d5703f5aSAndreas Boehler 1726d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 1727d5703f5aSAndreas Boehler // last change on a node is relevant. 1728d5703f5aSAndreas Boehler foreach($arr as $row) 1729d5703f5aSAndreas Boehler { 1730d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 1731d5703f5aSAndreas Boehler } 1732d5703f5aSAndreas Boehler 1733d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 1734d5703f5aSAndreas Boehler { 1735d5703f5aSAndreas Boehler switch ($operation) 1736d5703f5aSAndreas Boehler { 1737d5703f5aSAndreas Boehler case 1 : 1738d5703f5aSAndreas Boehler $result['added'][] = $uri; 1739d5703f5aSAndreas Boehler break; 1740d5703f5aSAndreas Boehler case 2 : 1741d5703f5aSAndreas Boehler $result['modified'][] = $uri; 1742d5703f5aSAndreas Boehler break; 1743d5703f5aSAndreas Boehler case 3 : 1744d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 1745d5703f5aSAndreas Boehler break; 1746d5703f5aSAndreas Boehler } 1747d5703f5aSAndreas Boehler 1748d5703f5aSAndreas Boehler } 1749d5703f5aSAndreas Boehler } 1750d5703f5aSAndreas Boehler else 1751d5703f5aSAndreas Boehler { 1752d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 1753d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1754d5703f5aSAndreas Boehler $res = $this->sqlite->query($query); 1755d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1756d5703f5aSAndreas Boehler 1757d5703f5aSAndreas Boehler $result['added'] = $arr; 1758d5703f5aSAndreas Boehler } 1759d5703f5aSAndreas Boehler return $result; 1760d5703f5aSAndreas Boehler } 1761d5703f5aSAndreas Boehler 1762a1a3b679SAndreas Boehler} 1763