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 /** 83185e2535SAndreas Boehler * Get all calendar pages used by a given page 84185e2535SAndreas Boehler * based on the stored metadata 85185e2535SAndreas Boehler * 86185e2535SAndreas Boehler * @param string $id optional The page id 87185e2535SAndreas Boehler * @return mixed The pages as array or false 88185e2535SAndreas Boehler */ 89185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 90185e2535SAndreas Boehler { 91185e2535SAndreas Boehler if(is_null($id)) 92185e2535SAndreas Boehler { 93185e2535SAndreas Boehler global $ID; 94185e2535SAndreas Boehler $id = $ID; 95185e2535SAndreas Boehler } 96185e2535SAndreas Boehler 97185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 98185e2535SAndreas Boehler if(isset($meta['id'])) 99185e2535SAndreas Boehler return array_keys($meta['id']); 100185e2535SAndreas Boehler return false; 101185e2535SAndreas Boehler } 102185e2535SAndreas Boehler 103185e2535SAndreas Boehler /** 104185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 105185e2535SAndreas Boehler * for an array of page ids 106185e2535SAndreas Boehler * 107185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 108185e2535SAndreas Boehler * @return array The list 109185e2535SAndreas Boehler */ 110185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 111185e2535SAndreas Boehler { 112185e2535SAndreas Boehler $data = array(); 113185e2535SAndreas Boehler foreach($calendarPages as $page) 114185e2535SAndreas Boehler { 115185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 116185e2535SAndreas Boehler if($calid !== false) 117185e2535SAndreas Boehler { 118185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 119185e2535SAndreas Boehler $name = $settings['displayname']; 120185e2535SAndreas Boehler $color = $settings['calendarcolor']; 121185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 122185e2535SAndreas Boehler 'color' => $color); 123185e2535SAndreas Boehler } 124185e2535SAndreas Boehler } 125185e2535SAndreas Boehler return $data; 126185e2535SAndreas Boehler } 127185e2535SAndreas Boehler 128185e2535SAndreas Boehler /** 129185e2535SAndreas Boehler * Get the saved calendar color for a given page. 130185e2535SAndreas Boehler * 131185e2535SAndreas Boehler * @param string $id optional The page ID 132185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 133185e2535SAndreas Boehler */ 134185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 135185e2535SAndreas Boehler { 136185e2535SAndreas Boehler if(is_null($id)) 137185e2535SAndreas Boehler { 138185e2535SAndreas Boehler global $ID; 139185e2535SAndreas Boehler $id = $ID; 140185e2535SAndreas Boehler } 141185e2535SAndreas Boehler 142185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 143185e2535SAndreas Boehler if($calid === false) 144185e2535SAndreas Boehler return false; 145185e2535SAndreas Boehler 146185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 147185e2535SAndreas Boehler } 148185e2535SAndreas Boehler 149185e2535SAndreas Boehler /** 150185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 151185e2535SAndreas Boehler * 152185e2535SAndreas Boehler * @param string $id optional The calendar ID 153185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 154185e2535SAndreas Boehler */ 155185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 156185e2535SAndreas Boehler { 157185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 158185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 159185e2535SAndreas Boehler 160185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 161185e2535SAndreas Boehler 162185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 163185e2535SAndreas Boehler return false; 164185e2535SAndreas Boehler 165185e2535SAndreas Boehler $color = $row['calendarcolor']; 166185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 167185e2535SAndreas Boehler return $color; 168185e2535SAndreas Boehler } 169185e2535SAndreas Boehler 170185e2535SAndreas Boehler /** 171e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 172e86c8dd3SAndreas Boehler * @param string $user the user name 173e86c8dd3SAndreas Boehler * @return the URL to the principal sync 174e86c8dd3SAndreas Boehler */ 175e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 176e86c8dd3SAndreas Boehler { 177e86c8dd3SAndreas Boehler if(is_null($user)) 178e86c8dd3SAndreas Boehler return false; 179e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 180e86c8dd3SAndreas Boehler return $url; 181e86c8dd3SAndreas Boehler } 182e86c8dd3SAndreas Boehler 183e86c8dd3SAndreas Boehler /** 184185e2535SAndreas Boehler * Set the calendar color for a given page. 185185e2535SAndreas Boehler * 186185e2535SAndreas Boehler * @param string $color The color definition 187185e2535SAndreas Boehler * @param string $id optional The page ID 188185e2535SAndreas Boehler * @return boolean True on success, otherwise false 189185e2535SAndreas Boehler */ 190185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 191185e2535SAndreas Boehler { 192185e2535SAndreas Boehler if(is_null($id)) 193185e2535SAndreas Boehler { 194185e2535SAndreas Boehler global $ID; 195185e2535SAndreas Boehler $id = $ID; 196185e2535SAndreas Boehler } 197185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 198185e2535SAndreas Boehler if($calid === false) 199185e2535SAndreas Boehler return false; 200185e2535SAndreas Boehler 20151f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 20251f4febbSAndreas Boehler " WHERE id = ?"; 20351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $color, $calid); 204185e2535SAndreas Boehler if($res !== false) 205185e2535SAndreas Boehler { 206185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 207185e2535SAndreas Boehler return true; 208185e2535SAndreas Boehler } 209185e2535SAndreas Boehler return false; 210185e2535SAndreas Boehler } 211185e2535SAndreas Boehler 212185e2535SAndreas Boehler /** 213cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 214cb71a62aSAndreas Boehler * page id. 215cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 216cb71a62aSAndreas Boehler * 217cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 218cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 219cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 220cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 221cb71a62aSAndreas Boehler * 222cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 223cb71a62aSAndreas Boehler */ 224a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 225a1a3b679SAndreas Boehler { 226a1a3b679SAndreas Boehler if(is_null($id)) 227a1a3b679SAndreas Boehler { 228a1a3b679SAndreas Boehler global $ID; 229a1a3b679SAndreas Boehler $id = $ID; 230a1a3b679SAndreas Boehler } 231a1a3b679SAndreas Boehler if(is_null($userid)) 23234a47953SAndreas Boehler { 23334a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 23434a47953SAndreas Boehler { 235a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 23634a47953SAndreas Boehler } 23734a47953SAndreas Boehler else 23834a47953SAndreas Boehler { 23934a47953SAndreas Boehler $userid = uniqid('davcal-'); 24034a47953SAndreas Boehler } 24134a47953SAndreas Boehler } 242a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 243a1a3b679SAndreas Boehler if($calid === false) 244a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 245a1a3b679SAndreas Boehler 24651f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 24751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $name, $description, $calid); 248b269830cSAndreas Boehler if($res !== false) 249b269830cSAndreas Boehler return true; 250b269830cSAndreas Boehler return false; 251a1a3b679SAndreas Boehler } 252a1a3b679SAndreas Boehler 253cb71a62aSAndreas Boehler /** 254cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 255cb71a62aSAndreas Boehler * 256cb71a62aSAndreas Boehler * @param array $settings The settings array to store 257cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 258cb71a62aSAndreas Boehler * 259cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 260cb71a62aSAndreas Boehler */ 261a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 262a495d34cSAndreas Boehler { 263a495d34cSAndreas Boehler if(is_null($userid)) 26434a47953SAndreas Boehler { 26534a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 26634a47953SAndreas Boehler { 267a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 26834a47953SAndreas Boehler } 26934a47953SAndreas Boehler else 27034a47953SAndreas Boehler { 27134a47953SAndreas Boehler return false; 27234a47953SAndreas Boehler } 27334a47953SAndreas Boehler } 274a495d34cSAndreas Boehler $this->sqlite->query("BEGIN TRANSACTION"); 275a495d34cSAndreas Boehler 27651f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 27751f4febbSAndreas Boehler $this->sqlite->query($query, $userid); 278bd883736SAndreas Boehler 279a495d34cSAndreas Boehler foreach($settings as $key => $value) 280a495d34cSAndreas Boehler { 28151f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 28251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid, $key, $value); 283a495d34cSAndreas Boehler if($res === false) 284a495d34cSAndreas Boehler return false; 285a495d34cSAndreas Boehler } 286a495d34cSAndreas Boehler $this->sqlite->query("COMMIT TRANSACTION"); 287185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 288a495d34cSAndreas Boehler return true; 289a495d34cSAndreas Boehler } 290a495d34cSAndreas Boehler 291cb71a62aSAndreas Boehler /** 292cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 293cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 294cb71a62aSAndreas Boehler * 295cb71a62aSAndreas Boehler * timezone => local 296cb71a62aSAndreas Boehler * weeknumbers => 0 297cb71a62aSAndreas Boehler * workweek => 0 298cb71a62aSAndreas Boehler * 299cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 300cb71a62aSAndreas Boehler * 301cb71a62aSAndreas Boehler * @return array The settings array 302cb71a62aSAndreas Boehler */ 303a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 304a495d34cSAndreas Boehler { 305bd883736SAndreas Boehler // Some sane default settings 306bd883736SAndreas Boehler $settings = array( 307fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 308fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 309fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 3101d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 3111d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 312bd883736SAndreas Boehler ); 31334a47953SAndreas Boehler if(is_null($userid)) 31434a47953SAndreas Boehler { 31534a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 31634a47953SAndreas Boehler { 31734a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 31834a47953SAndreas Boehler } 31934a47953SAndreas Boehler else 32034a47953SAndreas Boehler { 32134a47953SAndreas Boehler return $settings; 32234a47953SAndreas Boehler } 32334a47953SAndreas Boehler } 32434a47953SAndreas Boehler 32534a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 32634a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 32751f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 32851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid); 329a495d34cSAndreas Boehler $arr = $this->sqlite->res2arr($res); 330a495d34cSAndreas Boehler foreach($arr as $row) 331a495d34cSAndreas Boehler { 332a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 333a495d34cSAndreas Boehler } 334185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 335a495d34cSAndreas Boehler return $settings; 336a495d34cSAndreas Boehler } 337a495d34cSAndreas Boehler 338cb71a62aSAndreas Boehler /** 339cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 340cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 341cb71a62aSAndreas Boehler * 342cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 343cb71a62aSAndreas Boehler * 344cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 345cb71a62aSAndreas Boehler */ 346a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 347a1a3b679SAndreas Boehler { 348a1a3b679SAndreas Boehler if(is_null($id)) 349a1a3b679SAndreas Boehler { 350a1a3b679SAndreas Boehler global $ID; 351a1a3b679SAndreas Boehler $id = $ID; 352a1a3b679SAndreas Boehler } 353a1a3b679SAndreas Boehler 354185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 355185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 356185e2535SAndreas Boehler 35751f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 35851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id); 359a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 360a1a3b679SAndreas Boehler if(isset($row['calid'])) 361185e2535SAndreas Boehler { 362185e2535SAndreas Boehler $calid = $row['calid']; 363185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 364185e2535SAndreas Boehler return $calid; 365185e2535SAndreas Boehler } 366a1a3b679SAndreas Boehler return false; 367a1a3b679SAndreas Boehler } 368a1a3b679SAndreas Boehler 369cb71a62aSAndreas Boehler /** 370cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 371cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 372cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 373cb71a62aSAndreas Boehler * 374cb71a62aSAndreas Boehler * @return array The mapping array 375cb71a62aSAndreas Boehler */ 376a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 377a1a3b679SAndreas Boehler { 378a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 379a1a3b679SAndreas Boehler $res = $this->sqlite->query($query); 380a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 381a1a3b679SAndreas Boehler return $arr; 382a1a3b679SAndreas Boehler } 383a1a3b679SAndreas Boehler 384cb71a62aSAndreas Boehler /** 385cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 386cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 387cb71a62aSAndreas Boehler * user name is actually split from the URI component. 388cb71a62aSAndreas Boehler * 389cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 390cb71a62aSAndreas Boehler * and applied accordingly. 391cb71a62aSAndreas Boehler * 392cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 393cb71a62aSAndreas Boehler * 394cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 395cb71a62aSAndreas Boehler */ 396a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 397a1a3b679SAndreas Boehler { 39834a47953SAndreas Boehler global $auth; 399a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 400a1a3b679SAndreas Boehler $user = end($user); 401a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 402a1a3b679SAndreas Boehler $calids = array(); 40334a47953SAndreas Boehler $ud = $auth->getUserData($user); 40434a47953SAndreas Boehler $groups = $ud['grps']; 405a1a3b679SAndreas Boehler foreach($mapping as $row) 406a1a3b679SAndreas Boehler { 407a1a3b679SAndreas Boehler $id = $row['calid']; 408a1a3b679SAndreas Boehler $page = $row['page']; 40934a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 410a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 411a1a3b679SAndreas Boehler { 412a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 413a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 414a1a3b679SAndreas Boehler } 415a1a3b679SAndreas Boehler } 416a1a3b679SAndreas Boehler return $calids; 417a1a3b679SAndreas Boehler } 418a1a3b679SAndreas Boehler 419cb71a62aSAndreas Boehler /** 420cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 421cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 422cb71a62aSAndreas Boehler * 423cb71a62aSAndreas Boehler * @param string $name The calendar's name 424cb71a62aSAndreas Boehler * @param string $description The calendar's description 425cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 426cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 427cb71a62aSAndreas Boehler * 428cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 429cb71a62aSAndreas Boehler */ 430a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 431a1a3b679SAndreas Boehler { 432a1a3b679SAndreas Boehler if(is_null($id)) 433a1a3b679SAndreas Boehler { 434a1a3b679SAndreas Boehler global $ID; 435a1a3b679SAndreas Boehler $id = $ID; 436a1a3b679SAndreas Boehler } 437a1a3b679SAndreas Boehler if(is_null($userid)) 43834a47953SAndreas Boehler { 43934a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 44034a47953SAndreas Boehler { 441a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 44234a47953SAndreas Boehler } 44334a47953SAndreas Boehler else 44434a47953SAndreas Boehler { 44534a47953SAndreas Boehler $userid = uniqid('davcal-'); 44634a47953SAndreas Boehler } 44734a47953SAndreas Boehler } 448a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 449a1a3b679SAndreas Boehler $name, 450a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 451a1a3b679SAndreas Boehler $description, 452a1a3b679SAndreas Boehler 'VEVENT,VTODO', 45355a741c0SAndreas Boehler 0, 45455a741c0SAndreas Boehler 1); 45551f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 45651f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 45751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 45855a741c0SAndreas Boehler if($res === false) 45955a741c0SAndreas Boehler return false; 460cb71a62aSAndreas Boehler 461cb71a62aSAndreas Boehler // Get the new calendar ID 46251f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 46351f4febbSAndreas Boehler "uri = ? AND description = ?"; 46451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 465a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 466cb71a62aSAndreas Boehler 467cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 468a1a3b679SAndreas Boehler if(isset($row['id'])) 469a1a3b679SAndreas Boehler { 47051f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 47151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id, $row['id']); 47255a741c0SAndreas Boehler return ($res !== false); 473a1a3b679SAndreas Boehler } 474a1a3b679SAndreas Boehler 475a1a3b679SAndreas Boehler return false; 476a1a3b679SAndreas Boehler } 477a1a3b679SAndreas Boehler 478cb71a62aSAndreas Boehler /** 479cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 480cb71a62aSAndreas Boehler * 481cb71a62aSAndreas Boehler * The parameter array needs to contain 482cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 48382a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 484cb71a62aSAndreas Boehler * eventfrom => The event's start date 485cb71a62aSAndreas Boehler * eventfromtime => The event's start time 486cb71a62aSAndreas Boehler * eventto => The event's end date 487cb71a62aSAndreas Boehler * eventtotime => The event's end time 488cb71a62aSAndreas Boehler * eventname => The event's name 489cb71a62aSAndreas Boehler * eventdescription => The event's description 490cb71a62aSAndreas Boehler * 491cb71a62aSAndreas Boehler * @param string $id The page ID to work on 492cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 493cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 494cb71a62aSAndreas Boehler * 495cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 496cb71a62aSAndreas Boehler */ 497a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 498a1a3b679SAndreas Boehler { 49982a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 50082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 50182a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 502a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 503bd883736SAndreas Boehler else 504bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 505cb71a62aSAndreas Boehler 506cb71a62aSAndreas Boehler // Retrieve dates from settings 507b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 508b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 509b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 510b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 511cb71a62aSAndreas Boehler 512cb71a62aSAndreas Boehler // Load SabreDAV 5139bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 514a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 515cb71a62aSAndreas Boehler 516cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 517a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 518b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 519b269830cSAndreas Boehler $event->add('UID', $uuid); 520a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 521cb71a62aSAndreas Boehler 522cb71a62aSAndreas Boehler // Add a description if requested 5230eebc909SAndreas Boehler $description = $params['eventdescription']; 5240eebc909SAndreas Boehler if($description !== '') 5250eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 526cb71a62aSAndreas Boehler 5274ecb526cSAndreas Boehler // Add attachments 5284ecb526cSAndreas Boehler $attachments = $params['attachments']; 52982a48dfbSAndreas Boehler if(!is_null($attachments)) 5304ecb526cSAndreas Boehler foreach($attachments as $attachment) 5314ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 5324ecb526cSAndreas Boehler 533cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 534b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 535b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 536b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 537b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 538cb71a62aSAndreas Boehler 539cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 540b269830cSAndreas Boehler $dtStart = new \DateTime(); 541a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 542b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 543cb71a62aSAndreas Boehler 544cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 545b269830cSAndreas Boehler if($params['allday'] != '1') 546b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 547cb71a62aSAndreas Boehler 548cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 549b269830cSAndreas Boehler $dtEnd = new \DateTime(); 550a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 551b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 552cb71a62aSAndreas Boehler 553cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 554b269830cSAndreas Boehler if($params['allday'] != '1') 555b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 556cb71a62aSAndreas Boehler 557b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 558b269830cSAndreas Boehler if($params['allday'] == '1') 559b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 560cb71a62aSAndreas Boehler 561cb71a62aSAndreas Boehler // Really add Start and End events 562b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 563b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 564cb71a62aSAndreas Boehler 565cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 566b269830cSAndreas Boehler if($params['allday'] == '1') 567b269830cSAndreas Boehler { 568b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 569b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 570b269830cSAndreas Boehler } 571cb71a62aSAndreas Boehler 572cb71a62aSAndreas Boehler // Actually add the values to the database 573a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 574a1a3b679SAndreas Boehler $uri = uniqid('dokuwiki-').'.ics'; 575a1a3b679SAndreas Boehler $now = new DateTime(); 576a1a3b679SAndreas Boehler $eventStr = $vcalendar->serialize(); 577a1a3b679SAndreas Boehler 57851f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 57951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 58051f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 58151f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 582cb71a62aSAndreas Boehler 583cb71a62aSAndreas Boehler // If successfully, update the sync token database 58455a741c0SAndreas Boehler if($res !== false) 58555a741c0SAndreas Boehler { 58655a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 587a1a3b679SAndreas Boehler return true; 588a1a3b679SAndreas Boehler } 58955a741c0SAndreas Boehler return false; 59055a741c0SAndreas Boehler } 591a1a3b679SAndreas Boehler 592cb71a62aSAndreas Boehler /** 593cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 594cb71a62aSAndreas Boehler * 595cb71a62aSAndreas Boehler * @param string $calid The calendar ID 596cb71a62aSAndreas Boehler * 597cb71a62aSAndreas Boehler * @return array The calendar settings array 598cb71a62aSAndreas Boehler */ 599b269830cSAndreas Boehler public function getCalendarSettings($calid) 600b269830cSAndreas Boehler { 60151f4febbSAndreas Boehler $query = "SELECT principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? "; 60251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 603b269830cSAndreas Boehler $row = $this->sqlite->res2row($res); 604b269830cSAndreas Boehler return $row; 605b269830cSAndreas Boehler } 606b269830cSAndreas Boehler 607cb71a62aSAndreas Boehler /** 608cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 609cb71a62aSAndreas Boehler * based on the timezone setting. 610cb71a62aSAndreas Boehler * 611cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 612cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 613cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 614cb71a62aSAndreas Boehler * 615cb71a62aSAndreas Boehler * @param string $id The page ID to work with 616cb71a62aSAndreas Boehler * @param string $user The user ID to work with 617cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 618cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 619cb71a62aSAndreas Boehler * 620cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 621cb71a62aSAndreas Boehler */ 62282a48dfbSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone) 623a1a3b679SAndreas Boehler { 62482a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 62582a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 626bd883736SAndreas Boehler else 627bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 628a1a3b679SAndreas Boehler $data = array(); 629cb71a62aSAndreas Boehler 630cb71a62aSAndreas Boehler // Load SabreDAV 6319bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 632a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 633185e2535SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 634a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 635a469597cSAndreas Boehler $startTs = null; 636a469597cSAndreas Boehler $endTs = null; 637a469597cSAndreas Boehler if($startDate !== null) 638a469597cSAndreas Boehler { 639a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 640a469597cSAndreas Boehler $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp()); 641a469597cSAndreas Boehler } 642a469597cSAndreas Boehler if($endDate !== null) 643a469597cSAndreas Boehler { 644a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 645a469597cSAndreas Boehler $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp()); 646a469597cSAndreas Boehler } 647cb71a62aSAndreas Boehler 648cb71a62aSAndreas Boehler // Retrieve matching calendar objects 649a469597cSAndreas Boehler $res = $this->sqlite->query($query, $calid); 650a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 651cb71a62aSAndreas Boehler 652cb71a62aSAndreas Boehler // Parse individual calendar entries 653a1a3b679SAndreas Boehler foreach($arr as $row) 654a1a3b679SAndreas Boehler { 655a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 656a1a3b679SAndreas Boehler { 657b269830cSAndreas Boehler $entry = array(); 658a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 659ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 660cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 661ebc4eb57SAndreas Boehler if($recurrence != null) 662ebc4eb57SAndreas Boehler { 663ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 664ebc4eb57SAndreas Boehler $rEvents->rewind(); 665e9b7d302SAndreas Boehler while($rEvents->valid()) 666ebc4eb57SAndreas Boehler { 667ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 668cb71a62aSAndreas Boehler // If we are after the given time range, exit 669a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 670e9b7d302SAndreas Boehler break; 671cb71a62aSAndreas Boehler 672cb71a62aSAndreas Boehler // If we are before the given time range, continue 673a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 674ebc4eb57SAndreas Boehler { 675ebc4eb57SAndreas Boehler $rEvents->next(); 676ebc4eb57SAndreas Boehler continue; 677ebc4eb57SAndreas Boehler } 678cb71a62aSAndreas Boehler 679cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 680185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true); 681ebc4eb57SAndreas Boehler $rEvents->next(); 682ebc4eb57SAndreas Boehler } 683ebc4eb57SAndreas Boehler } 684ebc4eb57SAndreas Boehler else 685185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color); 686ebc4eb57SAndreas Boehler } 687ebc4eb57SAndreas Boehler } 688ebc4eb57SAndreas Boehler return $data; 689ebc4eb57SAndreas Boehler } 690ebc4eb57SAndreas Boehler 691cb71a62aSAndreas Boehler /** 692cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 693cb71a62aSAndreas Boehler * 694cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 695cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 696cb71a62aSAndreas Boehler * @param string $uid The entry's UID 6973c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 698cb71a62aSAndreas Boehler * 699cb71a62aSAndreas Boehler * @return array The parse calendar entry 700cb71a62aSAndreas Boehler */ 701185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 702ebc4eb57SAndreas Boehler { 703ebc4eb57SAndreas Boehler $entry = array(); 704ebc4eb57SAndreas Boehler $start = $event->DTSTART; 705cb71a62aSAndreas Boehler // Parse only if the start date/time is present 706b269830cSAndreas Boehler if($start !== null) 707b269830cSAndreas Boehler { 708b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 709b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 710bf0ad2b4SAndreas Boehler 711bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 712bf0ad2b4SAndreas Boehler // allDay is set to true 713bf0ad2b4SAndreas Boehler // This should fix T23 714b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 715bf0ad2b4SAndreas Boehler { 716b269830cSAndreas Boehler $entry['allDay'] = true; 717bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 718bf0ad2b4SAndreas Boehler } 719b269830cSAndreas Boehler else 720bf0ad2b4SAndreas Boehler { 721b269830cSAndreas Boehler $entry['allDay'] = false; 722bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 723bf0ad2b4SAndreas Boehler } 724b269830cSAndreas Boehler } 725ebc4eb57SAndreas Boehler $end = $event->DTEND; 726bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 727b269830cSAndreas Boehler if($end !== null) 728b269830cSAndreas Boehler { 729b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 730b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 731bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 732bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 733bf0ad2b4SAndreas Boehler else 734b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 735b269830cSAndreas Boehler } 736ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 7370eebc909SAndreas Boehler if($description !== null) 7380eebc909SAndreas Boehler $entry['description'] = (string)$description; 7390eebc909SAndreas Boehler else 7400eebc909SAndreas Boehler $entry['description'] = ''; 7414ecb526cSAndreas Boehler $attachments = $event->ATTACH; 7424ecb526cSAndreas Boehler if($attachments !== null) 7434ecb526cSAndreas Boehler { 7444ecb526cSAndreas Boehler $entry['attachments'] = array(); 7454ecb526cSAndreas Boehler foreach($attachments as $attachment) 7464ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 7474ecb526cSAndreas Boehler } 748ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 749ebc4eb57SAndreas Boehler $entry['id'] = $uid; 750185e2535SAndreas Boehler $entry['page'] = $page; 751185e2535SAndreas Boehler $entry['color'] = $color; 7523c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 753185e2535SAndreas Boehler 754ebc4eb57SAndreas Boehler return $entry; 755a1a3b679SAndreas Boehler } 756a1a3b679SAndreas Boehler 757cb71a62aSAndreas Boehler /** 758cb71a62aSAndreas Boehler * Retrieve an event by its UID 759cb71a62aSAndreas Boehler * 760cb71a62aSAndreas Boehler * @param string $uid The event's UID 761cb71a62aSAndreas Boehler * 762cb71a62aSAndreas Boehler * @return mixed The table row with the given event 763cb71a62aSAndreas Boehler */ 764a1a3b679SAndreas Boehler public function getEventWithUid($uid) 765a1a3b679SAndreas Boehler { 76651f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 76751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 768a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 769a1a3b679SAndreas Boehler return $row; 770a1a3b679SAndreas Boehler } 771a1a3b679SAndreas Boehler 772cb71a62aSAndreas Boehler /** 773cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 774cb71a62aSAndreas Boehler * 775cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 776cb71a62aSAndreas Boehler * 777cb71a62aSAndreas Boehler * @return array An array containing all calendar data 778cb71a62aSAndreas Boehler */ 779f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 780f69bb449SAndreas Boehler { 781*7e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 78251f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 783f69bb449SAndreas Boehler $arr = $this->sqlite->res2arr($res); 784f69bb449SAndreas Boehler return $arr; 785f69bb449SAndreas Boehler } 786f69bb449SAndreas Boehler 787cb71a62aSAndreas Boehler /** 788cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 789cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 790cb71a62aSAndreas Boehler * 791cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 792cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 793cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 794cb71a62aSAndreas Boehler * 795cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 796cb71a62aSAndreas Boehler */ 797a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 798a1a3b679SAndreas Boehler { 79982a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 80082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 80182a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 802a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 803bd883736SAndreas Boehler else 804bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 805cb71a62aSAndreas Boehler 806cb71a62aSAndreas Boehler // Parse dates 807b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 808b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 809b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 810b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 811cb71a62aSAndreas Boehler 812cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 81355a741c0SAndreas Boehler $uid = $params['uid']; 81455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 815cb71a62aSAndreas Boehler 816cb71a62aSAndreas Boehler // Load SabreDAV 8179bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 818a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 819a1a3b679SAndreas Boehler return false; 82055a741c0SAndreas Boehler $uri = $event['uri']; 82155a741c0SAndreas Boehler $calid = $event['calendarid']; 822cb71a62aSAndreas Boehler 823cb71a62aSAndreas Boehler // Parse the existing event 824a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 825b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 826cb71a62aSAndreas Boehler 827cb71a62aSAndreas Boehler // Set the new event values 828b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 829b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 8300eebc909SAndreas Boehler $description = $params['eventdescription']; 831cb71a62aSAndreas Boehler 832cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 8330eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 834b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 835b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 8364ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 837cb71a62aSAndreas Boehler 838cb71a62aSAndreas Boehler // Add new time stamps and description 839b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 840b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 8410eebc909SAndreas Boehler if($description !== '') 8420eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 843cb71a62aSAndreas Boehler 8444ecb526cSAndreas Boehler // Add attachments 8454ecb526cSAndreas Boehler $attachments = $params['attachments']; 84682a48dfbSAndreas Boehler if(!is_null($attachments)) 8474ecb526cSAndreas Boehler foreach($attachments as $attachment) 8484ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 8494ecb526cSAndreas Boehler 850cb71a62aSAndreas Boehler // Setup DTSTART 851b269830cSAndreas Boehler $dtStart = new \DateTime(); 852a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 853b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 854b269830cSAndreas Boehler if($params['allday'] != '1') 855b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 856cb71a62aSAndreas Boehler 8574ecb526cSAndreas Boehler // Setup DTEND 858b269830cSAndreas Boehler $dtEnd = new \DateTime(); 859a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 860b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 861b269830cSAndreas Boehler if($params['allday'] != '1') 862b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 863cb71a62aSAndreas Boehler 864b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 865b269830cSAndreas Boehler if($params['allday'] == '1') 866b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 867b269830cSAndreas Boehler $vevent->remove('DTSTART'); 868b269830cSAndreas Boehler $vevent->remove('DTEND'); 869b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 870b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 871cb71a62aSAndreas Boehler 872cb71a62aSAndreas Boehler // Remove the time for allday events 873b269830cSAndreas Boehler if($params['allday'] == '1') 874b269830cSAndreas Boehler { 875b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 876b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 877b269830cSAndreas Boehler } 878a1a3b679SAndreas Boehler $now = new DateTime(); 879a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 880cb71a62aSAndreas Boehler // Actually write to the database 88151f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 88251f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 88351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 88451f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 88555a741c0SAndreas Boehler if($res !== false) 88655a741c0SAndreas Boehler { 88755a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 888a1a3b679SAndreas Boehler return true; 889a1a3b679SAndreas Boehler } 89055a741c0SAndreas Boehler return false; 89155a741c0SAndreas Boehler } 892a1a3b679SAndreas Boehler 893cb71a62aSAndreas Boehler /** 894cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 895cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 896cb71a62aSAndreas Boehler * 897cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 898cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 899cb71a62aSAndreas Boehler * 900cb71a62aSAndreas Boehler * @return boolean True 901cb71a62aSAndreas Boehler */ 902a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 903a1a3b679SAndreas Boehler { 904a1a3b679SAndreas Boehler $uid = $params['uid']; 90555a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 9062c14b82bSAndreas Boehler $calid = $event['calendarid']; 90755a741c0SAndreas Boehler $uri = $event['uri']; 90851f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 90951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 91055a741c0SAndreas Boehler if($res !== false) 91155a741c0SAndreas Boehler { 91255a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 91355a741c0SAndreas Boehler } 914a1a3b679SAndreas Boehler return true; 915a1a3b679SAndreas Boehler } 916a1a3b679SAndreas Boehler 917cb71a62aSAndreas Boehler /** 918cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 919cb71a62aSAndreas Boehler * 920cb71a62aSAndreas Boehler * @param string $calid The calendar id 921cb71a62aSAndreas Boehler * 922cb71a62aSAndreas Boehler * @return mixed The synctoken or false 923cb71a62aSAndreas Boehler */ 92455a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 92555a741c0SAndreas Boehler { 926b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 92755a741c0SAndreas Boehler if(isset($row['synctoken'])) 92855a741c0SAndreas Boehler return $row['synctoken']; 92955a741c0SAndreas Boehler return false; 93055a741c0SAndreas Boehler } 93155a741c0SAndreas Boehler 932cb71a62aSAndreas Boehler /** 933cb71a62aSAndreas Boehler * Helper function to convert the operation name to 934cb71a62aSAndreas Boehler * an operation code as stored in the database 935cb71a62aSAndreas Boehler * 936cb71a62aSAndreas Boehler * @param string $operationName The operation name 937cb71a62aSAndreas Boehler * 938cb71a62aSAndreas Boehler * @return mixed The operation code or false 939cb71a62aSAndreas Boehler */ 94055a741c0SAndreas Boehler public function operationNameToOperation($operationName) 94155a741c0SAndreas Boehler { 94255a741c0SAndreas Boehler switch($operationName) 94355a741c0SAndreas Boehler { 94455a741c0SAndreas Boehler case 'added': 94555a741c0SAndreas Boehler return 1; 94655a741c0SAndreas Boehler break; 94755a741c0SAndreas Boehler case 'modified': 94855a741c0SAndreas Boehler return 2; 94955a741c0SAndreas Boehler break; 95055a741c0SAndreas Boehler case 'deleted': 95155a741c0SAndreas Boehler return 3; 95255a741c0SAndreas Boehler break; 95355a741c0SAndreas Boehler } 95455a741c0SAndreas Boehler return false; 95555a741c0SAndreas Boehler } 95655a741c0SAndreas Boehler 957cb71a62aSAndreas Boehler /** 958cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 959cb71a62aSAndreas Boehler * operation that was performed. 960cb71a62aSAndreas Boehler * 961cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 962cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 963cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 964cb71a62aSAndreas Boehler * 965cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 966cb71a62aSAndreas Boehler */ 96755a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 96855a741c0SAndreas Boehler { 96955a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 97055a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 97155a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 97255a741c0SAndreas Boehler return false; 97355a741c0SAndreas Boehler $values = array($uri, 97455a741c0SAndreas Boehler $currentToken, 97555a741c0SAndreas Boehler $calid, 97655a741c0SAndreas Boehler $operationCode 97755a741c0SAndreas Boehler ); 97851f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 97951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 98055a741c0SAndreas Boehler if($res === false) 98155a741c0SAndreas Boehler return false; 98255a741c0SAndreas Boehler $currentToken++; 98351f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 98451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $currentToken, $calid); 98555a741c0SAndreas Boehler return ($res !== false); 98655a741c0SAndreas Boehler } 98755a741c0SAndreas Boehler 988cb71a62aSAndreas Boehler /** 989cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 990cb71a62aSAndreas Boehler * 991cb71a62aSAndreas Boehler * @param string $id The page's ID 992cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 993cb71a62aSAndreas Boehler * 994cb71a62aSAndreas Boehler * @return mixed The sync url or false 995cb71a62aSAndreas Boehler */ 996b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 997b269830cSAndreas Boehler { 99834a47953SAndreas Boehler if(is_null($userid)) 99934a47953SAndreas Boehler { 100034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 100134a47953SAndreas Boehler { 100234a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 100334a47953SAndreas Boehler } 100434a47953SAndreas Boehler else 100534a47953SAndreas Boehler { 100634a47953SAndreas Boehler return false; 100734a47953SAndreas Boehler } 100834a47953SAndreas Boehler } 1009b269830cSAndreas Boehler 1010b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1011b269830cSAndreas Boehler if($calid === false) 1012b269830cSAndreas Boehler return false; 1013b269830cSAndreas Boehler 1014b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1015b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1016b269830cSAndreas Boehler return false; 1017b269830cSAndreas Boehler 1018b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1019b269830cSAndreas Boehler return $syncurl; 1020b269830cSAndreas Boehler } 1021b269830cSAndreas Boehler 1022cb71a62aSAndreas Boehler /** 1023cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1024cb71a62aSAndreas Boehler * 1025cb71a62aSAndreas Boehler * @param string $id the page ID 1026cb71a62aSAndreas Boehler * 1027cb71a62aSAndreas Boehler * @return mixed The private URL or false 1028cb71a62aSAndreas Boehler */ 1029f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1030f69bb449SAndreas Boehler { 1031f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1032f69bb449SAndreas Boehler if($calid === false) 1033f69bb449SAndreas Boehler return false; 1034f69bb449SAndreas Boehler 1035f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1036f69bb449SAndreas Boehler } 1037f69bb449SAndreas Boehler 1038cb71a62aSAndreas Boehler /** 1039cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1040cb71a62aSAndreas Boehler * 1041cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1042cb71a62aSAndreas Boehler * 1043cb71a62aSAndreas Boehler * @return mixed The private URL or false 1044cb71a62aSAndreas Boehler */ 1045f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1046f69bb449SAndreas Boehler { 1047185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1048185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 104951f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 105051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1051f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1052f69bb449SAndreas Boehler if(!isset($row['url'])) 1053f69bb449SAndreas Boehler { 1054f69bb449SAndreas Boehler $url = uniqid("dokuwiki-").".ics"; 105551f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 105651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url, $calid); 1057f69bb449SAndreas Boehler if($res === false) 1058f69bb449SAndreas Boehler return false; 1059f69bb449SAndreas Boehler } 1060f69bb449SAndreas Boehler else 1061f69bb449SAndreas Boehler { 1062f69bb449SAndreas Boehler $url = $row['url']; 1063f69bb449SAndreas Boehler } 1064185e2535SAndreas Boehler 1065185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1066185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1067185e2535SAndreas Boehler return $url; 1068f69bb449SAndreas Boehler } 1069f69bb449SAndreas Boehler 1070cb71a62aSAndreas Boehler /** 1071cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1072cb71a62aSAndreas Boehler * 1073cb71a62aSAndreas Boehler * @param string $url The private URL 1074cb71a62aSAndreas Boehler * 1075cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1076cb71a62aSAndreas Boehler */ 1077f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1078f69bb449SAndreas Boehler { 107951f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 108051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url); 1081f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1082f69bb449SAndreas Boehler if(!isset($row['calid'])) 1083f69bb449SAndreas Boehler return false; 1084f69bb449SAndreas Boehler return $row['calid']; 1085f69bb449SAndreas Boehler } 1086f69bb449SAndreas Boehler 1087cb71a62aSAndreas Boehler /** 1088cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1089cb71a62aSAndreas Boehler * 1090*7e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1091cb71a62aSAndreas Boehler * 1092cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1093cb71a62aSAndreas Boehler */ 1094f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1095f69bb449SAndreas Boehler { 1096f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1097f69bb449SAndreas Boehler if($calSettings === false) 1098f69bb449SAndreas Boehler return false; 1099f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1100f69bb449SAndreas Boehler if($events === false) 1101f69bb449SAndreas Boehler return false; 1102f69bb449SAndreas Boehler 1103*7e0b8590SAndreas Boehler // Load SabreDAV 1104*7e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1105*7e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 1106*7e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1107f69bb449SAndreas Boehler foreach($events as $event) 1108f69bb449SAndreas Boehler { 1109*7e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1110*7e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 1111*7e0b8590SAndreas Boehler $out .= $evt->serialize(); 1112f69bb449SAndreas Boehler } 1113*7e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1114f69bb449SAndreas Boehler return $out; 1115f69bb449SAndreas Boehler } 1116f69bb449SAndreas Boehler 11177c7c6b0bSAndreas Boehler /** 11187c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 11197c7c6b0bSAndreas Boehler * 11207c7c6b0bSAndreas Boehler * @param string $key The key to query 112121d04f73SAndreas Boehler * @return mixed The option set, null if not found 11227c7c6b0bSAndreas Boehler */ 11237c7c6b0bSAndreas Boehler public function getConfig($key) 11247c7c6b0bSAndreas Boehler { 11257c7c6b0bSAndreas Boehler return $this->getConf($key); 11267c7c6b0bSAndreas Boehler } 11277c7c6b0bSAndreas Boehler 1128a1a3b679SAndreas Boehler} 1129