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 { 940b805092SAndreas Boehler // WebDAV Connections are always readable 950b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 960b805092SAndreas Boehler { 970b805092SAndreas Boehler $retList[$page] = $data; 980b805092SAndreas Boehler } 990b805092SAndreas Boehler elseif(auth_quickaclcheck($page) >= AUTH_READ) 10080e1ddf7SAndreas Boehler { 10180e1ddf7SAndreas Boehler $retList[$page] = $data; 10280e1ddf7SAndreas Boehler } 10380e1ddf7SAndreas Boehler } 10480e1ddf7SAndreas Boehler return $retList; 10580e1ddf7SAndreas Boehler } 10680e1ddf7SAndreas Boehler 10780e1ddf7SAndreas Boehler /** 108185e2535SAndreas Boehler * Get all calendar pages used by a given page 109185e2535SAndreas Boehler * based on the stored metadata 110185e2535SAndreas Boehler * 111185e2535SAndreas Boehler * @param string $id optional The page id 112185e2535SAndreas Boehler * @return mixed The pages as array or false 113185e2535SAndreas Boehler */ 114185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 115185e2535SAndreas Boehler { 116185e2535SAndreas Boehler if(is_null($id)) 117185e2535SAndreas Boehler { 118185e2535SAndreas Boehler global $ID; 119185e2535SAndreas Boehler $id = $ID; 120185e2535SAndreas Boehler } 121185e2535SAndreas Boehler 122185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 1230b805092SAndreas Boehler 124185e2535SAndreas Boehler if(isset($meta['id'])) 125ed764890SAndreas Boehler { 126ed764890SAndreas Boehler // Filter the list of pages by permission 12780e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 12880e1ddf7SAndreas Boehler if(empty($pages)) 129ed764890SAndreas Boehler return false; 13080e1ddf7SAndreas Boehler return $pages; 131ed764890SAndreas Boehler } 132185e2535SAndreas Boehler return false; 133185e2535SAndreas Boehler } 134185e2535SAndreas Boehler 135185e2535SAndreas Boehler /** 136185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 137185e2535SAndreas Boehler * for an array of page ids 138185e2535SAndreas Boehler * 139185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 140185e2535SAndreas Boehler * @return array The list 141185e2535SAndreas Boehler */ 142185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 143185e2535SAndreas Boehler { 144185e2535SAndreas Boehler $data = array(); 1454a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 146185e2535SAndreas Boehler { 1470b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1480b805092SAndreas Boehler { 1490b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1500b805092SAndreas Boehler if(is_null($wdc)) 1510b805092SAndreas Boehler continue; 1520b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1530b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 1540b805092SAndreas Boehler $name = $settings['displayname']; 155809cb0faSAndreas Boehler $write = $settings['write']; 1560b805092SAndreas Boehler $calid = $connectionId; 1570b805092SAndreas Boehler } 1580b805092SAndreas Boehler else 1590b805092SAndreas Boehler { 160185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 161185e2535SAndreas Boehler if($calid !== false) 162185e2535SAndreas Boehler { 163185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 164185e2535SAndreas Boehler $name = $settings['displayname']; 1650b805092SAndreas Boehler //$color = $settings['calendarcolor']; 166ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 1670b805092SAndreas Boehler } 1680b805092SAndreas Boehler else 1690b805092SAndreas Boehler { 1700b805092SAndreas Boehler continue; 1710b805092SAndreas Boehler } 1720b805092SAndreas Boehler } 173185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 174ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 175185e2535SAndreas Boehler } 176185e2535SAndreas Boehler return $data; 177185e2535SAndreas Boehler } 178185e2535SAndreas Boehler 179185e2535SAndreas Boehler /** 180185e2535SAndreas Boehler * Get the saved calendar color for a given page. 181185e2535SAndreas Boehler * 182185e2535SAndreas Boehler * @param string $id optional The page ID 183185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 184185e2535SAndreas Boehler */ 185185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 186185e2535SAndreas Boehler { 187185e2535SAndreas Boehler if(is_null($id)) 188185e2535SAndreas Boehler { 189185e2535SAndreas Boehler global $ID; 190185e2535SAndreas Boehler $id = $ID; 191185e2535SAndreas Boehler } 192185e2535SAndreas Boehler 193185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 194185e2535SAndreas Boehler if($calid === false) 195185e2535SAndreas Boehler return false; 196185e2535SAndreas Boehler 197185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 198185e2535SAndreas Boehler } 199185e2535SAndreas Boehler 200185e2535SAndreas Boehler /** 201185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 202185e2535SAndreas Boehler * 203185e2535SAndreas Boehler * @param string $id optional The calendar ID 204185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 205185e2535SAndreas Boehler */ 206185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 207185e2535SAndreas Boehler { 208185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 209185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 210185e2535SAndreas Boehler 211185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 212185e2535SAndreas Boehler 213185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 214185e2535SAndreas Boehler return false; 215185e2535SAndreas Boehler 216185e2535SAndreas Boehler $color = $row['calendarcolor']; 217185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 218185e2535SAndreas Boehler return $color; 219185e2535SAndreas Boehler } 220185e2535SAndreas Boehler 221185e2535SAndreas Boehler /** 222e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 223e86c8dd3SAndreas Boehler * @param string $user the user name 224e86c8dd3SAndreas Boehler * @return the URL to the principal sync 225e86c8dd3SAndreas Boehler */ 226e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 227e86c8dd3SAndreas Boehler { 228e86c8dd3SAndreas Boehler if(is_null($user)) 229e86c8dd3SAndreas Boehler return false; 230e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 231e86c8dd3SAndreas Boehler return $url; 232e86c8dd3SAndreas Boehler } 233e86c8dd3SAndreas Boehler 234e86c8dd3SAndreas Boehler /** 235185e2535SAndreas Boehler * Set the calendar color for a given page. 236185e2535SAndreas Boehler * 237185e2535SAndreas Boehler * @param string $color The color definition 238185e2535SAndreas Boehler * @param string $id optional The page ID 239185e2535SAndreas Boehler * @return boolean True on success, otherwise false 240185e2535SAndreas Boehler */ 241185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 242185e2535SAndreas Boehler { 243185e2535SAndreas Boehler if(is_null($id)) 244185e2535SAndreas Boehler { 245185e2535SAndreas Boehler global $ID; 246185e2535SAndreas Boehler $id = $ID; 247185e2535SAndreas Boehler } 248185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 249185e2535SAndreas Boehler if($calid === false) 250185e2535SAndreas Boehler return false; 251185e2535SAndreas Boehler 25251f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 25351f4febbSAndreas Boehler " WHERE id = ?"; 25451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $color, $calid); 255185e2535SAndreas Boehler if($res !== false) 256185e2535SAndreas Boehler { 257185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 258185e2535SAndreas Boehler return true; 259185e2535SAndreas Boehler } 260185e2535SAndreas Boehler return false; 261185e2535SAndreas Boehler } 262185e2535SAndreas Boehler 263185e2535SAndreas Boehler /** 264cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 265cb71a62aSAndreas Boehler * page id. 266cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 267cb71a62aSAndreas Boehler * 268cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 269cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 270cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 271cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 272cb71a62aSAndreas Boehler * 273cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 274cb71a62aSAndreas Boehler */ 275a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 276a1a3b679SAndreas Boehler { 277a1a3b679SAndreas Boehler if(is_null($id)) 278a1a3b679SAndreas Boehler { 279a1a3b679SAndreas Boehler global $ID; 280a1a3b679SAndreas Boehler $id = $ID; 281a1a3b679SAndreas Boehler } 282a1a3b679SAndreas Boehler if(is_null($userid)) 28334a47953SAndreas Boehler { 28434a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 28534a47953SAndreas Boehler { 286a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 28734a47953SAndreas Boehler } 28834a47953SAndreas Boehler else 28934a47953SAndreas Boehler { 29034a47953SAndreas Boehler $userid = uniqid('davcal-'); 29134a47953SAndreas Boehler } 29234a47953SAndreas Boehler } 293a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 294a1a3b679SAndreas Boehler if($calid === false) 295a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 296a1a3b679SAndreas Boehler 29751f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 29851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $name, $description, $calid); 299b269830cSAndreas Boehler if($res !== false) 300b269830cSAndreas Boehler return true; 301b269830cSAndreas Boehler return false; 302a1a3b679SAndreas Boehler } 303a1a3b679SAndreas Boehler 304cb71a62aSAndreas Boehler /** 305*65133ef9SAndreas Boehler * Update a calendar's displayname 306*65133ef9SAndreas Boehler * 307*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 308*65133ef9SAndreas Boehler * @param string $name The new calendar name 309*65133ef9SAndreas Boehler * 310*65133ef9SAndreas Boehler * @return boolean True on success, otherwise false 311*65133ef9SAndreas Boehler */ 312*65133ef9SAndreas Boehler public function updateCalendarName($calid, $name) 313*65133ef9SAndreas Boehler { 314*65133ef9SAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 315*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid, $name); 316*65133ef9SAndreas Boehler if($res !== false) 317*65133ef9SAndreas Boehler { 318*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 319*65133ef9SAndreas Boehler return true; 320*65133ef9SAndreas Boehler } 321*65133ef9SAndreas Boehler return false; 322*65133ef9SAndreas Boehler } 323*65133ef9SAndreas Boehler 324*65133ef9SAndreas Boehler /** 325*65133ef9SAndreas Boehler * Update the calendar description 326*65133ef9SAndreas Boehler * 327*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 328*65133ef9SAndreas Boehler * @param string $description The new calendar's description 329*65133ef9SAndreas Boehler * 330*65133ef9SAndreas Boehler * @return boolean True on success, otherwise false 331*65133ef9SAndreas Boehler */ 332*65133ef9SAndreas Boehler public function updateCalendarDescription($calid, $description) 333*65133ef9SAndreas Boehler { 334*65133ef9SAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 335*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid, $description); 336*65133ef9SAndreas Boehler if($res !== false) 337*65133ef9SAndreas Boehler { 338*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 339*65133ef9SAndreas Boehler return true; 340*65133ef9SAndreas Boehler } 341*65133ef9SAndreas Boehler return false; 342*65133ef9SAndreas Boehler } 343*65133ef9SAndreas Boehler 344*65133ef9SAndreas Boehler /** 345*65133ef9SAndreas Boehler * Update a calendar's timezone information 346*65133ef9SAndreas Boehler * 347*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 348*65133ef9SAndreas Boehler * @param string $timezone The new timezone to set 349*65133ef9SAndreas Boehler * 350*65133ef9SAndreas Boehler * @return boolean True on success, otherwise false 351*65133ef9SAndreas Boehler */ 352*65133ef9SAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 353*65133ef9SAndreas Boehler { 354*65133ef9SAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 355*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid, $timezone); 356*65133ef9SAndreas Boehler if($res !== false) 357*65133ef9SAndreas Boehler { 358*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 359*65133ef9SAndreas Boehler return true; 360*65133ef9SAndreas Boehler } 361*65133ef9SAndreas Boehler return false; 362*65133ef9SAndreas Boehler } 363*65133ef9SAndreas Boehler 364*65133ef9SAndreas Boehler /** 365cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 366cb71a62aSAndreas Boehler * 367cb71a62aSAndreas Boehler * @param array $settings The settings array to store 368cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 369cb71a62aSAndreas Boehler * 370cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 371cb71a62aSAndreas Boehler */ 372a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 373a495d34cSAndreas Boehler { 374a495d34cSAndreas Boehler if(is_null($userid)) 37534a47953SAndreas Boehler { 37634a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 37734a47953SAndreas Boehler { 378a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 37934a47953SAndreas Boehler } 38034a47953SAndreas Boehler else 38134a47953SAndreas Boehler { 38234a47953SAndreas Boehler return false; 38334a47953SAndreas Boehler } 38434a47953SAndreas Boehler } 385a495d34cSAndreas Boehler $this->sqlite->query("BEGIN TRANSACTION"); 386a495d34cSAndreas Boehler 38751f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 38851f4febbSAndreas Boehler $this->sqlite->query($query, $userid); 389bd883736SAndreas Boehler 390a495d34cSAndreas Boehler foreach($settings as $key => $value) 391a495d34cSAndreas Boehler { 39251f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 39351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid, $key, $value); 394a495d34cSAndreas Boehler if($res === false) 395a495d34cSAndreas Boehler return false; 396a495d34cSAndreas Boehler } 397a495d34cSAndreas Boehler $this->sqlite->query("COMMIT TRANSACTION"); 398185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 399a495d34cSAndreas Boehler return true; 400a495d34cSAndreas Boehler } 401a495d34cSAndreas Boehler 402cb71a62aSAndreas Boehler /** 403cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 404cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 405cb71a62aSAndreas Boehler * 406cb71a62aSAndreas Boehler * timezone => local 407cb71a62aSAndreas Boehler * weeknumbers => 0 408cb71a62aSAndreas Boehler * workweek => 0 409cb71a62aSAndreas Boehler * 410cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 411cb71a62aSAndreas Boehler * 412cb71a62aSAndreas Boehler * @return array The settings array 413cb71a62aSAndreas Boehler */ 414a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 415a495d34cSAndreas Boehler { 416bd883736SAndreas Boehler // Some sane default settings 417bd883736SAndreas Boehler $settings = array( 418fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 419fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 420fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4211d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4221d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 423bd883736SAndreas Boehler ); 42434a47953SAndreas Boehler if(is_null($userid)) 42534a47953SAndreas Boehler { 42634a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 42734a47953SAndreas Boehler { 42834a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 42934a47953SAndreas Boehler } 43034a47953SAndreas Boehler else 43134a47953SAndreas Boehler { 43234a47953SAndreas Boehler return $settings; 43334a47953SAndreas Boehler } 43434a47953SAndreas Boehler } 43534a47953SAndreas Boehler 43634a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 43734a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 43851f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 43951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid); 440a495d34cSAndreas Boehler $arr = $this->sqlite->res2arr($res); 441a495d34cSAndreas Boehler foreach($arr as $row) 442a495d34cSAndreas Boehler { 443a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 444a495d34cSAndreas Boehler } 445185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 446a495d34cSAndreas Boehler return $settings; 447a495d34cSAndreas Boehler } 448a495d34cSAndreas Boehler 449cb71a62aSAndreas Boehler /** 450cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 451cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 452cb71a62aSAndreas Boehler * 453cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 454cb71a62aSAndreas Boehler * 455cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 456cb71a62aSAndreas Boehler */ 457a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 458a1a3b679SAndreas Boehler { 459a1a3b679SAndreas Boehler if(is_null($id)) 460a1a3b679SAndreas Boehler { 461a1a3b679SAndreas Boehler global $ID; 462a1a3b679SAndreas Boehler $id = $ID; 463a1a3b679SAndreas Boehler } 464a1a3b679SAndreas Boehler 465185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 466185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 467185e2535SAndreas Boehler 46851f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 46951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id); 470a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 471a1a3b679SAndreas Boehler if(isset($row['calid'])) 472185e2535SAndreas Boehler { 473185e2535SAndreas Boehler $calid = $row['calid']; 474185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 475185e2535SAndreas Boehler return $calid; 476185e2535SAndreas Boehler } 477a1a3b679SAndreas Boehler return false; 478a1a3b679SAndreas Boehler } 479a1a3b679SAndreas Boehler 480cb71a62aSAndreas Boehler /** 481cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 482cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 483cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 484cb71a62aSAndreas Boehler * 485cb71a62aSAndreas Boehler * @return array The mapping array 486cb71a62aSAndreas Boehler */ 487a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 488a1a3b679SAndreas Boehler { 489a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 490a1a3b679SAndreas Boehler $res = $this->sqlite->query($query); 491a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 492a1a3b679SAndreas Boehler return $arr; 493a1a3b679SAndreas Boehler } 494a1a3b679SAndreas Boehler 495cb71a62aSAndreas Boehler /** 496cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 497cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 498cb71a62aSAndreas Boehler * user name is actually split from the URI component. 499cb71a62aSAndreas Boehler * 500cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 501cb71a62aSAndreas Boehler * and applied accordingly. 502cb71a62aSAndreas Boehler * 503cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 504cb71a62aSAndreas Boehler * 505cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 506cb71a62aSAndreas Boehler */ 507a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 508a1a3b679SAndreas Boehler { 50934a47953SAndreas Boehler global $auth; 510a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 511a1a3b679SAndreas Boehler $user = end($user); 512a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 513a1a3b679SAndreas Boehler $calids = array(); 51434a47953SAndreas Boehler $ud = $auth->getUserData($user); 51534a47953SAndreas Boehler $groups = $ud['grps']; 516a1a3b679SAndreas Boehler foreach($mapping as $row) 517a1a3b679SAndreas Boehler { 518a1a3b679SAndreas Boehler $id = $row['calid']; 519a1a3b679SAndreas Boehler $page = $row['page']; 52034a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 521a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 522a1a3b679SAndreas Boehler { 523a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 524a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 525a1a3b679SAndreas Boehler } 526a1a3b679SAndreas Boehler } 527a1a3b679SAndreas Boehler return $calids; 528a1a3b679SAndreas Boehler } 529a1a3b679SAndreas Boehler 530cb71a62aSAndreas Boehler /** 531cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 532cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 533cb71a62aSAndreas Boehler * 534cb71a62aSAndreas Boehler * @param string $name The calendar's name 535cb71a62aSAndreas Boehler * @param string $description The calendar's description 536cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 537cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 538cb71a62aSAndreas Boehler * 539cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 540cb71a62aSAndreas Boehler */ 541a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 542a1a3b679SAndreas Boehler { 543a1a3b679SAndreas Boehler if(is_null($id)) 544a1a3b679SAndreas Boehler { 545a1a3b679SAndreas Boehler global $ID; 546a1a3b679SAndreas Boehler $id = $ID; 547a1a3b679SAndreas Boehler } 548a1a3b679SAndreas Boehler if(is_null($userid)) 54934a47953SAndreas Boehler { 55034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 55134a47953SAndreas Boehler { 552a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 55334a47953SAndreas Boehler } 55434a47953SAndreas Boehler else 55534a47953SAndreas Boehler { 55634a47953SAndreas Boehler $userid = uniqid('davcal-'); 55734a47953SAndreas Boehler } 55834a47953SAndreas Boehler } 559a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 560a1a3b679SAndreas Boehler $name, 561a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 562a1a3b679SAndreas Boehler $description, 563a1a3b679SAndreas Boehler 'VEVENT,VTODO', 56455a741c0SAndreas Boehler 0, 56555a741c0SAndreas Boehler 1); 56651f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 56751f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 56851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 56955a741c0SAndreas Boehler if($res === false) 57055a741c0SAndreas Boehler return false; 571cb71a62aSAndreas Boehler 572cb71a62aSAndreas Boehler // Get the new calendar ID 57351f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 57451f4febbSAndreas Boehler "uri = ? AND description = ?"; 57551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 576a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 577cb71a62aSAndreas Boehler 578cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 579a1a3b679SAndreas Boehler if(isset($row['id'])) 580a1a3b679SAndreas Boehler { 58151f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 58251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id, $row['id']); 58355a741c0SAndreas Boehler return ($res !== false); 584a1a3b679SAndreas Boehler } 585a1a3b679SAndreas Boehler 586a1a3b679SAndreas Boehler return false; 587a1a3b679SAndreas Boehler } 588a1a3b679SAndreas Boehler 589cb71a62aSAndreas Boehler /** 590*65133ef9SAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 591*65133ef9SAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 592*65133ef9SAndreas Boehler * 593*65133ef9SAndreas Boehler * This is mainly needed for the sync support. 594*65133ef9SAndreas Boehler * 595*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 596*65133ef9SAndreas Boehler * @param string $uri The new object URI 597*65133ef9SAndreas Boehler * @param string $ics The ICS file 598*65133ef9SAndreas Boehler * 599*65133ef9SAndreas Boehler * @return mixed The etag. 600*65133ef9SAndreas Boehler */ 601*65133ef9SAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 602*65133ef9SAndreas Boehler { 603*65133ef9SAndreas Boehler $extraData = $this->getDenormalizedData($ics); 604*65133ef9SAndreas Boehler 605*65133ef9SAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 606*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, 607*65133ef9SAndreas Boehler $calid, 608*65133ef9SAndreas Boehler $uri, 609*65133ef9SAndreas Boehler $ics, 610*65133ef9SAndreas Boehler time(), 611*65133ef9SAndreas Boehler $extraData['etag'], 612*65133ef9SAndreas Boehler $extraData['size'], 613*65133ef9SAndreas Boehler $extraData['componentType'], 614*65133ef9SAndreas Boehler $extraData['firstOccurence'], 615*65133ef9SAndreas Boehler $extraData['lastOccurence'], 616*65133ef9SAndreas Boehler $extraData['uid']); 617*65133ef9SAndreas Boehler // If successfully, update the sync token database 618*65133ef9SAndreas Boehler if($res !== false) 619*65133ef9SAndreas Boehler { 620*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 621*65133ef9SAndreas Boehler } 622*65133ef9SAndreas Boehler return $extraData['etag']; 623*65133ef9SAndreas Boehler } 624*65133ef9SAndreas Boehler 625*65133ef9SAndreas Boehler /** 626*65133ef9SAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 627*65133ef9SAndreas Boehler * needed for the sync support. 628*65133ef9SAndreas Boehler * 629*65133ef9SAndreas Boehler * @param int $calid The calendar's IS 630*65133ef9SAndreas Boehler * @param string $uri The object's URI to modify 631*65133ef9SAndreas Boehler * @param string $ics The new object's ICS file 632*65133ef9SAndreas Boehler */ 633*65133ef9SAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 634*65133ef9SAndreas Boehler { 635*65133ef9SAndreas Boehler $extraData = $this->getDenormalizedData($ics); 636*65133ef9SAndreas Boehler 637*65133ef9SAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 638*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, 639*65133ef9SAndreas Boehler $ics, 640*65133ef9SAndreas Boehler time(), 641*65133ef9SAndreas Boehler $extraData['etag'], 642*65133ef9SAndreas Boehler $extraData['size'], 643*65133ef9SAndreas Boehler $extraData['componentType'], 644*65133ef9SAndreas Boehler $extraData['firstOccurence'], 645*65133ef9SAndreas Boehler $extraData['lastOccurence'], 646*65133ef9SAndreas Boehler $extraData['uid'], 647*65133ef9SAndreas Boehler $calid, 648*65133ef9SAndreas Boehler $uri 649*65133ef9SAndreas Boehler ); 650*65133ef9SAndreas Boehler if($res !== false) 651*65133ef9SAndreas Boehler { 652*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 653*65133ef9SAndreas Boehler } 654*65133ef9SAndreas Boehler return $extraData['etag']; 655*65133ef9SAndreas Boehler } 656*65133ef9SAndreas Boehler 657*65133ef9SAndreas Boehler /** 658cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 659cb71a62aSAndreas Boehler * 660cb71a62aSAndreas Boehler * The parameter array needs to contain 661cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 66282a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 663cb71a62aSAndreas Boehler * eventfrom => The event's start date 664cb71a62aSAndreas Boehler * eventfromtime => The event's start time 665cb71a62aSAndreas Boehler * eventto => The event's end date 666cb71a62aSAndreas Boehler * eventtotime => The event's end time 667cb71a62aSAndreas Boehler * eventname => The event's name 668cb71a62aSAndreas Boehler * eventdescription => The event's description 669cb71a62aSAndreas Boehler * 670cb71a62aSAndreas Boehler * @param string $id The page ID to work on 671cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 672cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 673cb71a62aSAndreas Boehler * 674cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 675cb71a62aSAndreas Boehler */ 676a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 677a1a3b679SAndreas Boehler { 67882a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 67982a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 68082a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 681a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 682bd883736SAndreas Boehler else 683bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 684cb71a62aSAndreas Boehler 685cb71a62aSAndreas Boehler // Retrieve dates from settings 686b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 687b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 688b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 689b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 690cb71a62aSAndreas Boehler 691cb71a62aSAndreas Boehler // Load SabreDAV 6929bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 693a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 694cb71a62aSAndreas Boehler 695cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 696a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 697b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 698b269830cSAndreas Boehler $event->add('UID', $uuid); 699a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 700cb71a62aSAndreas Boehler 701cb71a62aSAndreas Boehler // Add a description if requested 7020eebc909SAndreas Boehler $description = $params['eventdescription']; 7030eebc909SAndreas Boehler if($description !== '') 7040eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 705cb71a62aSAndreas Boehler 7064ecb526cSAndreas Boehler // Add attachments 7074ecb526cSAndreas Boehler $attachments = $params['attachments']; 70882a48dfbSAndreas Boehler if(!is_null($attachments)) 7094ecb526cSAndreas Boehler foreach($attachments as $attachment) 7104ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 7114ecb526cSAndreas Boehler 712cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 713b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 714b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 715b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 716b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 717cb71a62aSAndreas Boehler 718cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 719b269830cSAndreas Boehler $dtStart = new \DateTime(); 720a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 721b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 722cb71a62aSAndreas Boehler 723cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 724b269830cSAndreas Boehler if($params['allday'] != '1') 725b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 726cb71a62aSAndreas Boehler 727cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 728b269830cSAndreas Boehler $dtEnd = new \DateTime(); 729a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 730b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 731cb71a62aSAndreas Boehler 732cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 733b269830cSAndreas Boehler if($params['allday'] != '1') 734b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 735cb71a62aSAndreas Boehler 736b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 737b269830cSAndreas Boehler if($params['allday'] == '1') 738b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 739cb71a62aSAndreas Boehler 740cb71a62aSAndreas Boehler // Really add Start and End events 741b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 742b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 743cb71a62aSAndreas Boehler 744cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 745b269830cSAndreas Boehler if($params['allday'] == '1') 746b269830cSAndreas Boehler { 747b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 748b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 749b269830cSAndreas Boehler } 750cb71a62aSAndreas Boehler 751809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 752809cb0faSAndreas Boehler 753809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 754809cb0faSAndreas Boehler { 755809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 756809cb0faSAndreas Boehler if(is_null($wdc)) 757809cb0faSAndreas Boehler return false; 758809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 759809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 760809cb0faSAndreas Boehler } 761809cb0faSAndreas Boehler else 762809cb0faSAndreas Boehler { 763cb71a62aSAndreas Boehler // Actually add the values to the database 764a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 765a1a3b679SAndreas Boehler $uri = uniqid('dokuwiki-').'.ics'; 766a1a3b679SAndreas Boehler $now = new DateTime(); 767a1a3b679SAndreas Boehler 76851f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 76951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 77051f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 77151f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 772cb71a62aSAndreas Boehler 773cb71a62aSAndreas Boehler // If successfully, update the sync token database 77455a741c0SAndreas Boehler if($res !== false) 77555a741c0SAndreas Boehler { 77655a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 777a1a3b679SAndreas Boehler return true; 778a1a3b679SAndreas Boehler } 779809cb0faSAndreas Boehler } 78055a741c0SAndreas Boehler return false; 78155a741c0SAndreas Boehler } 782a1a3b679SAndreas Boehler 783cb71a62aSAndreas Boehler /** 784cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 785cb71a62aSAndreas Boehler * 786cb71a62aSAndreas Boehler * @param string $calid The calendar ID 787cb71a62aSAndreas Boehler * 788cb71a62aSAndreas Boehler * @return array The calendar settings array 789cb71a62aSAndreas Boehler */ 790b269830cSAndreas Boehler public function getCalendarSettings($calid) 791b269830cSAndreas Boehler { 792*65133ef9SAndreas Boehler $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? "; 79351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 794b269830cSAndreas Boehler $row = $this->sqlite->res2row($res); 795b269830cSAndreas Boehler return $row; 796b269830cSAndreas Boehler } 797b269830cSAndreas Boehler 798cb71a62aSAndreas Boehler /** 799cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 800cb71a62aSAndreas Boehler * based on the timezone setting. 801cb71a62aSAndreas Boehler * 802cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 803cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 804cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 805cb71a62aSAndreas Boehler * 806cb71a62aSAndreas Boehler * @param string $id The page ID to work with 807cb71a62aSAndreas Boehler * @param string $user The user ID to work with 808cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 809cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 8104a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 811cb71a62aSAndreas Boehler * 812cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 813cb71a62aSAndreas Boehler */ 8144a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 815a1a3b679SAndreas Boehler { 81682a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 81782a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 818bd883736SAndreas Boehler else 819bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 820a1a3b679SAndreas Boehler $data = array(); 821cb71a62aSAndreas Boehler 822a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 823a469597cSAndreas Boehler $startTs = null; 824a469597cSAndreas Boehler $endTs = null; 825a469597cSAndreas Boehler if($startDate !== null) 826a469597cSAndreas Boehler { 827a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 828a469597cSAndreas Boehler $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp()); 829a469597cSAndreas Boehler } 830a469597cSAndreas Boehler if($endDate !== null) 831a469597cSAndreas Boehler { 832a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 833a469597cSAndreas Boehler $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp()); 834a469597cSAndreas Boehler } 835cb71a62aSAndreas Boehler 8360b805092SAndreas Boehler // Load SabreDAV 8370b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 8380b805092SAndreas Boehler 8390b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 8400b805092SAndreas Boehler { 8410b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 8420b805092SAndreas Boehler if(is_null($wdc)) 8430b805092SAndreas Boehler return $data; 8440b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 8450b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 8460b805092SAndreas Boehler } 8470b805092SAndreas Boehler else 8480b805092SAndreas Boehler { 8490b805092SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 8500b805092SAndreas Boehler if(is_null($color)) 8510b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 8520b805092SAndreas Boehler 853cb71a62aSAndreas Boehler // Retrieve matching calendar objects 854a469597cSAndreas Boehler $res = $this->sqlite->query($query, $calid); 855a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 8560b805092SAndreas Boehler } 857cb71a62aSAndreas Boehler 858cb71a62aSAndreas Boehler // Parse individual calendar entries 859a1a3b679SAndreas Boehler foreach($arr as $row) 860a1a3b679SAndreas Boehler { 861a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 862a1a3b679SAndreas Boehler { 863b269830cSAndreas Boehler $entry = array(); 864a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 865ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 866cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 867ebc4eb57SAndreas Boehler if($recurrence != null) 868ebc4eb57SAndreas Boehler { 869ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 870ebc4eb57SAndreas Boehler $rEvents->rewind(); 871e9b7d302SAndreas Boehler while($rEvents->valid()) 872ebc4eb57SAndreas Boehler { 873ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 874cb71a62aSAndreas Boehler // If we are after the given time range, exit 875a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 876e9b7d302SAndreas Boehler break; 877cb71a62aSAndreas Boehler 878cb71a62aSAndreas Boehler // If we are before the given time range, continue 879a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 880ebc4eb57SAndreas Boehler { 881ebc4eb57SAndreas Boehler $rEvents->next(); 882ebc4eb57SAndreas Boehler continue; 883ebc4eb57SAndreas Boehler } 884cb71a62aSAndreas Boehler 885cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 886185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true); 887ebc4eb57SAndreas Boehler $rEvents->next(); 888ebc4eb57SAndreas Boehler } 889ebc4eb57SAndreas Boehler } 890ebc4eb57SAndreas Boehler else 891185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color); 892ebc4eb57SAndreas Boehler } 893ebc4eb57SAndreas Boehler } 894ebc4eb57SAndreas Boehler return $data; 895ebc4eb57SAndreas Boehler } 896ebc4eb57SAndreas Boehler 897cb71a62aSAndreas Boehler /** 898cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 899cb71a62aSAndreas Boehler * 900cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 901cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 902cb71a62aSAndreas Boehler * @param string $uid The entry's UID 9033c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 904cb71a62aSAndreas Boehler * 905cb71a62aSAndreas Boehler * @return array The parse calendar entry 906cb71a62aSAndreas Boehler */ 907185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 908ebc4eb57SAndreas Boehler { 909ebc4eb57SAndreas Boehler $entry = array(); 910ebc4eb57SAndreas Boehler $start = $event->DTSTART; 911cb71a62aSAndreas Boehler // Parse only if the start date/time is present 912b269830cSAndreas Boehler if($start !== null) 913b269830cSAndreas Boehler { 914b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 915b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 916bf0ad2b4SAndreas Boehler 917bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 918bf0ad2b4SAndreas Boehler // allDay is set to true 919bf0ad2b4SAndreas Boehler // This should fix T23 920b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 921bf0ad2b4SAndreas Boehler { 922b269830cSAndreas Boehler $entry['allDay'] = true; 923bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 924bf0ad2b4SAndreas Boehler } 925b269830cSAndreas Boehler else 926bf0ad2b4SAndreas Boehler { 927b269830cSAndreas Boehler $entry['allDay'] = false; 928bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 929bf0ad2b4SAndreas Boehler } 930b269830cSAndreas Boehler } 931ebc4eb57SAndreas Boehler $end = $event->DTEND; 932bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 933b269830cSAndreas Boehler if($end !== null) 934b269830cSAndreas Boehler { 935b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 936b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 937bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 938bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 939bf0ad2b4SAndreas Boehler else 940b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 941b269830cSAndreas Boehler } 942ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 9430eebc909SAndreas Boehler if($description !== null) 9440eebc909SAndreas Boehler $entry['description'] = (string)$description; 9450eebc909SAndreas Boehler else 9460eebc909SAndreas Boehler $entry['description'] = ''; 9474ecb526cSAndreas Boehler $attachments = $event->ATTACH; 9484ecb526cSAndreas Boehler if($attachments !== null) 9494ecb526cSAndreas Boehler { 9504ecb526cSAndreas Boehler $entry['attachments'] = array(); 9514ecb526cSAndreas Boehler foreach($attachments as $attachment) 9524ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 9534ecb526cSAndreas Boehler } 954ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 955ebc4eb57SAndreas Boehler $entry['id'] = $uid; 956185e2535SAndreas Boehler $entry['page'] = $page; 957185e2535SAndreas Boehler $entry['color'] = $color; 9583c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 959185e2535SAndreas Boehler 960ebc4eb57SAndreas Boehler return $entry; 961a1a3b679SAndreas Boehler } 962a1a3b679SAndreas Boehler 963cb71a62aSAndreas Boehler /** 964cb71a62aSAndreas Boehler * Retrieve an event by its UID 965cb71a62aSAndreas Boehler * 966cb71a62aSAndreas Boehler * @param string $uid The event's UID 967cb71a62aSAndreas Boehler * 968cb71a62aSAndreas Boehler * @return mixed The table row with the given event 969cb71a62aSAndreas Boehler */ 970a1a3b679SAndreas Boehler public function getEventWithUid($uid) 971a1a3b679SAndreas Boehler { 97251f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 97351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 974a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 975a1a3b679SAndreas Boehler return $row; 976a1a3b679SAndreas Boehler } 977a1a3b679SAndreas Boehler 978cb71a62aSAndreas Boehler /** 979*65133ef9SAndreas Boehler * Retrieve information of a calendar's object, not including the actual 980*65133ef9SAndreas Boehler * calendar data! This is mainly neede for the sync support. 981*65133ef9SAndreas Boehler * 982*65133ef9SAndreas Boehler * @param int $calid The calendar ID 983*65133ef9SAndreas Boehler * 984*65133ef9SAndreas Boehler * @return mixed The result 985*65133ef9SAndreas Boehler */ 986*65133ef9SAndreas Boehler public function getCalendarObjects($calid) 987*65133ef9SAndreas Boehler { 988*65133ef9SAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 989*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid); 990*65133ef9SAndreas Boehler $arr = $this->sqlite->res2arr($res); 991*65133ef9SAndreas Boehler return $arr; 992*65133ef9SAndreas Boehler } 993*65133ef9SAndreas Boehler 994*65133ef9SAndreas Boehler /** 995*65133ef9SAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 996*65133ef9SAndreas Boehler * 997*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 998*65133ef9SAndreas Boehler * @param string $uri The object's URI 999*65133ef9SAndreas Boehler * 1000*65133ef9SAndreas Boehler * @return mixed The result 1001*65133ef9SAndreas Boehler */ 1002*65133ef9SAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1003*65133ef9SAndreas Boehler { 1004*65133ef9SAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1005*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1006*65133ef9SAndreas Boehler $row = $this->sqlite->res2row($res); 1007*65133ef9SAndreas Boehler return $row; 1008*65133ef9SAndreas Boehler } 1009*65133ef9SAndreas Boehler 1010*65133ef9SAndreas Boehler /** 1011*65133ef9SAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1012*65133ef9SAndreas Boehler * This is mainly neede for sync. 1013*65133ef9SAndreas Boehler * 1014*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 1015*65133ef9SAndreas Boehler * @param array $uris An array of URIs 1016*65133ef9SAndreas Boehler * 1017*65133ef9SAndreas Boehler * @return mixed The result 1018*65133ef9SAndreas Boehler */ 1019*65133ef9SAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1020*65133ef9SAndreas Boehler { 1021*65133ef9SAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1022*65133ef9SAndreas Boehler // Inserting a whole bunch of question marks 1023*65133ef9SAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1024*65133ef9SAndreas Boehler $query .= ')'; 1025*65133ef9SAndreas Boehler $vals = array_merge(array($calid), $uris); 1026*65133ef9SAndreas Boehler 1027*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $vals); 1028*65133ef9SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1029*65133ef9SAndreas Boehler return $arr; 1030*65133ef9SAndreas Boehler } 1031*65133ef9SAndreas Boehler 1032*65133ef9SAndreas Boehler /** 1033cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1034cb71a62aSAndreas Boehler * 1035cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1036cb71a62aSAndreas Boehler * 1037cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1038cb71a62aSAndreas Boehler */ 1039f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1040f69bb449SAndreas Boehler { 10417e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 104251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1043f69bb449SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1044f69bb449SAndreas Boehler return $arr; 1045f69bb449SAndreas Boehler } 1046f69bb449SAndreas Boehler 1047cb71a62aSAndreas Boehler /** 1048cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1049cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1050cb71a62aSAndreas Boehler * 1051cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1052cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1053cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1054cb71a62aSAndreas Boehler * 1055cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1056cb71a62aSAndreas Boehler */ 1057a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1058a1a3b679SAndreas Boehler { 105982a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 106082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 106182a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1062a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1063bd883736SAndreas Boehler else 1064bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1065cb71a62aSAndreas Boehler 1066cb71a62aSAndreas Boehler // Parse dates 1067b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1068b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1069b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1070b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1071cb71a62aSAndreas Boehler 1072cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 107355a741c0SAndreas Boehler $uid = $params['uid']; 1074809cb0faSAndreas Boehler 1075809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1076809cb0faSAndreas Boehler { 1077809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1078809cb0faSAndreas Boehler if(is_null($wdc)) 1079809cb0faSAndreas Boehler return false; 1080809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1081809cb0faSAndreas Boehler } 1082809cb0faSAndreas Boehler else 1083809cb0faSAndreas Boehler { 108455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1085809cb0faSAndreas Boehler } 1086cb71a62aSAndreas Boehler 1087cb71a62aSAndreas Boehler // Load SabreDAV 10889bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1089a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1090a1a3b679SAndreas Boehler return false; 109155a741c0SAndreas Boehler $uri = $event['uri']; 109255a741c0SAndreas Boehler $calid = $event['calendarid']; 1093cb71a62aSAndreas Boehler 1094cb71a62aSAndreas Boehler // Parse the existing event 1095a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1096b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1097cb71a62aSAndreas Boehler 1098cb71a62aSAndreas Boehler // Set the new event values 1099b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1100b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 11010eebc909SAndreas Boehler $description = $params['eventdescription']; 1102cb71a62aSAndreas Boehler 1103cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 11040eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1105b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1106b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 11074ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 1108cb71a62aSAndreas Boehler 1109cb71a62aSAndreas Boehler // Add new time stamps and description 1110b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1111b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 11120eebc909SAndreas Boehler if($description !== '') 11130eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 1114cb71a62aSAndreas Boehler 11154ecb526cSAndreas Boehler // Add attachments 11164ecb526cSAndreas Boehler $attachments = $params['attachments']; 111782a48dfbSAndreas Boehler if(!is_null($attachments)) 11184ecb526cSAndreas Boehler foreach($attachments as $attachment) 11194ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 11204ecb526cSAndreas Boehler 1121cb71a62aSAndreas Boehler // Setup DTSTART 1122b269830cSAndreas Boehler $dtStart = new \DateTime(); 1123a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1124b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1125b269830cSAndreas Boehler if($params['allday'] != '1') 1126b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1127cb71a62aSAndreas Boehler 11284ecb526cSAndreas Boehler // Setup DTEND 1129b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1130a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1131b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1132b269830cSAndreas Boehler if($params['allday'] != '1') 1133b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1134cb71a62aSAndreas Boehler 1135b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1136b269830cSAndreas Boehler if($params['allday'] == '1') 1137b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1138b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1139b269830cSAndreas Boehler $vevent->remove('DTEND'); 1140b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1141b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1142cb71a62aSAndreas Boehler 1143cb71a62aSAndreas Boehler // Remove the time for allday events 1144b269830cSAndreas Boehler if($params['allday'] == '1') 1145b269830cSAndreas Boehler { 1146b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1147b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1148b269830cSAndreas Boehler } 1149a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1150809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1151809cb0faSAndreas Boehler { 1152809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1153809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1154809cb0faSAndreas Boehler } 1155809cb0faSAndreas Boehler else 1156809cb0faSAndreas Boehler { 1157809cb0faSAndreas Boehler $now = new DateTime(); 1158cb71a62aSAndreas Boehler // Actually write to the database 115951f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 116051f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 116151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 116251f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 116355a741c0SAndreas Boehler if($res !== false) 116455a741c0SAndreas Boehler { 116555a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1166a1a3b679SAndreas Boehler return true; 1167a1a3b679SAndreas Boehler } 1168809cb0faSAndreas Boehler } 116955a741c0SAndreas Boehler return false; 117055a741c0SAndreas Boehler } 1171a1a3b679SAndreas Boehler 1172cb71a62aSAndreas Boehler /** 1173*65133ef9SAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1174*65133ef9SAndreas Boehler * 1175*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 1176*65133ef9SAndreas Boehler * @param string $uri The object's URI 1177*65133ef9SAndreas Boehler * 1178*65133ef9SAndreas Boehler * @return true 1179*65133ef9SAndreas Boehler */ 1180*65133ef9SAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1181*65133ef9SAndreas Boehler { 1182*65133ef9SAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1183*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1184*65133ef9SAndreas Boehler if($res !== false) 1185*65133ef9SAndreas Boehler { 1186*65133ef9SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1187*65133ef9SAndreas Boehler } 1188*65133ef9SAndreas Boehler return true; 1189*65133ef9SAndreas Boehler } 1190*65133ef9SAndreas Boehler 1191*65133ef9SAndreas Boehler /** 1192cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1193cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1194cb71a62aSAndreas Boehler * 1195cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1196cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1197cb71a62aSAndreas Boehler * 1198cb71a62aSAndreas Boehler * @return boolean True 1199cb71a62aSAndreas Boehler */ 1200a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1201a1a3b679SAndreas Boehler { 1202a1a3b679SAndreas Boehler $uid = $params['uid']; 1203809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1204809cb0faSAndreas Boehler { 1205809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1206809cb0faSAndreas Boehler if(is_null($wdc)) 1207809cb0faSAndreas Boehler return false; 1208809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1209809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1210809cb0faSAndreas Boehler return $result; 1211809cb0faSAndreas Boehler } 121255a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 12132c14b82bSAndreas Boehler $calid = $event['calendarid']; 121455a741c0SAndreas Boehler $uri = $event['uri']; 121551f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 121651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 121755a741c0SAndreas Boehler if($res !== false) 121855a741c0SAndreas Boehler { 121955a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 122055a741c0SAndreas Boehler } 1221a1a3b679SAndreas Boehler return true; 1222a1a3b679SAndreas Boehler } 1223a1a3b679SAndreas Boehler 1224cb71a62aSAndreas Boehler /** 1225cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1226cb71a62aSAndreas Boehler * 1227cb71a62aSAndreas Boehler * @param string $calid The calendar id 1228cb71a62aSAndreas Boehler * 1229cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1230cb71a62aSAndreas Boehler */ 123155a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 123255a741c0SAndreas Boehler { 1233b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 123455a741c0SAndreas Boehler if(isset($row['synctoken'])) 123555a741c0SAndreas Boehler return $row['synctoken']; 123655a741c0SAndreas Boehler return false; 123755a741c0SAndreas Boehler } 123855a741c0SAndreas Boehler 1239cb71a62aSAndreas Boehler /** 1240cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1241cb71a62aSAndreas Boehler * an operation code as stored in the database 1242cb71a62aSAndreas Boehler * 1243cb71a62aSAndreas Boehler * @param string $operationName The operation name 1244cb71a62aSAndreas Boehler * 1245cb71a62aSAndreas Boehler * @return mixed The operation code or false 1246cb71a62aSAndreas Boehler */ 124755a741c0SAndreas Boehler public function operationNameToOperation($operationName) 124855a741c0SAndreas Boehler { 124955a741c0SAndreas Boehler switch($operationName) 125055a741c0SAndreas Boehler { 125155a741c0SAndreas Boehler case 'added': 125255a741c0SAndreas Boehler return 1; 125355a741c0SAndreas Boehler break; 125455a741c0SAndreas Boehler case 'modified': 125555a741c0SAndreas Boehler return 2; 125655a741c0SAndreas Boehler break; 125755a741c0SAndreas Boehler case 'deleted': 125855a741c0SAndreas Boehler return 3; 125955a741c0SAndreas Boehler break; 126055a741c0SAndreas Boehler } 126155a741c0SAndreas Boehler return false; 126255a741c0SAndreas Boehler } 126355a741c0SAndreas Boehler 1264cb71a62aSAndreas Boehler /** 1265cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1266cb71a62aSAndreas Boehler * operation that was performed. 1267cb71a62aSAndreas Boehler * 1268cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1269cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1270cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1271cb71a62aSAndreas Boehler * 1272cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1273cb71a62aSAndreas Boehler */ 127455a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 127555a741c0SAndreas Boehler { 127655a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 127755a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 127855a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 127955a741c0SAndreas Boehler return false; 128055a741c0SAndreas Boehler $values = array($uri, 128155a741c0SAndreas Boehler $currentToken, 128255a741c0SAndreas Boehler $calid, 128355a741c0SAndreas Boehler $operationCode 128455a741c0SAndreas Boehler ); 128551f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 128651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 128755a741c0SAndreas Boehler if($res === false) 128855a741c0SAndreas Boehler return false; 128955a741c0SAndreas Boehler $currentToken++; 129051f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 129151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $currentToken, $calid); 129255a741c0SAndreas Boehler return ($res !== false); 129355a741c0SAndreas Boehler } 129455a741c0SAndreas Boehler 1295cb71a62aSAndreas Boehler /** 1296cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1297cb71a62aSAndreas Boehler * 1298cb71a62aSAndreas Boehler * @param string $id The page's ID 1299cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1300cb71a62aSAndreas Boehler * 1301cb71a62aSAndreas Boehler * @return mixed The sync url or false 1302cb71a62aSAndreas Boehler */ 1303b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1304b269830cSAndreas Boehler { 130534a47953SAndreas Boehler if(is_null($userid)) 130634a47953SAndreas Boehler { 130734a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 130834a47953SAndreas Boehler { 130934a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 131034a47953SAndreas Boehler } 131134a47953SAndreas Boehler else 131234a47953SAndreas Boehler { 131334a47953SAndreas Boehler return false; 131434a47953SAndreas Boehler } 131534a47953SAndreas Boehler } 1316b269830cSAndreas Boehler 1317b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1318b269830cSAndreas Boehler if($calid === false) 1319b269830cSAndreas Boehler return false; 1320b269830cSAndreas Boehler 1321b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1322b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1323b269830cSAndreas Boehler return false; 1324b269830cSAndreas Boehler 1325b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1326b269830cSAndreas Boehler return $syncurl; 1327b269830cSAndreas Boehler } 1328b269830cSAndreas Boehler 1329cb71a62aSAndreas Boehler /** 1330cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1331cb71a62aSAndreas Boehler * 1332cb71a62aSAndreas Boehler * @param string $id the page ID 1333cb71a62aSAndreas Boehler * 1334cb71a62aSAndreas Boehler * @return mixed The private URL or false 1335cb71a62aSAndreas Boehler */ 1336f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1337f69bb449SAndreas Boehler { 1338f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1339f69bb449SAndreas Boehler if($calid === false) 1340f69bb449SAndreas Boehler return false; 1341f69bb449SAndreas Boehler 1342f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1343f69bb449SAndreas Boehler } 1344f69bb449SAndreas Boehler 1345cb71a62aSAndreas Boehler /** 1346cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1347cb71a62aSAndreas Boehler * 1348cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1349cb71a62aSAndreas Boehler * 1350cb71a62aSAndreas Boehler * @return mixed The private URL or false 1351cb71a62aSAndreas Boehler */ 1352f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1353f69bb449SAndreas Boehler { 1354185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1355185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 135651f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 135751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1358f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1359f69bb449SAndreas Boehler if(!isset($row['url'])) 1360f69bb449SAndreas Boehler { 1361f69bb449SAndreas Boehler $url = uniqid("dokuwiki-").".ics"; 136251f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 136351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url, $calid); 1364f69bb449SAndreas Boehler if($res === false) 1365f69bb449SAndreas Boehler return false; 1366f69bb449SAndreas Boehler } 1367f69bb449SAndreas Boehler else 1368f69bb449SAndreas Boehler { 1369f69bb449SAndreas Boehler $url = $row['url']; 1370f69bb449SAndreas Boehler } 1371185e2535SAndreas Boehler 1372185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1373185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1374185e2535SAndreas Boehler return $url; 1375f69bb449SAndreas Boehler } 1376f69bb449SAndreas Boehler 1377cb71a62aSAndreas Boehler /** 1378cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1379cb71a62aSAndreas Boehler * 1380cb71a62aSAndreas Boehler * @param string $url The private URL 1381cb71a62aSAndreas Boehler * 1382cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1383cb71a62aSAndreas Boehler */ 1384f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1385f69bb449SAndreas Boehler { 138651f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 138751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url); 1388f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1389f69bb449SAndreas Boehler if(!isset($row['calid'])) 1390f69bb449SAndreas Boehler return false; 1391f69bb449SAndreas Boehler return $row['calid']; 1392f69bb449SAndreas Boehler } 1393f69bb449SAndreas Boehler 1394cb71a62aSAndreas Boehler /** 1395cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1396cb71a62aSAndreas Boehler * 13977e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1398cb71a62aSAndreas Boehler * 1399cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1400cb71a62aSAndreas Boehler */ 1401f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1402f69bb449SAndreas Boehler { 1403f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1404f69bb449SAndreas Boehler if($calSettings === false) 1405f69bb449SAndreas Boehler return false; 1406f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1407f69bb449SAndreas Boehler if($events === false) 1408f69bb449SAndreas Boehler return false; 1409f69bb449SAndreas Boehler 14107e0b8590SAndreas Boehler // Load SabreDAV 14117e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 14127e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 14137e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1414f69bb449SAndreas Boehler foreach($events as $event) 1415f69bb449SAndreas Boehler { 14167e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 14177e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 14187e0b8590SAndreas Boehler $out .= $evt->serialize(); 1419f69bb449SAndreas Boehler } 14207e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1421f69bb449SAndreas Boehler return $out; 1422f69bb449SAndreas Boehler } 1423f69bb449SAndreas Boehler 14247c7c6b0bSAndreas Boehler /** 14257c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 14267c7c6b0bSAndreas Boehler * 14277c7c6b0bSAndreas Boehler * @param string $key The key to query 142821d04f73SAndreas Boehler * @return mixed The option set, null if not found 14297c7c6b0bSAndreas Boehler */ 14307c7c6b0bSAndreas Boehler public function getConfig($key) 14317c7c6b0bSAndreas Boehler { 14327c7c6b0bSAndreas Boehler return $this->getConf($key); 14337c7c6b0bSAndreas Boehler } 14347c7c6b0bSAndreas Boehler 1435*65133ef9SAndreas Boehler /** 1436*65133ef9SAndreas Boehler * Parses some information from calendar objects, used for optimized 1437*65133ef9SAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1438*65133ef9SAndreas Boehler * 1439*65133ef9SAndreas Boehler * Returns an array with the following keys: 1440*65133ef9SAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1441*65133ef9SAndreas Boehler * * size - Size of the object in bytes 1442*65133ef9SAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1443*65133ef9SAndreas Boehler * * firstOccurence 1444*65133ef9SAndreas Boehler * * lastOccurence 1445*65133ef9SAndreas Boehler * * uid - value of the UID property 1446*65133ef9SAndreas Boehler * 1447*65133ef9SAndreas Boehler * @param string $calendarData 1448*65133ef9SAndreas Boehler * @return array 1449*65133ef9SAndreas Boehler */ 1450*65133ef9SAndreas Boehler protected function getDenormalizedData($calendarData) 1451*65133ef9SAndreas Boehler { 1452*65133ef9SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1453*65133ef9SAndreas Boehler 1454*65133ef9SAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1455*65133ef9SAndreas Boehler $componentType = null; 1456*65133ef9SAndreas Boehler $component = null; 1457*65133ef9SAndreas Boehler $firstOccurence = null; 1458*65133ef9SAndreas Boehler $lastOccurence = null; 1459*65133ef9SAndreas Boehler $uid = null; 1460*65133ef9SAndreas Boehler foreach ($vObject->getComponents() as $component) 1461*65133ef9SAndreas Boehler { 1462*65133ef9SAndreas Boehler if ($component->name !== 'VTIMEZONE') 1463*65133ef9SAndreas Boehler { 1464*65133ef9SAndreas Boehler $componentType = $component->name; 1465*65133ef9SAndreas Boehler $uid = (string)$component->UID; 1466*65133ef9SAndreas Boehler break; 1467*65133ef9SAndreas Boehler } 1468*65133ef9SAndreas Boehler } 1469*65133ef9SAndreas Boehler if (!$componentType) 1470*65133ef9SAndreas Boehler { 1471*65133ef9SAndreas Boehler return false; 1472*65133ef9SAndreas Boehler } 1473*65133ef9SAndreas Boehler if ($componentType === 'VEVENT') 1474*65133ef9SAndreas Boehler { 1475*65133ef9SAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1476*65133ef9SAndreas Boehler // Finding the last occurence is a bit harder 1477*65133ef9SAndreas Boehler if (!isset($component->RRULE)) 1478*65133ef9SAndreas Boehler { 1479*65133ef9SAndreas Boehler if (isset($component->DTEND)) 1480*65133ef9SAndreas Boehler { 1481*65133ef9SAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1482*65133ef9SAndreas Boehler } 1483*65133ef9SAndreas Boehler elseif (isset($component->DURATION)) 1484*65133ef9SAndreas Boehler { 1485*65133ef9SAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1486*65133ef9SAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1487*65133ef9SAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1488*65133ef9SAndreas Boehler } 1489*65133ef9SAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1490*65133ef9SAndreas Boehler { 1491*65133ef9SAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1492*65133ef9SAndreas Boehler $endDate->modify('+1 day'); 1493*65133ef9SAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1494*65133ef9SAndreas Boehler } 1495*65133ef9SAndreas Boehler else 1496*65133ef9SAndreas Boehler { 1497*65133ef9SAndreas Boehler $lastOccurence = $firstOccurence; 1498*65133ef9SAndreas Boehler } 1499*65133ef9SAndreas Boehler } 1500*65133ef9SAndreas Boehler else 1501*65133ef9SAndreas Boehler { 1502*65133ef9SAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1503*65133ef9SAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1504*65133ef9SAndreas Boehler if ($it->isInfinite()) 1505*65133ef9SAndreas Boehler { 1506*65133ef9SAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1507*65133ef9SAndreas Boehler } 1508*65133ef9SAndreas Boehler else 1509*65133ef9SAndreas Boehler { 1510*65133ef9SAndreas Boehler $end = $it->getDtEnd(); 1511*65133ef9SAndreas Boehler while ($it->valid() && $end < $maxDate) 1512*65133ef9SAndreas Boehler { 1513*65133ef9SAndreas Boehler $end = $it->getDtEnd(); 1514*65133ef9SAndreas Boehler $it->next(); 1515*65133ef9SAndreas Boehler } 1516*65133ef9SAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1517*65133ef9SAndreas Boehler } 1518*65133ef9SAndreas Boehler } 1519*65133ef9SAndreas Boehler } 1520*65133ef9SAndreas Boehler 1521*65133ef9SAndreas Boehler return array( 1522*65133ef9SAndreas Boehler 'etag' => md5($calendarData), 1523*65133ef9SAndreas Boehler 'size' => strlen($calendarData), 1524*65133ef9SAndreas Boehler 'componentType' => $componentType, 1525*65133ef9SAndreas Boehler 'firstOccurence' => $firstOccurence, 1526*65133ef9SAndreas Boehler 'lastOccurence' => $lastOccurence, 1527*65133ef9SAndreas Boehler 'uid' => $uid, 1528*65133ef9SAndreas Boehler ); 1529*65133ef9SAndreas Boehler 1530*65133ef9SAndreas Boehler } 1531*65133ef9SAndreas Boehler 1532*65133ef9SAndreas Boehler /** 1533*65133ef9SAndreas Boehler * Query a calendar by ID and taking several filters into account. 1534*65133ef9SAndreas Boehler * This is heavily based on Sabre's PDO backend. 1535*65133ef9SAndreas Boehler * 1536*65133ef9SAndreas Boehler * @param int $calendarId The calendar's ID 1537*65133ef9SAndreas Boehler * @param array $filters The filter array to apply 1538*65133ef9SAndreas Boehler * 1539*65133ef9SAndreas Boehler * @return mixed The result 1540*65133ef9SAndreas Boehler */ 1541*65133ef9SAndreas Boehler public function calendarQuery($calendarId, $filters) 1542*65133ef9SAndreas Boehler { 1543*65133ef9SAndreas Boehler $componentType = null; 1544*65133ef9SAndreas Boehler $requirePostFilter = true; 1545*65133ef9SAndreas Boehler $timeRange = null; 1546*65133ef9SAndreas Boehler 1547*65133ef9SAndreas Boehler // if no filters were specified, we don't need to filter after a query 1548*65133ef9SAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1549*65133ef9SAndreas Boehler { 1550*65133ef9SAndreas Boehler $requirePostFilter = false; 1551*65133ef9SAndreas Boehler } 1552*65133ef9SAndreas Boehler 1553*65133ef9SAndreas Boehler // Figuring out if there's a component filter 1554*65133ef9SAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1555*65133ef9SAndreas Boehler { 1556*65133ef9SAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1557*65133ef9SAndreas Boehler 1558*65133ef9SAndreas Boehler // Checking if we need post-filters 1559*65133ef9SAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1560*65133ef9SAndreas Boehler { 1561*65133ef9SAndreas Boehler $requirePostFilter = false; 1562*65133ef9SAndreas Boehler } 1563*65133ef9SAndreas Boehler // There was a time-range filter 1564*65133ef9SAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1565*65133ef9SAndreas Boehler { 1566*65133ef9SAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1567*65133ef9SAndreas Boehler 1568*65133ef9SAndreas Boehler // If start time OR the end time is not specified, we can do a 1569*65133ef9SAndreas Boehler // 100% accurate mysql query. 1570*65133ef9SAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1571*65133ef9SAndreas Boehler { 1572*65133ef9SAndreas Boehler $requirePostFilter = false; 1573*65133ef9SAndreas Boehler } 1574*65133ef9SAndreas Boehler } 1575*65133ef9SAndreas Boehler 1576*65133ef9SAndreas Boehler } 1577*65133ef9SAndreas Boehler 1578*65133ef9SAndreas Boehler if ($requirePostFilter) 1579*65133ef9SAndreas Boehler { 1580*65133ef9SAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1581*65133ef9SAndreas Boehler } 1582*65133ef9SAndreas Boehler else 1583*65133ef9SAndreas Boehler { 1584*65133ef9SAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1585*65133ef9SAndreas Boehler } 1586*65133ef9SAndreas Boehler 1587*65133ef9SAndreas Boehler $values = array( 1588*65133ef9SAndreas Boehler $calendarId 1589*65133ef9SAndreas Boehler ); 1590*65133ef9SAndreas Boehler 1591*65133ef9SAndreas Boehler if ($componentType) 1592*65133ef9SAndreas Boehler { 1593*65133ef9SAndreas Boehler $query .= " AND componenttype = ?"; 1594*65133ef9SAndreas Boehler $values[] = $componentType; 1595*65133ef9SAndreas Boehler } 1596*65133ef9SAndreas Boehler 1597*65133ef9SAndreas Boehler if ($timeRange && $timeRange['start']) 1598*65133ef9SAndreas Boehler { 1599*65133ef9SAndreas Boehler $query .= " AND lastoccurence > ?"; 1600*65133ef9SAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1601*65133ef9SAndreas Boehler } 1602*65133ef9SAndreas Boehler if ($timeRange && $timeRange['end']) 1603*65133ef9SAndreas Boehler { 1604*65133ef9SAndreas Boehler $query .= " AND firstoccurence < ?"; 1605*65133ef9SAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1606*65133ef9SAndreas Boehler } 1607*65133ef9SAndreas Boehler 1608*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $values); 1609*65133ef9SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1610*65133ef9SAndreas Boehler 1611*65133ef9SAndreas Boehler $result = array(); 1612*65133ef9SAndreas Boehler foreach($arr as $row) 1613*65133ef9SAndreas Boehler { 1614*65133ef9SAndreas Boehler if ($requirePostFilter) 1615*65133ef9SAndreas Boehler { 1616*65133ef9SAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1617*65133ef9SAndreas Boehler { 1618*65133ef9SAndreas Boehler continue; 1619*65133ef9SAndreas Boehler } 1620*65133ef9SAndreas Boehler } 1621*65133ef9SAndreas Boehler $result[] = $row['uri']; 1622*65133ef9SAndreas Boehler 1623*65133ef9SAndreas Boehler } 1624*65133ef9SAndreas Boehler 1625*65133ef9SAndreas Boehler return $result; 1626*65133ef9SAndreas Boehler } 1627*65133ef9SAndreas Boehler 1628*65133ef9SAndreas Boehler /** 1629*65133ef9SAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1630*65133ef9SAndreas Boehler * the given object. Taken from Sabre's PDO backend 1631*65133ef9SAndreas Boehler * 1632*65133ef9SAndreas Boehler * @param array $object 1633*65133ef9SAndreas Boehler * @param array $filters 1634*65133ef9SAndreas Boehler * @return bool 1635*65133ef9SAndreas Boehler */ 1636*65133ef9SAndreas Boehler protected function validateFilterForObject($object, $filters) 1637*65133ef9SAndreas Boehler { 1638*65133ef9SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1639*65133ef9SAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1640*65133ef9SAndreas Boehler // it was excluded, we actually need another call to get this as 1641*65133ef9SAndreas Boehler // well. 1642*65133ef9SAndreas Boehler if (!isset($object['calendardata'])) 1643*65133ef9SAndreas Boehler { 1644*65133ef9SAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1645*65133ef9SAndreas Boehler } 1646*65133ef9SAndreas Boehler 1647*65133ef9SAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1648*65133ef9SAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1649*65133ef9SAndreas Boehler 1650*65133ef9SAndreas Boehler return $validator->validate($vObject, $filters); 1651*65133ef9SAndreas Boehler 1652*65133ef9SAndreas Boehler } 1653*65133ef9SAndreas Boehler 1654*65133ef9SAndreas Boehler /** 1655*65133ef9SAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1656*65133ef9SAndreas Boehler * 1657*65133ef9SAndreas Boehler * @param int $calid The calendar's ID 1658*65133ef9SAndreas Boehler * @param int $syncToken The supplied sync token 1659*65133ef9SAndreas Boehler * @param int $syncLevel The sync level 1660*65133ef9SAndreas Boehler * @param int $limit The limit of changes 1661*65133ef9SAndreas Boehler * 1662*65133ef9SAndreas Boehler * @return array The result 1663*65133ef9SAndreas Boehler */ 1664*65133ef9SAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1665*65133ef9SAndreas Boehler { 1666*65133ef9SAndreas Boehler // Current synctoken 1667*65133ef9SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1668*65133ef9SAndreas Boehler 1669*65133ef9SAndreas Boehler if ($currentToken === false) return null; 1670*65133ef9SAndreas Boehler 1671*65133ef9SAndreas Boehler $result = array( 1672*65133ef9SAndreas Boehler 'syncToken' => $currentToken, 1673*65133ef9SAndreas Boehler 'added' => array(), 1674*65133ef9SAndreas Boehler 'modified' => array(), 1675*65133ef9SAndreas Boehler 'deleted' => array(), 1676*65133ef9SAndreas Boehler ); 1677*65133ef9SAndreas Boehler 1678*65133ef9SAndreas Boehler if ($syncToken) 1679*65133ef9SAndreas Boehler { 1680*65133ef9SAndreas Boehler 1681*65133ef9SAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 1682*65133ef9SAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 1683*65133ef9SAndreas Boehler 1684*65133ef9SAndreas Boehler // Fetching all changes 1685*65133ef9SAndreas Boehler $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid); 1686*65133ef9SAndreas Boehler if($res === false) 1687*65133ef9SAndreas Boehler return null; 1688*65133ef9SAndreas Boehler 1689*65133ef9SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1690*65133ef9SAndreas Boehler $changes = array(); 1691*65133ef9SAndreas Boehler 1692*65133ef9SAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 1693*65133ef9SAndreas Boehler // last change on a node is relevant. 1694*65133ef9SAndreas Boehler foreach($arr as $row) 1695*65133ef9SAndreas Boehler { 1696*65133ef9SAndreas Boehler $changes[$row['uri']] = $row['operation']; 1697*65133ef9SAndreas Boehler } 1698*65133ef9SAndreas Boehler 1699*65133ef9SAndreas Boehler foreach ($changes as $uri => $operation) 1700*65133ef9SAndreas Boehler { 1701*65133ef9SAndreas Boehler switch ($operation) 1702*65133ef9SAndreas Boehler { 1703*65133ef9SAndreas Boehler case 1 : 1704*65133ef9SAndreas Boehler $result['added'][] = $uri; 1705*65133ef9SAndreas Boehler break; 1706*65133ef9SAndreas Boehler case 2 : 1707*65133ef9SAndreas Boehler $result['modified'][] = $uri; 1708*65133ef9SAndreas Boehler break; 1709*65133ef9SAndreas Boehler case 3 : 1710*65133ef9SAndreas Boehler $result['deleted'][] = $uri; 1711*65133ef9SAndreas Boehler break; 1712*65133ef9SAndreas Boehler } 1713*65133ef9SAndreas Boehler 1714*65133ef9SAndreas Boehler } 1715*65133ef9SAndreas Boehler } 1716*65133ef9SAndreas Boehler else 1717*65133ef9SAndreas Boehler { 1718*65133ef9SAndreas Boehler // No synctoken supplied, this is the initial sync. 1719*65133ef9SAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1720*65133ef9SAndreas Boehler $res = $this->sqlite->query($query); 1721*65133ef9SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1722*65133ef9SAndreas Boehler 1723*65133ef9SAndreas Boehler $result['added'] = $arr; 1724*65133ef9SAndreas Boehler } 1725*65133ef9SAndreas Boehler return $result; 1726*65133ef9SAndreas Boehler } 1727*65133ef9SAndreas Boehler 1728a1a3b679SAndreas Boehler} 1729