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 /** 8380e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 8480e1ddf7SAndreas Boehler * user has at least read permission. 8580e1ddf7SAndreas Boehler * 8680e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 8780e1ddf7SAndreas Boehler * @return array with filtered calendar pages 8880e1ddf7SAndreas Boehler */ 8980e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 9080e1ddf7SAndreas Boehler { 9180e1ddf7SAndreas Boehler $retList = array(); 9280e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 9380e1ddf7SAndreas Boehler { 9480e1ddf7SAndreas Boehler if(auth_quickaclcheck($page) >= AUTH_READ) 9580e1ddf7SAndreas Boehler { 9680e1ddf7SAndreas Boehler $retList[$page] = $data; 9780e1ddf7SAndreas Boehler } 9880e1ddf7SAndreas Boehler } 9980e1ddf7SAndreas Boehler return $retList; 10080e1ddf7SAndreas Boehler } 10180e1ddf7SAndreas Boehler 10280e1ddf7SAndreas Boehler /** 103185e2535SAndreas Boehler * Get all calendar pages used by a given page 104185e2535SAndreas Boehler * based on the stored metadata 105185e2535SAndreas Boehler * 106185e2535SAndreas Boehler * @param string $id optional The page id 107185e2535SAndreas Boehler * @return mixed The pages as array or false 108185e2535SAndreas Boehler */ 109185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 110185e2535SAndreas Boehler { 111185e2535SAndreas Boehler if(is_null($id)) 112185e2535SAndreas Boehler { 113185e2535SAndreas Boehler global $ID; 114185e2535SAndreas Boehler $id = $ID; 115185e2535SAndreas Boehler } 116185e2535SAndreas Boehler 117185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 118185e2535SAndreas Boehler if(isset($meta['id'])) 119ed764890SAndreas Boehler { 120ed764890SAndreas Boehler // Filter the list of pages by permission 12180e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 12280e1ddf7SAndreas Boehler if(empty($pages)) 123ed764890SAndreas Boehler return false; 12480e1ddf7SAndreas Boehler return $pages; 125ed764890SAndreas Boehler } 126185e2535SAndreas Boehler return false; 127185e2535SAndreas Boehler } 128185e2535SAndreas Boehler 129185e2535SAndreas Boehler /** 130185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 131185e2535SAndreas Boehler * for an array of page ids 132185e2535SAndreas Boehler * 133185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 134185e2535SAndreas Boehler * @return array The list 135185e2535SAndreas Boehler */ 136185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 137185e2535SAndreas Boehler { 138185e2535SAndreas Boehler $data = array(); 1394a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 140185e2535SAndreas Boehler { 141185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 142185e2535SAndreas Boehler if($calid !== false) 143185e2535SAndreas Boehler { 144185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 145185e2535SAndreas Boehler $name = $settings['displayname']; 146ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 147185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 148ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 149185e2535SAndreas Boehler } 150185e2535SAndreas Boehler } 151185e2535SAndreas Boehler return $data; 152185e2535SAndreas Boehler } 153185e2535SAndreas Boehler 154185e2535SAndreas Boehler /** 155185e2535SAndreas Boehler * Get the saved calendar color for a given page. 156185e2535SAndreas Boehler * 157185e2535SAndreas Boehler * @param string $id optional The page ID 158185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 159185e2535SAndreas Boehler */ 160185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 161185e2535SAndreas Boehler { 162185e2535SAndreas Boehler if(is_null($id)) 163185e2535SAndreas Boehler { 164185e2535SAndreas Boehler global $ID; 165185e2535SAndreas Boehler $id = $ID; 166185e2535SAndreas Boehler } 167185e2535SAndreas Boehler 168185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 169185e2535SAndreas Boehler if($calid === false) 170185e2535SAndreas Boehler return false; 171185e2535SAndreas Boehler 172185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 173185e2535SAndreas Boehler } 174185e2535SAndreas Boehler 175185e2535SAndreas Boehler /** 176185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 177185e2535SAndreas Boehler * 178185e2535SAndreas Boehler * @param string $id optional The calendar ID 179185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 180185e2535SAndreas Boehler */ 181185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 182185e2535SAndreas Boehler { 183185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 184185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 185185e2535SAndreas Boehler 186185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 187185e2535SAndreas Boehler 188185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 189185e2535SAndreas Boehler return false; 190185e2535SAndreas Boehler 191185e2535SAndreas Boehler $color = $row['calendarcolor']; 192185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 193185e2535SAndreas Boehler return $color; 194185e2535SAndreas Boehler } 195185e2535SAndreas Boehler 196185e2535SAndreas Boehler /** 197e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 198e86c8dd3SAndreas Boehler * @param string $user the user name 199e86c8dd3SAndreas Boehler * @return the URL to the principal sync 200e86c8dd3SAndreas Boehler */ 201e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 202e86c8dd3SAndreas Boehler { 203e86c8dd3SAndreas Boehler if(is_null($user)) 204e86c8dd3SAndreas Boehler return false; 205e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 206e86c8dd3SAndreas Boehler return $url; 207e86c8dd3SAndreas Boehler } 208e86c8dd3SAndreas Boehler 209e86c8dd3SAndreas Boehler /** 210185e2535SAndreas Boehler * Set the calendar color for a given page. 211185e2535SAndreas Boehler * 212185e2535SAndreas Boehler * @param string $color The color definition 213185e2535SAndreas Boehler * @param string $id optional The page ID 214185e2535SAndreas Boehler * @return boolean True on success, otherwise false 215185e2535SAndreas Boehler */ 216185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 217185e2535SAndreas Boehler { 218185e2535SAndreas Boehler if(is_null($id)) 219185e2535SAndreas Boehler { 220185e2535SAndreas Boehler global $ID; 221185e2535SAndreas Boehler $id = $ID; 222185e2535SAndreas Boehler } 223185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 224185e2535SAndreas Boehler if($calid === false) 225185e2535SAndreas Boehler return false; 226185e2535SAndreas Boehler 22751f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 22851f4febbSAndreas Boehler " WHERE id = ?"; 22951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $color, $calid); 230185e2535SAndreas Boehler if($res !== false) 231185e2535SAndreas Boehler { 232185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 233185e2535SAndreas Boehler return true; 234185e2535SAndreas Boehler } 235185e2535SAndreas Boehler return false; 236185e2535SAndreas Boehler } 237185e2535SAndreas Boehler 238185e2535SAndreas Boehler /** 239cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 240cb71a62aSAndreas Boehler * page id. 241cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 242cb71a62aSAndreas Boehler * 243cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 244cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 245cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 246cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 247cb71a62aSAndreas Boehler * 248cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 249cb71a62aSAndreas Boehler */ 250a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 251a1a3b679SAndreas Boehler { 252a1a3b679SAndreas Boehler if(is_null($id)) 253a1a3b679SAndreas Boehler { 254a1a3b679SAndreas Boehler global $ID; 255a1a3b679SAndreas Boehler $id = $ID; 256a1a3b679SAndreas Boehler } 257a1a3b679SAndreas Boehler if(is_null($userid)) 25834a47953SAndreas Boehler { 25934a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 26034a47953SAndreas Boehler { 261a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 26234a47953SAndreas Boehler } 26334a47953SAndreas Boehler else 26434a47953SAndreas Boehler { 26534a47953SAndreas Boehler $userid = uniqid('davcal-'); 26634a47953SAndreas Boehler } 26734a47953SAndreas Boehler } 268a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 269a1a3b679SAndreas Boehler if($calid === false) 270a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 271a1a3b679SAndreas Boehler 27251f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 27351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $name, $description, $calid); 274b269830cSAndreas Boehler if($res !== false) 275b269830cSAndreas Boehler return true; 276b269830cSAndreas Boehler return false; 277a1a3b679SAndreas Boehler } 278a1a3b679SAndreas Boehler 279cb71a62aSAndreas Boehler /** 280*d5703f5aSAndreas Boehler * Update a calendar's displayname 281*d5703f5aSAndreas Boehler * 282*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 283*d5703f5aSAndreas Boehler * @param string $name The new calendar name 284*d5703f5aSAndreas Boehler * 285*d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 286*d5703f5aSAndreas Boehler */ 287*d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 288*d5703f5aSAndreas Boehler { 289*d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 290*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $name); 291*d5703f5aSAndreas Boehler if($res !== false) 292*d5703f5aSAndreas Boehler { 293*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 294*d5703f5aSAndreas Boehler return true; 295*d5703f5aSAndreas Boehler } 296*d5703f5aSAndreas Boehler return false; 297*d5703f5aSAndreas Boehler } 298*d5703f5aSAndreas Boehler 299*d5703f5aSAndreas Boehler /** 300*d5703f5aSAndreas Boehler * Update the calendar description 301*d5703f5aSAndreas Boehler * 302*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 303*d5703f5aSAndreas Boehler * @param string $description The new calendar's description 304*d5703f5aSAndreas Boehler * 305*d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 306*d5703f5aSAndreas Boehler */ 307*d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 308*d5703f5aSAndreas Boehler { 309*d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 310*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $description); 311*d5703f5aSAndreas Boehler if($res !== false) 312*d5703f5aSAndreas Boehler { 313*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 314*d5703f5aSAndreas Boehler return true; 315*d5703f5aSAndreas Boehler } 316*d5703f5aSAndreas Boehler return false; 317*d5703f5aSAndreas Boehler } 318*d5703f5aSAndreas Boehler 319*d5703f5aSAndreas Boehler /** 320*d5703f5aSAndreas Boehler * Update a calendar's timezone information 321*d5703f5aSAndreas Boehler * 322*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 323*d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 324*d5703f5aSAndreas Boehler * 325*d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 326*d5703f5aSAndreas Boehler */ 327*d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 328*d5703f5aSAndreas Boehler { 329*d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 330*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $timezone); 331*d5703f5aSAndreas Boehler if($res !== false) 332*d5703f5aSAndreas Boehler { 333*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 334*d5703f5aSAndreas Boehler return true; 335*d5703f5aSAndreas Boehler } 336*d5703f5aSAndreas Boehler return false; 337*d5703f5aSAndreas Boehler } 338*d5703f5aSAndreas Boehler 339*d5703f5aSAndreas Boehler /** 340cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 341cb71a62aSAndreas Boehler * 342cb71a62aSAndreas Boehler * @param array $settings The settings array to store 343cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 344cb71a62aSAndreas Boehler * 345cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 346cb71a62aSAndreas Boehler */ 347a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 348a495d34cSAndreas Boehler { 349a495d34cSAndreas Boehler if(is_null($userid)) 35034a47953SAndreas Boehler { 35134a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 35234a47953SAndreas Boehler { 353a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 35434a47953SAndreas Boehler } 35534a47953SAndreas Boehler else 35634a47953SAndreas Boehler { 35734a47953SAndreas Boehler return false; 35834a47953SAndreas Boehler } 35934a47953SAndreas Boehler } 360a495d34cSAndreas Boehler $this->sqlite->query("BEGIN TRANSACTION"); 361a495d34cSAndreas Boehler 36251f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 36351f4febbSAndreas Boehler $this->sqlite->query($query, $userid); 364bd883736SAndreas Boehler 365a495d34cSAndreas Boehler foreach($settings as $key => $value) 366a495d34cSAndreas Boehler { 36751f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 36851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid, $key, $value); 369a495d34cSAndreas Boehler if($res === false) 370a495d34cSAndreas Boehler return false; 371a495d34cSAndreas Boehler } 372a495d34cSAndreas Boehler $this->sqlite->query("COMMIT TRANSACTION"); 373185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 374a495d34cSAndreas Boehler return true; 375a495d34cSAndreas Boehler } 376a495d34cSAndreas Boehler 377cb71a62aSAndreas Boehler /** 378cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 379cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 380cb71a62aSAndreas Boehler * 381cb71a62aSAndreas Boehler * timezone => local 382cb71a62aSAndreas Boehler * weeknumbers => 0 383cb71a62aSAndreas Boehler * workweek => 0 384cb71a62aSAndreas Boehler * 385cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 386cb71a62aSAndreas Boehler * 387cb71a62aSAndreas Boehler * @return array The settings array 388cb71a62aSAndreas Boehler */ 389a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 390a495d34cSAndreas Boehler { 391bd883736SAndreas Boehler // Some sane default settings 392bd883736SAndreas Boehler $settings = array( 393fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 394fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 395fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 3961d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 3971d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 398bd883736SAndreas Boehler ); 39934a47953SAndreas Boehler if(is_null($userid)) 40034a47953SAndreas Boehler { 40134a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 40234a47953SAndreas Boehler { 40334a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 40434a47953SAndreas Boehler } 40534a47953SAndreas Boehler else 40634a47953SAndreas Boehler { 40734a47953SAndreas Boehler return $settings; 40834a47953SAndreas Boehler } 40934a47953SAndreas Boehler } 41034a47953SAndreas Boehler 41134a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 41234a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 41351f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 41451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid); 415a495d34cSAndreas Boehler $arr = $this->sqlite->res2arr($res); 416a495d34cSAndreas Boehler foreach($arr as $row) 417a495d34cSAndreas Boehler { 418a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 419a495d34cSAndreas Boehler } 420185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 421a495d34cSAndreas Boehler return $settings; 422a495d34cSAndreas Boehler } 423a495d34cSAndreas Boehler 424cb71a62aSAndreas Boehler /** 425cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 426cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 427cb71a62aSAndreas Boehler * 428cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 429cb71a62aSAndreas Boehler * 430cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 431cb71a62aSAndreas Boehler */ 432a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 433a1a3b679SAndreas Boehler { 434a1a3b679SAndreas Boehler if(is_null($id)) 435a1a3b679SAndreas Boehler { 436a1a3b679SAndreas Boehler global $ID; 437a1a3b679SAndreas Boehler $id = $ID; 438a1a3b679SAndreas Boehler } 439a1a3b679SAndreas Boehler 440185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 441185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 442185e2535SAndreas Boehler 44351f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 44451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id); 445a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 446a1a3b679SAndreas Boehler if(isset($row['calid'])) 447185e2535SAndreas Boehler { 448185e2535SAndreas Boehler $calid = $row['calid']; 449185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 450185e2535SAndreas Boehler return $calid; 451185e2535SAndreas Boehler } 452a1a3b679SAndreas Boehler return false; 453a1a3b679SAndreas Boehler } 454a1a3b679SAndreas Boehler 455cb71a62aSAndreas Boehler /** 456cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 457cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 458cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 459cb71a62aSAndreas Boehler * 460cb71a62aSAndreas Boehler * @return array The mapping array 461cb71a62aSAndreas Boehler */ 462a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 463a1a3b679SAndreas Boehler { 464a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 465a1a3b679SAndreas Boehler $res = $this->sqlite->query($query); 466a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 467a1a3b679SAndreas Boehler return $arr; 468a1a3b679SAndreas Boehler } 469a1a3b679SAndreas Boehler 470cb71a62aSAndreas Boehler /** 471cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 472cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 473cb71a62aSAndreas Boehler * user name is actually split from the URI component. 474cb71a62aSAndreas Boehler * 475cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 476cb71a62aSAndreas Boehler * and applied accordingly. 477cb71a62aSAndreas Boehler * 478cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 479cb71a62aSAndreas Boehler * 480cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 481cb71a62aSAndreas Boehler */ 482a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 483a1a3b679SAndreas Boehler { 48434a47953SAndreas Boehler global $auth; 485a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 486a1a3b679SAndreas Boehler $user = end($user); 487a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 488a1a3b679SAndreas Boehler $calids = array(); 48934a47953SAndreas Boehler $ud = $auth->getUserData($user); 49034a47953SAndreas Boehler $groups = $ud['grps']; 491a1a3b679SAndreas Boehler foreach($mapping as $row) 492a1a3b679SAndreas Boehler { 493a1a3b679SAndreas Boehler $id = $row['calid']; 494a1a3b679SAndreas Boehler $page = $row['page']; 49534a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 496a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 497a1a3b679SAndreas Boehler { 498a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 499a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 500a1a3b679SAndreas Boehler } 501a1a3b679SAndreas Boehler } 502a1a3b679SAndreas Boehler return $calids; 503a1a3b679SAndreas Boehler } 504a1a3b679SAndreas Boehler 505cb71a62aSAndreas Boehler /** 506cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 507cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 508cb71a62aSAndreas Boehler * 509cb71a62aSAndreas Boehler * @param string $name The calendar's name 510cb71a62aSAndreas Boehler * @param string $description The calendar's description 511cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 512cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 513cb71a62aSAndreas Boehler * 514cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 515cb71a62aSAndreas Boehler */ 516a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 517a1a3b679SAndreas Boehler { 518a1a3b679SAndreas Boehler if(is_null($id)) 519a1a3b679SAndreas Boehler { 520a1a3b679SAndreas Boehler global $ID; 521a1a3b679SAndreas Boehler $id = $ID; 522a1a3b679SAndreas Boehler } 523a1a3b679SAndreas Boehler if(is_null($userid)) 52434a47953SAndreas Boehler { 52534a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 52634a47953SAndreas Boehler { 527a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 52834a47953SAndreas Boehler } 52934a47953SAndreas Boehler else 53034a47953SAndreas Boehler { 53134a47953SAndreas Boehler $userid = uniqid('davcal-'); 53234a47953SAndreas Boehler } 53334a47953SAndreas Boehler } 534a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 535a1a3b679SAndreas Boehler $name, 536a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 537a1a3b679SAndreas Boehler $description, 538a1a3b679SAndreas Boehler 'VEVENT,VTODO', 53955a741c0SAndreas Boehler 0, 54055a741c0SAndreas Boehler 1); 54151f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 54251f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 54351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 54455a741c0SAndreas Boehler if($res === false) 54555a741c0SAndreas Boehler return false; 546cb71a62aSAndreas Boehler 547cb71a62aSAndreas Boehler // Get the new calendar ID 54851f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 54951f4febbSAndreas Boehler "uri = ? AND description = ?"; 55051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 551a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 552cb71a62aSAndreas Boehler 553cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 554a1a3b679SAndreas Boehler if(isset($row['id'])) 555a1a3b679SAndreas Boehler { 55651f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 55751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id, $row['id']); 55855a741c0SAndreas Boehler return ($res !== false); 559a1a3b679SAndreas Boehler } 560a1a3b679SAndreas Boehler 561a1a3b679SAndreas Boehler return false; 562a1a3b679SAndreas Boehler } 563a1a3b679SAndreas Boehler 564cb71a62aSAndreas Boehler /** 565*d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 566*d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 567*d5703f5aSAndreas Boehler * 568*d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 569*d5703f5aSAndreas Boehler * 570*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 571*d5703f5aSAndreas Boehler * @param string $uri The new object URI 572*d5703f5aSAndreas Boehler * @param string $ics The ICS file 573*d5703f5aSAndreas Boehler * 574*d5703f5aSAndreas Boehler * @return mixed The etag. 575*d5703f5aSAndreas Boehler */ 576*d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 577*d5703f5aSAndreas Boehler { 578*d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 579*d5703f5aSAndreas Boehler 580*d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 581*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 582*d5703f5aSAndreas Boehler $calid, 583*d5703f5aSAndreas Boehler $uri, 584*d5703f5aSAndreas Boehler $ics, 585*d5703f5aSAndreas Boehler time(), 586*d5703f5aSAndreas Boehler $extraData['etag'], 587*d5703f5aSAndreas Boehler $extraData['size'], 588*d5703f5aSAndreas Boehler $extraData['componentType'], 589*d5703f5aSAndreas Boehler $extraData['firstOccurence'], 590*d5703f5aSAndreas Boehler $extraData['lastOccurence'], 591*d5703f5aSAndreas Boehler $extraData['uid']); 592*d5703f5aSAndreas Boehler // If successfully, update the sync token database 593*d5703f5aSAndreas Boehler if($res !== false) 594*d5703f5aSAndreas Boehler { 595*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 596*d5703f5aSAndreas Boehler } 597*d5703f5aSAndreas Boehler return $extraData['etag']; 598*d5703f5aSAndreas Boehler } 599*d5703f5aSAndreas Boehler 600*d5703f5aSAndreas Boehler /** 601*d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 602*d5703f5aSAndreas Boehler * needed for the sync support. 603*d5703f5aSAndreas Boehler * 604*d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 605*d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 606*d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 607*d5703f5aSAndreas Boehler */ 608*d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 609*d5703f5aSAndreas Boehler { 610*d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 611*d5703f5aSAndreas Boehler 612*d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 613*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 614*d5703f5aSAndreas Boehler $ics, 615*d5703f5aSAndreas Boehler time(), 616*d5703f5aSAndreas Boehler $extraData['etag'], 617*d5703f5aSAndreas Boehler $extraData['size'], 618*d5703f5aSAndreas Boehler $extraData['componentType'], 619*d5703f5aSAndreas Boehler $extraData['firstOccurence'], 620*d5703f5aSAndreas Boehler $extraData['lastOccurence'], 621*d5703f5aSAndreas Boehler $extraData['uid'], 622*d5703f5aSAndreas Boehler $calid, 623*d5703f5aSAndreas Boehler $uri 624*d5703f5aSAndreas Boehler ); 625*d5703f5aSAndreas Boehler if($res !== false) 626*d5703f5aSAndreas Boehler { 627*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 628*d5703f5aSAndreas Boehler } 629*d5703f5aSAndreas Boehler return $extraData['etag']; 630*d5703f5aSAndreas Boehler } 631*d5703f5aSAndreas Boehler 632*d5703f5aSAndreas Boehler /** 633cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 634cb71a62aSAndreas Boehler * 635cb71a62aSAndreas Boehler * The parameter array needs to contain 636cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 63782a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 638cb71a62aSAndreas Boehler * eventfrom => The event's start date 639cb71a62aSAndreas Boehler * eventfromtime => The event's start time 640cb71a62aSAndreas Boehler * eventto => The event's end date 641cb71a62aSAndreas Boehler * eventtotime => The event's end time 642cb71a62aSAndreas Boehler * eventname => The event's name 643cb71a62aSAndreas Boehler * eventdescription => The event's description 644cb71a62aSAndreas Boehler * 645cb71a62aSAndreas Boehler * @param string $id The page ID to work on 646cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 647cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 648cb71a62aSAndreas Boehler * 649cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 650cb71a62aSAndreas Boehler */ 651a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 652a1a3b679SAndreas Boehler { 65382a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 65482a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 65582a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 656a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 657bd883736SAndreas Boehler else 658bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 659cb71a62aSAndreas Boehler 660cb71a62aSAndreas Boehler // Retrieve dates from settings 661b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 662b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 663b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 664b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 665cb71a62aSAndreas Boehler 666cb71a62aSAndreas Boehler // Load SabreDAV 6679bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 668a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 669cb71a62aSAndreas Boehler 670cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 671a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 672b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 673b269830cSAndreas Boehler $event->add('UID', $uuid); 674a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 675cb71a62aSAndreas Boehler 676cb71a62aSAndreas Boehler // Add a description if requested 6770eebc909SAndreas Boehler $description = $params['eventdescription']; 6780eebc909SAndreas Boehler if($description !== '') 6790eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 680cb71a62aSAndreas Boehler 6814ecb526cSAndreas Boehler // Add attachments 6824ecb526cSAndreas Boehler $attachments = $params['attachments']; 68382a48dfbSAndreas Boehler if(!is_null($attachments)) 6844ecb526cSAndreas Boehler foreach($attachments as $attachment) 6854ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 6864ecb526cSAndreas Boehler 687cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 688b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 689b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 690b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 691b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 692cb71a62aSAndreas Boehler 693cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 694b269830cSAndreas Boehler $dtStart = new \DateTime(); 695a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 696b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 697cb71a62aSAndreas Boehler 698cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 699b269830cSAndreas Boehler if($params['allday'] != '1') 700b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 701cb71a62aSAndreas Boehler 702cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 703b269830cSAndreas Boehler $dtEnd = new \DateTime(); 704a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 705b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 706cb71a62aSAndreas Boehler 707cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 708b269830cSAndreas Boehler if($params['allday'] != '1') 709b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 710cb71a62aSAndreas Boehler 711b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 712b269830cSAndreas Boehler if($params['allday'] == '1') 713b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 714cb71a62aSAndreas Boehler 715cb71a62aSAndreas Boehler // Really add Start and End events 716b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 717b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 718cb71a62aSAndreas Boehler 719cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 720b269830cSAndreas Boehler if($params['allday'] == '1') 721b269830cSAndreas Boehler { 722b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 723b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 724b269830cSAndreas Boehler } 725cb71a62aSAndreas Boehler 726cb71a62aSAndreas Boehler // Actually add the values to the database 727a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 728a1a3b679SAndreas Boehler $uri = uniqid('dokuwiki-').'.ics'; 729a1a3b679SAndreas Boehler $now = new DateTime(); 730a1a3b679SAndreas Boehler $eventStr = $vcalendar->serialize(); 731a1a3b679SAndreas Boehler 73251f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 73351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 73451f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 73551f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 736cb71a62aSAndreas Boehler 737cb71a62aSAndreas Boehler // If successfully, update the sync token database 73855a741c0SAndreas Boehler if($res !== false) 73955a741c0SAndreas Boehler { 74055a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 741a1a3b679SAndreas Boehler return true; 742a1a3b679SAndreas Boehler } 74355a741c0SAndreas Boehler return false; 74455a741c0SAndreas Boehler } 745a1a3b679SAndreas Boehler 746cb71a62aSAndreas Boehler /** 747cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 748cb71a62aSAndreas Boehler * 749cb71a62aSAndreas Boehler * @param string $calid The calendar ID 750cb71a62aSAndreas Boehler * 751cb71a62aSAndreas Boehler * @return array The calendar settings array 752cb71a62aSAndreas Boehler */ 753b269830cSAndreas Boehler public function getCalendarSettings($calid) 754b269830cSAndreas Boehler { 755*d5703f5aSAndreas Boehler $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? "; 75651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 757b269830cSAndreas Boehler $row = $this->sqlite->res2row($res); 758b269830cSAndreas Boehler return $row; 759b269830cSAndreas Boehler } 760b269830cSAndreas Boehler 761cb71a62aSAndreas Boehler /** 762cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 763cb71a62aSAndreas Boehler * based on the timezone setting. 764cb71a62aSAndreas Boehler * 765cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 766cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 767cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 768cb71a62aSAndreas Boehler * 769cb71a62aSAndreas Boehler * @param string $id The page ID to work with 770cb71a62aSAndreas Boehler * @param string $user The user ID to work with 771cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 772cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 7734a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 774cb71a62aSAndreas Boehler * 775cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 776cb71a62aSAndreas Boehler */ 7774a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 778a1a3b679SAndreas Boehler { 77982a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 78082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 781bd883736SAndreas Boehler else 782bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 783a1a3b679SAndreas Boehler $data = array(); 784cb71a62aSAndreas Boehler 785cb71a62aSAndreas Boehler // Load SabreDAV 7869bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 787a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 7884a2bf5eeSAndreas Boehler if(is_null($color)) 789185e2535SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 790a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 791a469597cSAndreas Boehler $startTs = null; 792a469597cSAndreas Boehler $endTs = null; 793a469597cSAndreas Boehler if($startDate !== null) 794a469597cSAndreas Boehler { 795a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 796a469597cSAndreas Boehler $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp()); 797a469597cSAndreas Boehler } 798a469597cSAndreas Boehler if($endDate !== null) 799a469597cSAndreas Boehler { 800a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 801a469597cSAndreas Boehler $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp()); 802a469597cSAndreas Boehler } 803cb71a62aSAndreas Boehler 804cb71a62aSAndreas Boehler // Retrieve matching calendar objects 805a469597cSAndreas Boehler $res = $this->sqlite->query($query, $calid); 806a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 807cb71a62aSAndreas Boehler 808cb71a62aSAndreas Boehler // Parse individual calendar entries 809a1a3b679SAndreas Boehler foreach($arr as $row) 810a1a3b679SAndreas Boehler { 811a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 812a1a3b679SAndreas Boehler { 813b269830cSAndreas Boehler $entry = array(); 814a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 815ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 816cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 817ebc4eb57SAndreas Boehler if($recurrence != null) 818ebc4eb57SAndreas Boehler { 819ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 820ebc4eb57SAndreas Boehler $rEvents->rewind(); 821e9b7d302SAndreas Boehler while($rEvents->valid()) 822ebc4eb57SAndreas Boehler { 823ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 824cb71a62aSAndreas Boehler // If we are after the given time range, exit 825a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 826e9b7d302SAndreas Boehler break; 827cb71a62aSAndreas Boehler 828cb71a62aSAndreas Boehler // If we are before the given time range, continue 829a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 830ebc4eb57SAndreas Boehler { 831ebc4eb57SAndreas Boehler $rEvents->next(); 832ebc4eb57SAndreas Boehler continue; 833ebc4eb57SAndreas Boehler } 834cb71a62aSAndreas Boehler 835cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 836185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true); 837ebc4eb57SAndreas Boehler $rEvents->next(); 838ebc4eb57SAndreas Boehler } 839ebc4eb57SAndreas Boehler } 840ebc4eb57SAndreas Boehler else 841185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color); 842ebc4eb57SAndreas Boehler } 843ebc4eb57SAndreas Boehler } 844ebc4eb57SAndreas Boehler return $data; 845ebc4eb57SAndreas Boehler } 846ebc4eb57SAndreas Boehler 847cb71a62aSAndreas Boehler /** 848cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 849cb71a62aSAndreas Boehler * 850cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 851cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 852cb71a62aSAndreas Boehler * @param string $uid The entry's UID 8533c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 854cb71a62aSAndreas Boehler * 855cb71a62aSAndreas Boehler * @return array The parse calendar entry 856cb71a62aSAndreas Boehler */ 857185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 858ebc4eb57SAndreas Boehler { 859ebc4eb57SAndreas Boehler $entry = array(); 860ebc4eb57SAndreas Boehler $start = $event->DTSTART; 861cb71a62aSAndreas Boehler // Parse only if the start date/time is present 862b269830cSAndreas Boehler if($start !== null) 863b269830cSAndreas Boehler { 864b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 865b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 866bf0ad2b4SAndreas Boehler 867bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 868bf0ad2b4SAndreas Boehler // allDay is set to true 869bf0ad2b4SAndreas Boehler // This should fix T23 870b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 871bf0ad2b4SAndreas Boehler { 872b269830cSAndreas Boehler $entry['allDay'] = true; 873bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 874bf0ad2b4SAndreas Boehler } 875b269830cSAndreas Boehler else 876bf0ad2b4SAndreas Boehler { 877b269830cSAndreas Boehler $entry['allDay'] = false; 878bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 879bf0ad2b4SAndreas Boehler } 880b269830cSAndreas Boehler } 881ebc4eb57SAndreas Boehler $end = $event->DTEND; 882bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 883b269830cSAndreas Boehler if($end !== null) 884b269830cSAndreas Boehler { 885b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 886b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 887bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 888bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 889bf0ad2b4SAndreas Boehler else 890b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 891b269830cSAndreas Boehler } 892ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 8930eebc909SAndreas Boehler if($description !== null) 8940eebc909SAndreas Boehler $entry['description'] = (string)$description; 8950eebc909SAndreas Boehler else 8960eebc909SAndreas Boehler $entry['description'] = ''; 8974ecb526cSAndreas Boehler $attachments = $event->ATTACH; 8984ecb526cSAndreas Boehler if($attachments !== null) 8994ecb526cSAndreas Boehler { 9004ecb526cSAndreas Boehler $entry['attachments'] = array(); 9014ecb526cSAndreas Boehler foreach($attachments as $attachment) 9024ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 9034ecb526cSAndreas Boehler } 904ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 905ebc4eb57SAndreas Boehler $entry['id'] = $uid; 906185e2535SAndreas Boehler $entry['page'] = $page; 907185e2535SAndreas Boehler $entry['color'] = $color; 9083c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 909185e2535SAndreas Boehler 910ebc4eb57SAndreas Boehler return $entry; 911a1a3b679SAndreas Boehler } 912a1a3b679SAndreas Boehler 913cb71a62aSAndreas Boehler /** 914cb71a62aSAndreas Boehler * Retrieve an event by its UID 915cb71a62aSAndreas Boehler * 916cb71a62aSAndreas Boehler * @param string $uid The event's UID 917cb71a62aSAndreas Boehler * 918cb71a62aSAndreas Boehler * @return mixed The table row with the given event 919cb71a62aSAndreas Boehler */ 920a1a3b679SAndreas Boehler public function getEventWithUid($uid) 921a1a3b679SAndreas Boehler { 92251f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 92351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 924a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 925a1a3b679SAndreas Boehler return $row; 926a1a3b679SAndreas Boehler } 927a1a3b679SAndreas Boehler 928cb71a62aSAndreas Boehler /** 929*d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 930*d5703f5aSAndreas Boehler * calendar data! This is mainly neede for the sync support. 931*d5703f5aSAndreas Boehler * 932*d5703f5aSAndreas Boehler * @param int $calid The calendar ID 933*d5703f5aSAndreas Boehler * 934*d5703f5aSAndreas Boehler * @return mixed The result 935*d5703f5aSAndreas Boehler */ 936*d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 937*d5703f5aSAndreas Boehler { 938*d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 939*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid); 940*d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 941*d5703f5aSAndreas Boehler return $arr; 942*d5703f5aSAndreas Boehler } 943*d5703f5aSAndreas Boehler 944*d5703f5aSAndreas Boehler /** 945*d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 946*d5703f5aSAndreas Boehler * 947*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 948*d5703f5aSAndreas Boehler * @param string $uri The object's URI 949*d5703f5aSAndreas Boehler * 950*d5703f5aSAndreas Boehler * @return mixed The result 951*d5703f5aSAndreas Boehler */ 952*d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 953*d5703f5aSAndreas Boehler { 954*d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 955*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 956*d5703f5aSAndreas Boehler $row = $this->sqlite->res2row($res); 957*d5703f5aSAndreas Boehler return $row; 958*d5703f5aSAndreas Boehler } 959*d5703f5aSAndreas Boehler 960*d5703f5aSAndreas Boehler /** 961*d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 962*d5703f5aSAndreas Boehler * This is mainly neede for sync. 963*d5703f5aSAndreas Boehler * 964*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 965*d5703f5aSAndreas Boehler * @param array $uris An array of URIs 966*d5703f5aSAndreas Boehler * 967*d5703f5aSAndreas Boehler * @return mixed The result 968*d5703f5aSAndreas Boehler */ 969*d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 970*d5703f5aSAndreas Boehler { 971*d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 972*d5703f5aSAndreas Boehler // Inserting a whole bunch of question marks 973*d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 974*d5703f5aSAndreas Boehler $query .= ')'; 975*d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 976*d5703f5aSAndreas Boehler 977*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $vals); 978*d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 979*d5703f5aSAndreas Boehler return $arr; 980*d5703f5aSAndreas Boehler } 981*d5703f5aSAndreas Boehler 982*d5703f5aSAndreas Boehler /** 983cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 984cb71a62aSAndreas Boehler * 985cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 986cb71a62aSAndreas Boehler * 987cb71a62aSAndreas Boehler * @return array An array containing all calendar data 988cb71a62aSAndreas Boehler */ 989f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 990f69bb449SAndreas Boehler { 9917e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 99251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 993f69bb449SAndreas Boehler $arr = $this->sqlite->res2arr($res); 994f69bb449SAndreas Boehler return $arr; 995f69bb449SAndreas Boehler } 996f69bb449SAndreas Boehler 997cb71a62aSAndreas Boehler /** 998cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 999cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1000cb71a62aSAndreas Boehler * 1001cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1002cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1003cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1004cb71a62aSAndreas Boehler * 1005cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1006cb71a62aSAndreas Boehler */ 1007a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1008a1a3b679SAndreas Boehler { 100982a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 101082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 101182a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1012a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1013bd883736SAndreas Boehler else 1014bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1015cb71a62aSAndreas Boehler 1016cb71a62aSAndreas Boehler // Parse dates 1017b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1018b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1019b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1020b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1021cb71a62aSAndreas Boehler 1022cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 102355a741c0SAndreas Boehler $uid = $params['uid']; 102455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1025cb71a62aSAndreas Boehler 1026cb71a62aSAndreas Boehler // Load SabreDAV 10279bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1028a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1029a1a3b679SAndreas Boehler return false; 103055a741c0SAndreas Boehler $uri = $event['uri']; 103155a741c0SAndreas Boehler $calid = $event['calendarid']; 1032cb71a62aSAndreas Boehler 1033cb71a62aSAndreas Boehler // Parse the existing event 1034a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1035b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1036cb71a62aSAndreas Boehler 1037cb71a62aSAndreas Boehler // Set the new event values 1038b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1039b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 10400eebc909SAndreas Boehler $description = $params['eventdescription']; 1041cb71a62aSAndreas Boehler 1042cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 10430eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1044b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1045b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 10464ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 1047cb71a62aSAndreas Boehler 1048cb71a62aSAndreas Boehler // Add new time stamps and description 1049b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1050b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 10510eebc909SAndreas Boehler if($description !== '') 10520eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 1053cb71a62aSAndreas Boehler 10544ecb526cSAndreas Boehler // Add attachments 10554ecb526cSAndreas Boehler $attachments = $params['attachments']; 105682a48dfbSAndreas Boehler if(!is_null($attachments)) 10574ecb526cSAndreas Boehler foreach($attachments as $attachment) 10584ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 10594ecb526cSAndreas Boehler 1060cb71a62aSAndreas Boehler // Setup DTSTART 1061b269830cSAndreas Boehler $dtStart = new \DateTime(); 1062a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1063b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1064b269830cSAndreas Boehler if($params['allday'] != '1') 1065b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1066cb71a62aSAndreas Boehler 10674ecb526cSAndreas Boehler // Setup DTEND 1068b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1069a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1070b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1071b269830cSAndreas Boehler if($params['allday'] != '1') 1072b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1073cb71a62aSAndreas Boehler 1074b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1075b269830cSAndreas Boehler if($params['allday'] == '1') 1076b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1077b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1078b269830cSAndreas Boehler $vevent->remove('DTEND'); 1079b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1080b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1081cb71a62aSAndreas Boehler 1082cb71a62aSAndreas Boehler // Remove the time for allday events 1083b269830cSAndreas Boehler if($params['allday'] == '1') 1084b269830cSAndreas Boehler { 1085b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1086b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1087b269830cSAndreas Boehler } 1088a1a3b679SAndreas Boehler $now = new DateTime(); 1089a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1090cb71a62aSAndreas Boehler // Actually write to the database 109151f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 109251f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 109351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 109451f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 109555a741c0SAndreas Boehler if($res !== false) 109655a741c0SAndreas Boehler { 109755a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1098a1a3b679SAndreas Boehler return true; 1099a1a3b679SAndreas Boehler } 110055a741c0SAndreas Boehler return false; 110155a741c0SAndreas Boehler } 1102a1a3b679SAndreas Boehler 1103cb71a62aSAndreas Boehler /** 1104*d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1105*d5703f5aSAndreas Boehler * 1106*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1107*d5703f5aSAndreas Boehler * @param string $uri The object's URI 1108*d5703f5aSAndreas Boehler * 1109*d5703f5aSAndreas Boehler * @return true 1110*d5703f5aSAndreas Boehler */ 1111*d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1112*d5703f5aSAndreas Boehler { 1113*d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1114*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1115*d5703f5aSAndreas Boehler if($res !== false) 1116*d5703f5aSAndreas Boehler { 1117*d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1118*d5703f5aSAndreas Boehler } 1119*d5703f5aSAndreas Boehler return true; 1120*d5703f5aSAndreas Boehler } 1121*d5703f5aSAndreas Boehler 1122*d5703f5aSAndreas Boehler /** 1123cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1124cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1125cb71a62aSAndreas Boehler * 1126cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1127cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1128cb71a62aSAndreas Boehler * 1129cb71a62aSAndreas Boehler * @return boolean True 1130cb71a62aSAndreas Boehler */ 1131a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1132a1a3b679SAndreas Boehler { 1133a1a3b679SAndreas Boehler $uid = $params['uid']; 113455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 11352c14b82bSAndreas Boehler $calid = $event['calendarid']; 113655a741c0SAndreas Boehler $uri = $event['uri']; 113751f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 113851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 113955a741c0SAndreas Boehler if($res !== false) 114055a741c0SAndreas Boehler { 114155a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 114255a741c0SAndreas Boehler } 1143a1a3b679SAndreas Boehler return true; 1144a1a3b679SAndreas Boehler } 1145a1a3b679SAndreas Boehler 1146cb71a62aSAndreas Boehler /** 1147cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1148cb71a62aSAndreas Boehler * 1149cb71a62aSAndreas Boehler * @param string $calid The calendar id 1150cb71a62aSAndreas Boehler * 1151cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1152cb71a62aSAndreas Boehler */ 115355a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 115455a741c0SAndreas Boehler { 1155b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 115655a741c0SAndreas Boehler if(isset($row['synctoken'])) 115755a741c0SAndreas Boehler return $row['synctoken']; 115855a741c0SAndreas Boehler return false; 115955a741c0SAndreas Boehler } 116055a741c0SAndreas Boehler 1161cb71a62aSAndreas Boehler /** 1162cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1163cb71a62aSAndreas Boehler * an operation code as stored in the database 1164cb71a62aSAndreas Boehler * 1165cb71a62aSAndreas Boehler * @param string $operationName The operation name 1166cb71a62aSAndreas Boehler * 1167cb71a62aSAndreas Boehler * @return mixed The operation code or false 1168cb71a62aSAndreas Boehler */ 116955a741c0SAndreas Boehler public function operationNameToOperation($operationName) 117055a741c0SAndreas Boehler { 117155a741c0SAndreas Boehler switch($operationName) 117255a741c0SAndreas Boehler { 117355a741c0SAndreas Boehler case 'added': 117455a741c0SAndreas Boehler return 1; 117555a741c0SAndreas Boehler break; 117655a741c0SAndreas Boehler case 'modified': 117755a741c0SAndreas Boehler return 2; 117855a741c0SAndreas Boehler break; 117955a741c0SAndreas Boehler case 'deleted': 118055a741c0SAndreas Boehler return 3; 118155a741c0SAndreas Boehler break; 118255a741c0SAndreas Boehler } 118355a741c0SAndreas Boehler return false; 118455a741c0SAndreas Boehler } 118555a741c0SAndreas Boehler 1186cb71a62aSAndreas Boehler /** 1187cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1188cb71a62aSAndreas Boehler * operation that was performed. 1189cb71a62aSAndreas Boehler * 1190cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1191cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1192cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1193cb71a62aSAndreas Boehler * 1194cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1195cb71a62aSAndreas Boehler */ 119655a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 119755a741c0SAndreas Boehler { 119855a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 119955a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 120055a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 120155a741c0SAndreas Boehler return false; 120255a741c0SAndreas Boehler $values = array($uri, 120355a741c0SAndreas Boehler $currentToken, 120455a741c0SAndreas Boehler $calid, 120555a741c0SAndreas Boehler $operationCode 120655a741c0SAndreas Boehler ); 120751f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 120851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 120955a741c0SAndreas Boehler if($res === false) 121055a741c0SAndreas Boehler return false; 121155a741c0SAndreas Boehler $currentToken++; 121251f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 121351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $currentToken, $calid); 121455a741c0SAndreas Boehler return ($res !== false); 121555a741c0SAndreas Boehler } 121655a741c0SAndreas Boehler 1217cb71a62aSAndreas Boehler /** 1218cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1219cb71a62aSAndreas Boehler * 1220cb71a62aSAndreas Boehler * @param string $id The page's ID 1221cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1222cb71a62aSAndreas Boehler * 1223cb71a62aSAndreas Boehler * @return mixed The sync url or false 1224cb71a62aSAndreas Boehler */ 1225b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1226b269830cSAndreas Boehler { 122734a47953SAndreas Boehler if(is_null($userid)) 122834a47953SAndreas Boehler { 122934a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 123034a47953SAndreas Boehler { 123134a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 123234a47953SAndreas Boehler } 123334a47953SAndreas Boehler else 123434a47953SAndreas Boehler { 123534a47953SAndreas Boehler return false; 123634a47953SAndreas Boehler } 123734a47953SAndreas Boehler } 1238b269830cSAndreas Boehler 1239b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1240b269830cSAndreas Boehler if($calid === false) 1241b269830cSAndreas Boehler return false; 1242b269830cSAndreas Boehler 1243b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1244b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1245b269830cSAndreas Boehler return false; 1246b269830cSAndreas Boehler 1247b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1248b269830cSAndreas Boehler return $syncurl; 1249b269830cSAndreas Boehler } 1250b269830cSAndreas Boehler 1251cb71a62aSAndreas Boehler /** 1252cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1253cb71a62aSAndreas Boehler * 1254cb71a62aSAndreas Boehler * @param string $id the page ID 1255cb71a62aSAndreas Boehler * 1256cb71a62aSAndreas Boehler * @return mixed The private URL or false 1257cb71a62aSAndreas Boehler */ 1258f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1259f69bb449SAndreas Boehler { 1260f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1261f69bb449SAndreas Boehler if($calid === false) 1262f69bb449SAndreas Boehler return false; 1263f69bb449SAndreas Boehler 1264f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1265f69bb449SAndreas Boehler } 1266f69bb449SAndreas Boehler 1267cb71a62aSAndreas Boehler /** 1268cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1269cb71a62aSAndreas Boehler * 1270cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1271cb71a62aSAndreas Boehler * 1272cb71a62aSAndreas Boehler * @return mixed The private URL or false 1273cb71a62aSAndreas Boehler */ 1274f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1275f69bb449SAndreas Boehler { 1276185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1277185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 127851f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 127951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1280f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1281f69bb449SAndreas Boehler if(!isset($row['url'])) 1282f69bb449SAndreas Boehler { 1283f69bb449SAndreas Boehler $url = uniqid("dokuwiki-").".ics"; 128451f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 128551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url, $calid); 1286f69bb449SAndreas Boehler if($res === false) 1287f69bb449SAndreas Boehler return false; 1288f69bb449SAndreas Boehler } 1289f69bb449SAndreas Boehler else 1290f69bb449SAndreas Boehler { 1291f69bb449SAndreas Boehler $url = $row['url']; 1292f69bb449SAndreas Boehler } 1293185e2535SAndreas Boehler 1294185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1295185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1296185e2535SAndreas Boehler return $url; 1297f69bb449SAndreas Boehler } 1298f69bb449SAndreas Boehler 1299cb71a62aSAndreas Boehler /** 1300cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1301cb71a62aSAndreas Boehler * 1302cb71a62aSAndreas Boehler * @param string $url The private URL 1303cb71a62aSAndreas Boehler * 1304cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1305cb71a62aSAndreas Boehler */ 1306f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1307f69bb449SAndreas Boehler { 130851f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 130951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url); 1310f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1311f69bb449SAndreas Boehler if(!isset($row['calid'])) 1312f69bb449SAndreas Boehler return false; 1313f69bb449SAndreas Boehler return $row['calid']; 1314f69bb449SAndreas Boehler } 1315f69bb449SAndreas Boehler 1316cb71a62aSAndreas Boehler /** 1317cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1318cb71a62aSAndreas Boehler * 13197e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1320cb71a62aSAndreas Boehler * 1321cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1322cb71a62aSAndreas Boehler */ 1323f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1324f69bb449SAndreas Boehler { 1325f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1326f69bb449SAndreas Boehler if($calSettings === false) 1327f69bb449SAndreas Boehler return false; 1328f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1329f69bb449SAndreas Boehler if($events === false) 1330f69bb449SAndreas Boehler return false; 1331f69bb449SAndreas Boehler 13327e0b8590SAndreas Boehler // Load SabreDAV 13337e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 13347e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 13357e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1336f69bb449SAndreas Boehler foreach($events as $event) 1337f69bb449SAndreas Boehler { 13387e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 13397e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 13407e0b8590SAndreas Boehler $out .= $evt->serialize(); 1341f69bb449SAndreas Boehler } 13427e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1343f69bb449SAndreas Boehler return $out; 1344f69bb449SAndreas Boehler } 1345f69bb449SAndreas Boehler 13467c7c6b0bSAndreas Boehler /** 13477c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 13487c7c6b0bSAndreas Boehler * 13497c7c6b0bSAndreas Boehler * @param string $key The key to query 135021d04f73SAndreas Boehler * @return mixed The option set, null if not found 13517c7c6b0bSAndreas Boehler */ 13527c7c6b0bSAndreas Boehler public function getConfig($key) 13537c7c6b0bSAndreas Boehler { 13547c7c6b0bSAndreas Boehler return $this->getConf($key); 13557c7c6b0bSAndreas Boehler } 13567c7c6b0bSAndreas Boehler 1357*d5703f5aSAndreas Boehler /** 1358*d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1359*d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1360*d5703f5aSAndreas Boehler * 1361*d5703f5aSAndreas Boehler * Returns an array with the following keys: 1362*d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1363*d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1364*d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1365*d5703f5aSAndreas Boehler * * firstOccurence 1366*d5703f5aSAndreas Boehler * * lastOccurence 1367*d5703f5aSAndreas Boehler * * uid - value of the UID property 1368*d5703f5aSAndreas Boehler * 1369*d5703f5aSAndreas Boehler * @param string $calendarData 1370*d5703f5aSAndreas Boehler * @return array 1371*d5703f5aSAndreas Boehler */ 1372*d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1373*d5703f5aSAndreas Boehler { 1374*d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1375*d5703f5aSAndreas Boehler 1376*d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1377*d5703f5aSAndreas Boehler $componentType = null; 1378*d5703f5aSAndreas Boehler $component = null; 1379*d5703f5aSAndreas Boehler $firstOccurence = null; 1380*d5703f5aSAndreas Boehler $lastOccurence = null; 1381*d5703f5aSAndreas Boehler $uid = null; 1382*d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1383*d5703f5aSAndreas Boehler { 1384*d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1385*d5703f5aSAndreas Boehler { 1386*d5703f5aSAndreas Boehler $componentType = $component->name; 1387*d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1388*d5703f5aSAndreas Boehler break; 1389*d5703f5aSAndreas Boehler } 1390*d5703f5aSAndreas Boehler } 1391*d5703f5aSAndreas Boehler if (!$componentType) 1392*d5703f5aSAndreas Boehler { 1393*d5703f5aSAndreas Boehler return false; 1394*d5703f5aSAndreas Boehler } 1395*d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1396*d5703f5aSAndreas Boehler { 1397*d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1398*d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1399*d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1400*d5703f5aSAndreas Boehler { 1401*d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1402*d5703f5aSAndreas Boehler { 1403*d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1404*d5703f5aSAndreas Boehler } 1405*d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1406*d5703f5aSAndreas Boehler { 1407*d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1408*d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1409*d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1410*d5703f5aSAndreas Boehler } 1411*d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1412*d5703f5aSAndreas Boehler { 1413*d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1414*d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1415*d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1416*d5703f5aSAndreas Boehler } 1417*d5703f5aSAndreas Boehler else 1418*d5703f5aSAndreas Boehler { 1419*d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1420*d5703f5aSAndreas Boehler } 1421*d5703f5aSAndreas Boehler } 1422*d5703f5aSAndreas Boehler else 1423*d5703f5aSAndreas Boehler { 1424*d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1425*d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1426*d5703f5aSAndreas Boehler if ($it->isInfinite()) 1427*d5703f5aSAndreas Boehler { 1428*d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1429*d5703f5aSAndreas Boehler } 1430*d5703f5aSAndreas Boehler else 1431*d5703f5aSAndreas Boehler { 1432*d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1433*d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1434*d5703f5aSAndreas Boehler { 1435*d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1436*d5703f5aSAndreas Boehler $it->next(); 1437*d5703f5aSAndreas Boehler } 1438*d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1439*d5703f5aSAndreas Boehler } 1440*d5703f5aSAndreas Boehler } 1441*d5703f5aSAndreas Boehler } 1442*d5703f5aSAndreas Boehler 1443*d5703f5aSAndreas Boehler return array( 1444*d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1445*d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1446*d5703f5aSAndreas Boehler 'componentType' => $componentType, 1447*d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1448*d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1449*d5703f5aSAndreas Boehler 'uid' => $uid, 1450*d5703f5aSAndreas Boehler ); 1451*d5703f5aSAndreas Boehler 1452*d5703f5aSAndreas Boehler } 1453*d5703f5aSAndreas Boehler 1454*d5703f5aSAndreas Boehler /** 1455*d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1456*d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1457*d5703f5aSAndreas Boehler * 1458*d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1459*d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1460*d5703f5aSAndreas Boehler * 1461*d5703f5aSAndreas Boehler * @return mixed The result 1462*d5703f5aSAndreas Boehler */ 1463*d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1464*d5703f5aSAndreas Boehler { 1465*d5703f5aSAndreas Boehler $componentType = null; 1466*d5703f5aSAndreas Boehler $requirePostFilter = true; 1467*d5703f5aSAndreas Boehler $timeRange = null; 1468*d5703f5aSAndreas Boehler 1469*d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1470*d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1471*d5703f5aSAndreas Boehler { 1472*d5703f5aSAndreas Boehler $requirePostFilter = false; 1473*d5703f5aSAndreas Boehler } 1474*d5703f5aSAndreas Boehler 1475*d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1476*d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1477*d5703f5aSAndreas Boehler { 1478*d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1479*d5703f5aSAndreas Boehler 1480*d5703f5aSAndreas Boehler // Checking if we need post-filters 1481*d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1482*d5703f5aSAndreas Boehler { 1483*d5703f5aSAndreas Boehler $requirePostFilter = false; 1484*d5703f5aSAndreas Boehler } 1485*d5703f5aSAndreas Boehler // There was a time-range filter 1486*d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1487*d5703f5aSAndreas Boehler { 1488*d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1489*d5703f5aSAndreas Boehler 1490*d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1491*d5703f5aSAndreas Boehler // 100% accurate mysql query. 1492*d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1493*d5703f5aSAndreas Boehler { 1494*d5703f5aSAndreas Boehler $requirePostFilter = false; 1495*d5703f5aSAndreas Boehler } 1496*d5703f5aSAndreas Boehler } 1497*d5703f5aSAndreas Boehler 1498*d5703f5aSAndreas Boehler } 1499*d5703f5aSAndreas Boehler 1500*d5703f5aSAndreas Boehler if ($requirePostFilter) 1501*d5703f5aSAndreas Boehler { 1502*d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1503*d5703f5aSAndreas Boehler } 1504*d5703f5aSAndreas Boehler else 1505*d5703f5aSAndreas Boehler { 1506*d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1507*d5703f5aSAndreas Boehler } 1508*d5703f5aSAndreas Boehler 1509*d5703f5aSAndreas Boehler $values = array( 1510*d5703f5aSAndreas Boehler $calendarId 1511*d5703f5aSAndreas Boehler ); 1512*d5703f5aSAndreas Boehler 1513*d5703f5aSAndreas Boehler if ($componentType) 1514*d5703f5aSAndreas Boehler { 1515*d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1516*d5703f5aSAndreas Boehler $values[] = $componentType; 1517*d5703f5aSAndreas Boehler } 1518*d5703f5aSAndreas Boehler 1519*d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1520*d5703f5aSAndreas Boehler { 1521*d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1522*d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1523*d5703f5aSAndreas Boehler } 1524*d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1525*d5703f5aSAndreas Boehler { 1526*d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1527*d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1528*d5703f5aSAndreas Boehler } 1529*d5703f5aSAndreas Boehler 1530*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $values); 1531*d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1532*d5703f5aSAndreas Boehler 1533*d5703f5aSAndreas Boehler $result = array(); 1534*d5703f5aSAndreas Boehler foreach($arr as $row) 1535*d5703f5aSAndreas Boehler { 1536*d5703f5aSAndreas Boehler if ($requirePostFilter) 1537*d5703f5aSAndreas Boehler { 1538*d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1539*d5703f5aSAndreas Boehler { 1540*d5703f5aSAndreas Boehler continue; 1541*d5703f5aSAndreas Boehler } 1542*d5703f5aSAndreas Boehler } 1543*d5703f5aSAndreas Boehler $result[] = $row['uri']; 1544*d5703f5aSAndreas Boehler 1545*d5703f5aSAndreas Boehler } 1546*d5703f5aSAndreas Boehler 1547*d5703f5aSAndreas Boehler return $result; 1548*d5703f5aSAndreas Boehler } 1549*d5703f5aSAndreas Boehler 1550*d5703f5aSAndreas Boehler /** 1551*d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1552*d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1553*d5703f5aSAndreas Boehler * 1554*d5703f5aSAndreas Boehler * @param array $object 1555*d5703f5aSAndreas Boehler * @param array $filters 1556*d5703f5aSAndreas Boehler * @return bool 1557*d5703f5aSAndreas Boehler */ 1558*d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 1559*d5703f5aSAndreas Boehler { 1560*d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1561*d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1562*d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 1563*d5703f5aSAndreas Boehler // well. 1564*d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 1565*d5703f5aSAndreas Boehler { 1566*d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1567*d5703f5aSAndreas Boehler } 1568*d5703f5aSAndreas Boehler 1569*d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1570*d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1571*d5703f5aSAndreas Boehler 1572*d5703f5aSAndreas Boehler return $validator->validate($vObject, $filters); 1573*d5703f5aSAndreas Boehler 1574*d5703f5aSAndreas Boehler } 1575*d5703f5aSAndreas Boehler 1576*d5703f5aSAndreas Boehler /** 1577*d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1578*d5703f5aSAndreas Boehler * 1579*d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1580*d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 1581*d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 1582*d5703f5aSAndreas Boehler * @param int $limit The limit of changes 1583*d5703f5aSAndreas Boehler * 1584*d5703f5aSAndreas Boehler * @return array The result 1585*d5703f5aSAndreas Boehler */ 1586*d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1587*d5703f5aSAndreas Boehler { 1588*d5703f5aSAndreas Boehler // Current synctoken 1589*d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1590*d5703f5aSAndreas Boehler 1591*d5703f5aSAndreas Boehler if ($currentToken === false) return null; 1592*d5703f5aSAndreas Boehler 1593*d5703f5aSAndreas Boehler $result = array( 1594*d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 1595*d5703f5aSAndreas Boehler 'added' => array(), 1596*d5703f5aSAndreas Boehler 'modified' => array(), 1597*d5703f5aSAndreas Boehler 'deleted' => array(), 1598*d5703f5aSAndreas Boehler ); 1599*d5703f5aSAndreas Boehler 1600*d5703f5aSAndreas Boehler if ($syncToken) 1601*d5703f5aSAndreas Boehler { 1602*d5703f5aSAndreas Boehler 1603*d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 1604*d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 1605*d5703f5aSAndreas Boehler 1606*d5703f5aSAndreas Boehler // Fetching all changes 1607*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid); 1608*d5703f5aSAndreas Boehler if($res === false) 1609*d5703f5aSAndreas Boehler return null; 1610*d5703f5aSAndreas Boehler 1611*d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1612*d5703f5aSAndreas Boehler $changes = array(); 1613*d5703f5aSAndreas Boehler 1614*d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 1615*d5703f5aSAndreas Boehler // last change on a node is relevant. 1616*d5703f5aSAndreas Boehler foreach($arr as $row) 1617*d5703f5aSAndreas Boehler { 1618*d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 1619*d5703f5aSAndreas Boehler } 1620*d5703f5aSAndreas Boehler 1621*d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 1622*d5703f5aSAndreas Boehler { 1623*d5703f5aSAndreas Boehler switch ($operation) 1624*d5703f5aSAndreas Boehler { 1625*d5703f5aSAndreas Boehler case 1 : 1626*d5703f5aSAndreas Boehler $result['added'][] = $uri; 1627*d5703f5aSAndreas Boehler break; 1628*d5703f5aSAndreas Boehler case 2 : 1629*d5703f5aSAndreas Boehler $result['modified'][] = $uri; 1630*d5703f5aSAndreas Boehler break; 1631*d5703f5aSAndreas Boehler case 3 : 1632*d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 1633*d5703f5aSAndreas Boehler break; 1634*d5703f5aSAndreas Boehler } 1635*d5703f5aSAndreas Boehler 1636*d5703f5aSAndreas Boehler } 1637*d5703f5aSAndreas Boehler } 1638*d5703f5aSAndreas Boehler else 1639*d5703f5aSAndreas Boehler { 1640*d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 1641*d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1642*d5703f5aSAndreas Boehler $res = $this->sqlite->query($query); 1643*d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1644*d5703f5aSAndreas Boehler 1645*d5703f5aSAndreas Boehler $result['added'] = $arr; 1646*d5703f5aSAndreas Boehler } 1647*d5703f5aSAndreas Boehler return $result; 1648*d5703f5aSAndreas Boehler } 1649*d5703f5aSAndreas Boehler 1650a1a3b679SAndreas Boehler} 1651