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 dbglog('---- DAVCAL helper.php init'); 23a1a3b679SAndreas Boehler if(!$this->sqlite) 24a1a3b679SAndreas Boehler { 2521d04f73SAndreas Boehler dbglog('This plugin requires the sqlite plugin. Please install it.'); 26a1a3b679SAndreas Boehler msg('This plugin requires the sqlite plugin. Please install it.'); 27a1a3b679SAndreas Boehler return; 28a1a3b679SAndreas Boehler } 29a1a3b679SAndreas Boehler 30a1a3b679SAndreas Boehler if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/')) 31a1a3b679SAndreas Boehler { 3221d04f73SAndreas Boehler dbglog('Error initialising the SQLite DB for DAVCal'); 33a1a3b679SAndreas Boehler return; 34a1a3b679SAndreas Boehler } 35a1a3b679SAndreas Boehler } 36a1a3b679SAndreas Boehler 37cb71a62aSAndreas Boehler /** 38185e2535SAndreas Boehler * Retrieve meta data for a given page 39185e2535SAndreas Boehler * 40185e2535SAndreas Boehler * @param string $id optional The page ID 41185e2535SAndreas Boehler * @return array The metadata 42185e2535SAndreas Boehler */ 43185e2535SAndreas Boehler private function getMeta($id = null) { 44185e2535SAndreas Boehler global $ID; 45185e2535SAndreas Boehler global $INFO; 46185e2535SAndreas Boehler 47185e2535SAndreas Boehler if ($id === null) $id = $ID; 48185e2535SAndreas Boehler 49185e2535SAndreas Boehler if($ID === $id && $INFO['meta']) { 50185e2535SAndreas Boehler $meta = $INFO['meta']; 51185e2535SAndreas Boehler } else { 52185e2535SAndreas Boehler $meta = p_get_metadata($id); 53185e2535SAndreas Boehler } 54185e2535SAndreas Boehler 55185e2535SAndreas Boehler return $meta; 56185e2535SAndreas Boehler } 57185e2535SAndreas Boehler 58185e2535SAndreas Boehler /** 59185e2535SAndreas Boehler * Retrieve the meta data for a given page 60185e2535SAndreas Boehler * 61185e2535SAndreas Boehler * @param string $id optional The page ID 62185e2535SAndreas Boehler * @return array with meta data 63185e2535SAndreas Boehler */ 64185e2535SAndreas Boehler public function getCalendarMetaForPage($id = null) 65185e2535SAndreas Boehler { 66185e2535SAndreas Boehler if(is_null($id)) 67185e2535SAndreas Boehler { 68185e2535SAndreas Boehler global $ID; 69185e2535SAndreas Boehler $id = $ID; 70185e2535SAndreas Boehler } 71185e2535SAndreas Boehler 72185e2535SAndreas Boehler $meta = $this->getMeta($id); 73185e2535SAndreas Boehler if(isset($meta['plugin_davcal'])) 74185e2535SAndreas Boehler return $meta['plugin_davcal']; 75185e2535SAndreas Boehler else 76185e2535SAndreas Boehler return array(); 77185e2535SAndreas Boehler } 78185e2535SAndreas Boehler 79185e2535SAndreas Boehler /** 80d71c9934SAndreas Boehler * Check the permission of a user for a given calendar ID 81d71c9934SAndreas Boehler * 82d71c9934SAndreas Boehler * @param string $id The calendar ID to check 83d71c9934SAndreas Boehler * @return int AUTH_* constants 84d71c9934SAndreas Boehler */ 85d71c9934SAndreas Boehler public function checkCalendarPermission($id) 86d71c9934SAndreas Boehler { 87d71c9934SAndreas Boehler if(strpos($page, 'webdav://') === 0) 88d71c9934SAndreas Boehler { 89d71c9934SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 90d71c9934SAndreas Boehler if(is_null($wdc)) 91d71c9934SAndreas Boehler return AUTH_NONE; 92d71c9934SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 93d71c9934SAndreas Boehler $settings = $wdc->getConnection($connectionId); 94d71c9934SAndreas Boehler if($settings === false) 95d71c9934SAndreas Boehler return AUTH_NONE; 96d71c9934SAndreas Boehler if($settings['write'] === '1') 97d71c9934SAndreas Boehler return AUTH_CREATE; 98d71c9934SAndreas Boehler return AUTH_READ; 99d71c9934SAndreas Boehler } 100d71c9934SAndreas Boehler else 101d71c9934SAndreas Boehler { 102d71c9934SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 103d71c9934SAndreas Boehler // We return AUTH_READ if the calendar does not exist. This makes 104d71c9934SAndreas Boehler // davcal happy when there are just included calendars 105d71c9934SAndreas Boehler if($calid === false) 106d71c9934SAndreas Boehler return AUTH_READ; 107d71c9934SAndreas Boehler return auth_quickaclcheck($id); 108d71c9934SAndreas Boehler } 109d71c9934SAndreas Boehler } 110d71c9934SAndreas Boehler 111d71c9934SAndreas Boehler /** 11280e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 11380e1ddf7SAndreas Boehler * user has at least read permission. 11480e1ddf7SAndreas Boehler * 11580e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 11680e1ddf7SAndreas Boehler * @return array with filtered calendar pages 11780e1ddf7SAndreas Boehler */ 11880e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 11980e1ddf7SAndreas Boehler { 12080e1ddf7SAndreas Boehler $retList = array(); 12180e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 12280e1ddf7SAndreas Boehler { 1230b805092SAndreas Boehler // WebDAV Connections are always readable 1240b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1250b805092SAndreas Boehler { 1260b805092SAndreas Boehler $retList[$page] = $data; 1270b805092SAndreas Boehler } 1280b805092SAndreas Boehler elseif(auth_quickaclcheck($page) >= AUTH_READ) 12980e1ddf7SAndreas Boehler { 13080e1ddf7SAndreas Boehler $retList[$page] = $data; 13180e1ddf7SAndreas Boehler } 13280e1ddf7SAndreas Boehler } 13380e1ddf7SAndreas Boehler return $retList; 13480e1ddf7SAndreas Boehler } 13580e1ddf7SAndreas Boehler 13680e1ddf7SAndreas Boehler /** 137185e2535SAndreas Boehler * Get all calendar pages used by a given page 138185e2535SAndreas Boehler * based on the stored metadata 139185e2535SAndreas Boehler * 140185e2535SAndreas Boehler * @param string $id optional The page id 141185e2535SAndreas Boehler * @return mixed The pages as array or false 142185e2535SAndreas Boehler */ 143185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 144185e2535SAndreas Boehler { 145185e2535SAndreas Boehler if(is_null($id)) 146185e2535SAndreas Boehler { 147185e2535SAndreas Boehler global $ID; 148185e2535SAndreas Boehler $id = $ID; 149185e2535SAndreas Boehler } 150185e2535SAndreas Boehler 151185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 1520b805092SAndreas Boehler 153185e2535SAndreas Boehler if(isset($meta['id'])) 154ed764890SAndreas Boehler { 155ed764890SAndreas Boehler // Filter the list of pages by permission 15680e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 15780e1ddf7SAndreas Boehler if(empty($pages)) 158ed764890SAndreas Boehler return false; 15980e1ddf7SAndreas Boehler return $pages; 160ed764890SAndreas Boehler } 161185e2535SAndreas Boehler return false; 162185e2535SAndreas Boehler } 163185e2535SAndreas Boehler 164185e2535SAndreas Boehler /** 165185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 166185e2535SAndreas Boehler * for an array of page ids 167185e2535SAndreas Boehler * 168185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 169185e2535SAndreas Boehler * @return array The list 170185e2535SAndreas Boehler */ 171185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 172185e2535SAndreas Boehler { 173185e2535SAndreas Boehler $data = array(); 1744a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 175185e2535SAndreas Boehler { 1760b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1770b805092SAndreas Boehler { 1780b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1790b805092SAndreas Boehler if(is_null($wdc)) 1800b805092SAndreas Boehler continue; 1810b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1820b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 1832393a702SAndreas Boehler if($settings === false) 1842393a702SAndreas Boehler continue; 1850b805092SAndreas Boehler $name = $settings['displayname']; 186d71c9934SAndreas Boehler $write = ($settings['write'] === '1'); 1870b805092SAndreas Boehler $calid = $connectionId; 188cd2f100dSAndreas Boehler $color = '#3a87ad'; 1890b805092SAndreas Boehler } 1900b805092SAndreas Boehler else 1910b805092SAndreas Boehler { 192185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 193185e2535SAndreas Boehler if($calid !== false) 194185e2535SAndreas Boehler { 195185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 196185e2535SAndreas Boehler $name = $settings['displayname']; 197cd2f100dSAndreas Boehler $color = $settings['calendarcolor']; 198ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 1990b805092SAndreas Boehler } 2000b805092SAndreas Boehler else 2010b805092SAndreas Boehler { 2020b805092SAndreas Boehler continue; 2030b805092SAndreas Boehler } 2040b805092SAndreas Boehler } 205185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 206ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 207185e2535SAndreas Boehler } 208185e2535SAndreas Boehler return $data; 209185e2535SAndreas Boehler } 210185e2535SAndreas Boehler 211185e2535SAndreas Boehler /** 212185e2535SAndreas Boehler * Get the saved calendar color for a given page. 213185e2535SAndreas Boehler * 214185e2535SAndreas Boehler * @param string $id optional The page ID 215185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 216185e2535SAndreas Boehler */ 217185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 218185e2535SAndreas Boehler { 219185e2535SAndreas Boehler if(is_null($id)) 220185e2535SAndreas Boehler { 221185e2535SAndreas Boehler global $ID; 222185e2535SAndreas Boehler $id = $ID; 223185e2535SAndreas Boehler } 224185e2535SAndreas Boehler 225185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 226185e2535SAndreas Boehler if($calid === false) 227185e2535SAndreas Boehler return false; 228185e2535SAndreas Boehler 229185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 230185e2535SAndreas Boehler } 231185e2535SAndreas Boehler 232185e2535SAndreas Boehler /** 233185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 234185e2535SAndreas Boehler * 235185e2535SAndreas Boehler * @param string $id optional The calendar ID 236185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 237185e2535SAndreas Boehler */ 238185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 239185e2535SAndreas Boehler { 240185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 241185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 242185e2535SAndreas Boehler 243185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 244185e2535SAndreas Boehler 245185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 246185e2535SAndreas Boehler return false; 247185e2535SAndreas Boehler 248185e2535SAndreas Boehler $color = $row['calendarcolor']; 249185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 250185e2535SAndreas Boehler return $color; 251185e2535SAndreas Boehler } 252185e2535SAndreas Boehler 253185e2535SAndreas Boehler /** 254e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 255e86c8dd3SAndreas Boehler * @param string $user the user name 256e86c8dd3SAndreas Boehler * @return the URL to the principal sync 257e86c8dd3SAndreas Boehler */ 258e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 259e86c8dd3SAndreas Boehler { 260e86c8dd3SAndreas Boehler if(is_null($user)) 261e86c8dd3SAndreas Boehler return false; 262e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 263e86c8dd3SAndreas Boehler return $url; 264e86c8dd3SAndreas Boehler } 265e86c8dd3SAndreas Boehler 266e86c8dd3SAndreas Boehler /** 267185e2535SAndreas Boehler * Set the calendar color for a given page. 268185e2535SAndreas Boehler * 269185e2535SAndreas Boehler * @param string $color The color definition 270185e2535SAndreas Boehler * @param string $id optional The page ID 271185e2535SAndreas Boehler * @return boolean True on success, otherwise false 272185e2535SAndreas Boehler */ 273185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 274185e2535SAndreas Boehler { 275185e2535SAndreas Boehler if(is_null($id)) 276185e2535SAndreas Boehler { 277185e2535SAndreas Boehler global $ID; 278185e2535SAndreas Boehler $id = $ID; 279185e2535SAndreas Boehler } 280185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 281185e2535SAndreas Boehler if($calid === false) 282185e2535SAndreas Boehler return false; 283185e2535SAndreas Boehler 28451f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 28551f4febbSAndreas Boehler " WHERE id = ?"; 28651f4febbSAndreas Boehler $res = $this->sqlite->query($query, $color, $calid); 287185e2535SAndreas Boehler if($res !== false) 288185e2535SAndreas Boehler { 289185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 290185e2535SAndreas Boehler return true; 291185e2535SAndreas Boehler } 292185e2535SAndreas Boehler return false; 293185e2535SAndreas Boehler } 294185e2535SAndreas Boehler 295185e2535SAndreas Boehler /** 296cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 297cb71a62aSAndreas Boehler * page id. 298cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 299cb71a62aSAndreas Boehler * 300cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 301cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 302cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 303cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 304cb71a62aSAndreas Boehler * 305cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 306cb71a62aSAndreas Boehler */ 307a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 308a1a3b679SAndreas Boehler { 309a1a3b679SAndreas Boehler if(is_null($id)) 310a1a3b679SAndreas Boehler { 311a1a3b679SAndreas Boehler global $ID; 312a1a3b679SAndreas Boehler $id = $ID; 313a1a3b679SAndreas Boehler } 314a1a3b679SAndreas Boehler if(is_null($userid)) 31534a47953SAndreas Boehler { 31634a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 31734a47953SAndreas Boehler { 318a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 31934a47953SAndreas Boehler } 32034a47953SAndreas Boehler else 32134a47953SAndreas Boehler { 32234a47953SAndreas Boehler $userid = uniqid('davcal-'); 32334a47953SAndreas Boehler } 32434a47953SAndreas Boehler } 325a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 326a1a3b679SAndreas Boehler if($calid === false) 327a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 328a1a3b679SAndreas Boehler 32951f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 33051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $name, $description, $calid); 331b269830cSAndreas Boehler if($res !== false) 332b269830cSAndreas Boehler return true; 333b269830cSAndreas Boehler return false; 334a1a3b679SAndreas Boehler } 335a1a3b679SAndreas Boehler 336cb71a62aSAndreas Boehler /** 337d5703f5aSAndreas Boehler * Update a calendar's displayname 338d5703f5aSAndreas Boehler * 339d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 340d5703f5aSAndreas Boehler * @param string $name The new calendar name 341d5703f5aSAndreas Boehler * 342d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 343d5703f5aSAndreas Boehler */ 344d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 345d5703f5aSAndreas Boehler { 346d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 347d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $name); 348d5703f5aSAndreas Boehler if($res !== false) 349d5703f5aSAndreas Boehler { 350d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 351d5703f5aSAndreas Boehler return true; 352d5703f5aSAndreas Boehler } 353d5703f5aSAndreas Boehler return false; 354d5703f5aSAndreas Boehler } 355d5703f5aSAndreas Boehler 356d5703f5aSAndreas Boehler /** 357d5703f5aSAndreas Boehler * Update the calendar description 358d5703f5aSAndreas Boehler * 359d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 360d5703f5aSAndreas Boehler * @param string $description The new calendar's description 361d5703f5aSAndreas Boehler * 362d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 363d5703f5aSAndreas Boehler */ 364d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 365d5703f5aSAndreas Boehler { 366d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 367d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $description); 368d5703f5aSAndreas Boehler if($res !== false) 369d5703f5aSAndreas Boehler { 370d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 371d5703f5aSAndreas Boehler return true; 372d5703f5aSAndreas Boehler } 373d5703f5aSAndreas Boehler return false; 374d5703f5aSAndreas Boehler } 375d5703f5aSAndreas Boehler 376d5703f5aSAndreas Boehler /** 377d5703f5aSAndreas Boehler * Update a calendar's timezone information 378d5703f5aSAndreas Boehler * 379d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 380d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 381d5703f5aSAndreas Boehler * 382d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 383d5703f5aSAndreas Boehler */ 384d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 385d5703f5aSAndreas Boehler { 386d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 387d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $timezone); 388d5703f5aSAndreas Boehler if($res !== false) 389d5703f5aSAndreas Boehler { 390d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 391d5703f5aSAndreas Boehler return true; 392d5703f5aSAndreas Boehler } 393d5703f5aSAndreas Boehler return false; 394d5703f5aSAndreas Boehler } 395d5703f5aSAndreas Boehler 396d5703f5aSAndreas Boehler /** 397cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 398cb71a62aSAndreas Boehler * 399cb71a62aSAndreas Boehler * @param array $settings The settings array to store 400cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 401cb71a62aSAndreas Boehler * 402cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 403cb71a62aSAndreas Boehler */ 404a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 405a495d34cSAndreas Boehler { 406a495d34cSAndreas Boehler if(is_null($userid)) 40734a47953SAndreas Boehler { 40834a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 40934a47953SAndreas Boehler { 410a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 41134a47953SAndreas Boehler } 41234a47953SAndreas Boehler else 41334a47953SAndreas Boehler { 41434a47953SAndreas Boehler return false; 41534a47953SAndreas Boehler } 41634a47953SAndreas Boehler } 417a495d34cSAndreas Boehler $this->sqlite->query("BEGIN TRANSACTION"); 418a495d34cSAndreas Boehler 41951f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 42051f4febbSAndreas Boehler $this->sqlite->query($query, $userid); 421bd883736SAndreas Boehler 422a495d34cSAndreas Boehler foreach($settings as $key => $value) 423a495d34cSAndreas Boehler { 42451f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 42551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid, $key, $value); 426a495d34cSAndreas Boehler if($res === false) 427a495d34cSAndreas Boehler return false; 428a495d34cSAndreas Boehler } 429a495d34cSAndreas Boehler $this->sqlite->query("COMMIT TRANSACTION"); 430185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 431a495d34cSAndreas Boehler return true; 432a495d34cSAndreas Boehler } 433a495d34cSAndreas Boehler 434cb71a62aSAndreas Boehler /** 435cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 436cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 437cb71a62aSAndreas Boehler * 438cb71a62aSAndreas Boehler * timezone => local 439cb71a62aSAndreas Boehler * weeknumbers => 0 440cb71a62aSAndreas Boehler * workweek => 0 441cb71a62aSAndreas Boehler * 442cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 443cb71a62aSAndreas Boehler * 444cb71a62aSAndreas Boehler * @return array The settings array 445cb71a62aSAndreas Boehler */ 446a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 447a495d34cSAndreas Boehler { 448bd883736SAndreas Boehler // Some sane default settings 449bd883736SAndreas Boehler $settings = array( 450fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 451fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 452fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4531d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4541d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 455bd883736SAndreas Boehler ); 45634a47953SAndreas Boehler if(is_null($userid)) 45734a47953SAndreas Boehler { 45834a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 45934a47953SAndreas Boehler { 46034a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 46134a47953SAndreas Boehler } 46234a47953SAndreas Boehler else 46334a47953SAndreas Boehler { 46434a47953SAndreas Boehler return $settings; 46534a47953SAndreas Boehler } 46634a47953SAndreas Boehler } 46734a47953SAndreas Boehler 46834a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 46934a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 47051f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 47151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $userid); 472a495d34cSAndreas Boehler $arr = $this->sqlite->res2arr($res); 473a495d34cSAndreas Boehler foreach($arr as $row) 474a495d34cSAndreas Boehler { 475a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 476a495d34cSAndreas Boehler } 477185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 478a495d34cSAndreas Boehler return $settings; 479a495d34cSAndreas Boehler } 480a495d34cSAndreas Boehler 481cb71a62aSAndreas Boehler /** 482cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 483cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 484cb71a62aSAndreas Boehler * 485cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 486cb71a62aSAndreas Boehler * 487cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 488cb71a62aSAndreas Boehler */ 489a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 490a1a3b679SAndreas Boehler { 491a1a3b679SAndreas Boehler if(is_null($id)) 492a1a3b679SAndreas Boehler { 493a1a3b679SAndreas Boehler global $ID; 494a1a3b679SAndreas Boehler $id = $ID; 495a1a3b679SAndreas Boehler } 496a1a3b679SAndreas Boehler 497185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 498185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 499185e2535SAndreas Boehler 50051f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 50151f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id); 502a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 503a1a3b679SAndreas Boehler if(isset($row['calid'])) 504185e2535SAndreas Boehler { 505185e2535SAndreas Boehler $calid = $row['calid']; 506185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 507185e2535SAndreas Boehler return $calid; 508185e2535SAndreas Boehler } 509a1a3b679SAndreas Boehler return false; 510a1a3b679SAndreas Boehler } 511a1a3b679SAndreas Boehler 512cb71a62aSAndreas Boehler /** 513cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 514cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 515cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 516cb71a62aSAndreas Boehler * 517cb71a62aSAndreas Boehler * @return array The mapping array 518cb71a62aSAndreas Boehler */ 519a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 520a1a3b679SAndreas Boehler { 521a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 522a1a3b679SAndreas Boehler $res = $this->sqlite->query($query); 523a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 524a1a3b679SAndreas Boehler return $arr; 525a1a3b679SAndreas Boehler } 526a1a3b679SAndreas Boehler 527cb71a62aSAndreas Boehler /** 528cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 529cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 530cb71a62aSAndreas Boehler * user name is actually split from the URI component. 531cb71a62aSAndreas Boehler * 532cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 533cb71a62aSAndreas Boehler * and applied accordingly. 534cb71a62aSAndreas Boehler * 535cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 536cb71a62aSAndreas Boehler * 537cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 538cb71a62aSAndreas Boehler */ 539a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 540a1a3b679SAndreas Boehler { 54134a47953SAndreas Boehler global $auth; 542a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 543a1a3b679SAndreas Boehler $user = end($user); 544a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 545a1a3b679SAndreas Boehler $calids = array(); 54634a47953SAndreas Boehler $ud = $auth->getUserData($user); 54734a47953SAndreas Boehler $groups = $ud['grps']; 548a1a3b679SAndreas Boehler foreach($mapping as $row) 549a1a3b679SAndreas Boehler { 550a1a3b679SAndreas Boehler $id = $row['calid']; 55113b16484SAndreas Boehler $enabled = $this->getCalendarStatus($id); 55213b16484SAndreas Boehler if($enabled == false) 55313b16484SAndreas Boehler continue; 554a1a3b679SAndreas Boehler $page = $row['page']; 55534a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 556a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 557a1a3b679SAndreas Boehler { 558a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 559a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 560a1a3b679SAndreas Boehler } 561a1a3b679SAndreas Boehler } 562a1a3b679SAndreas Boehler return $calids; 563a1a3b679SAndreas Boehler } 564a1a3b679SAndreas Boehler 565cb71a62aSAndreas Boehler /** 566cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 567cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 568cb71a62aSAndreas Boehler * 569cb71a62aSAndreas Boehler * @param string $name The calendar's name 570cb71a62aSAndreas Boehler * @param string $description The calendar's description 571cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 572cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 573cb71a62aSAndreas Boehler * 574cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 575cb71a62aSAndreas Boehler */ 576a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 577a1a3b679SAndreas Boehler { 578a1a3b679SAndreas Boehler if(is_null($id)) 579a1a3b679SAndreas Boehler { 580a1a3b679SAndreas Boehler global $ID; 581a1a3b679SAndreas Boehler $id = $ID; 582a1a3b679SAndreas Boehler } 583a1a3b679SAndreas Boehler if(is_null($userid)) 58434a47953SAndreas Boehler { 58534a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 58634a47953SAndreas Boehler { 587a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 58834a47953SAndreas Boehler } 58934a47953SAndreas Boehler else 59034a47953SAndreas Boehler { 59134a47953SAndreas Boehler $userid = uniqid('davcal-'); 59234a47953SAndreas Boehler } 59334a47953SAndreas Boehler } 594a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 595a1a3b679SAndreas Boehler $name, 596a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 597a1a3b679SAndreas Boehler $description, 598a1a3b679SAndreas Boehler 'VEVENT,VTODO', 59955a741c0SAndreas Boehler 0, 60055a741c0SAndreas Boehler 1); 60151f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 60251f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 60351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 60455a741c0SAndreas Boehler if($res === false) 60555a741c0SAndreas Boehler return false; 606cb71a62aSAndreas Boehler 607cb71a62aSAndreas Boehler // Get the new calendar ID 60851f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 60951f4febbSAndreas Boehler "uri = ? AND description = ?"; 61051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 611a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 612cb71a62aSAndreas Boehler 613cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 614a1a3b679SAndreas Boehler if(isset($row['id'])) 615a1a3b679SAndreas Boehler { 61651f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 61751f4febbSAndreas Boehler $res = $this->sqlite->query($query, $id, $row['id']); 61855a741c0SAndreas Boehler return ($res !== false); 619a1a3b679SAndreas Boehler } 620a1a3b679SAndreas Boehler 621a1a3b679SAndreas Boehler return false; 622a1a3b679SAndreas Boehler } 623a1a3b679SAndreas Boehler 624cb71a62aSAndreas Boehler /** 625d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 626d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 627d5703f5aSAndreas Boehler * 628d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 629d5703f5aSAndreas Boehler * 630d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 631d5703f5aSAndreas Boehler * @param string $uri The new object URI 632d5703f5aSAndreas Boehler * @param string $ics The ICS file 633d5703f5aSAndreas Boehler * 634d5703f5aSAndreas Boehler * @return mixed The etag. 635d5703f5aSAndreas Boehler */ 636d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 637d5703f5aSAndreas Boehler { 638d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 639d5703f5aSAndreas Boehler 640d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 641d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 642d5703f5aSAndreas Boehler $calid, 643d5703f5aSAndreas Boehler $uri, 644d5703f5aSAndreas Boehler $ics, 645d5703f5aSAndreas Boehler time(), 646d5703f5aSAndreas Boehler $extraData['etag'], 647d5703f5aSAndreas Boehler $extraData['size'], 648d5703f5aSAndreas Boehler $extraData['componentType'], 649d5703f5aSAndreas Boehler $extraData['firstOccurence'], 650d5703f5aSAndreas Boehler $extraData['lastOccurence'], 651d5703f5aSAndreas Boehler $extraData['uid']); 652d5703f5aSAndreas Boehler // If successfully, update the sync token database 653d5703f5aSAndreas Boehler if($res !== false) 654d5703f5aSAndreas Boehler { 655d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 656d5703f5aSAndreas Boehler } 657d5703f5aSAndreas Boehler return $extraData['etag']; 658d5703f5aSAndreas Boehler } 659d5703f5aSAndreas Boehler 660d5703f5aSAndreas Boehler /** 661d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 662d5703f5aSAndreas Boehler * needed for the sync support. 663d5703f5aSAndreas Boehler * 664d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 665d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 666d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 667d5703f5aSAndreas Boehler */ 668d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 669d5703f5aSAndreas Boehler { 670d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 671d5703f5aSAndreas Boehler 672d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 673d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, 674d5703f5aSAndreas Boehler $ics, 675d5703f5aSAndreas Boehler time(), 676d5703f5aSAndreas Boehler $extraData['etag'], 677d5703f5aSAndreas Boehler $extraData['size'], 678d5703f5aSAndreas Boehler $extraData['componentType'], 679d5703f5aSAndreas Boehler $extraData['firstOccurence'], 680d5703f5aSAndreas Boehler $extraData['lastOccurence'], 681d5703f5aSAndreas Boehler $extraData['uid'], 682d5703f5aSAndreas Boehler $calid, 683d5703f5aSAndreas Boehler $uri 684d5703f5aSAndreas Boehler ); 685d5703f5aSAndreas Boehler if($res !== false) 686d5703f5aSAndreas Boehler { 687d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 688d5703f5aSAndreas Boehler } 689d5703f5aSAndreas Boehler return $extraData['etag']; 690d5703f5aSAndreas Boehler } 691d5703f5aSAndreas Boehler 692d5703f5aSAndreas Boehler /** 693cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 694cb71a62aSAndreas Boehler * 695cb71a62aSAndreas Boehler * The parameter array needs to contain 696cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 69782a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 698cb71a62aSAndreas Boehler * eventfrom => The event's start date 699cb71a62aSAndreas Boehler * eventfromtime => The event's start time 700cb71a62aSAndreas Boehler * eventto => The event's end date 701cb71a62aSAndreas Boehler * eventtotime => The event's end time 702cb71a62aSAndreas Boehler * eventname => The event's name 703cb71a62aSAndreas Boehler * eventdescription => The event's description 704cb71a62aSAndreas Boehler * 705cb71a62aSAndreas Boehler * @param string $id The page ID to work on 706cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 707cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 708cb71a62aSAndreas Boehler * 709cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 710cb71a62aSAndreas Boehler */ 711a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 712a1a3b679SAndreas Boehler { 71382a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 71482a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 71582a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 716a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 717bd883736SAndreas Boehler else 718bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 719cb71a62aSAndreas Boehler 720cb71a62aSAndreas Boehler // Retrieve dates from settings 721b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 722b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 723b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 724b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 725cb71a62aSAndreas Boehler 726cb71a62aSAndreas Boehler // Load SabreDAV 7279bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 728a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 729cb71a62aSAndreas Boehler 730cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 731a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 732b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 733b269830cSAndreas Boehler $event->add('UID', $uuid); 734a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 735cb71a62aSAndreas Boehler 736cb71a62aSAndreas Boehler // Add a description if requested 7370eebc909SAndreas Boehler $description = $params['eventdescription']; 7380eebc909SAndreas Boehler if($description !== '') 7390eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 740cb71a62aSAndreas Boehler 7412b7be5bdSAndreas Boehler // Add a location if requested 7422b7be5bdSAndreas Boehler $location = $params['eventlocation']; 7432b7be5bdSAndreas Boehler if($location !== '') 7442b7be5bdSAndreas Boehler $event->add('LOCATION', $location); 7452b7be5bdSAndreas Boehler 7464ecb526cSAndreas Boehler // Add attachments 7474ecb526cSAndreas Boehler $attachments = $params['attachments']; 74882a48dfbSAndreas Boehler if(!is_null($attachments)) 7494ecb526cSAndreas Boehler foreach($attachments as $attachment) 7504ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 7514ecb526cSAndreas Boehler 752cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 753b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 754b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 755b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 756b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 757cb71a62aSAndreas Boehler 758cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 759b269830cSAndreas Boehler $dtStart = new \DateTime(); 760a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 761b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 762cb71a62aSAndreas Boehler 763cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 764b269830cSAndreas Boehler if($params['allday'] != '1') 765b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 766cb71a62aSAndreas Boehler 767cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 768b269830cSAndreas Boehler $dtEnd = new \DateTime(); 769a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 770b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 771cb71a62aSAndreas Boehler 772cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 773b269830cSAndreas Boehler if($params['allday'] != '1') 774b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 775cb71a62aSAndreas Boehler 776b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 777b269830cSAndreas Boehler if($params['allday'] == '1') 778b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 779cb71a62aSAndreas Boehler 780cb71a62aSAndreas Boehler // Really add Start and End events 781b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 782b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 783cb71a62aSAndreas Boehler 784cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 785b269830cSAndreas Boehler if($params['allday'] == '1') 786b269830cSAndreas Boehler { 787b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 788b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 789b269830cSAndreas Boehler } 790cb71a62aSAndreas Boehler 791809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 792809cb0faSAndreas Boehler 793809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 794809cb0faSAndreas Boehler { 795809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 796809cb0faSAndreas Boehler if(is_null($wdc)) 797809cb0faSAndreas Boehler return false; 798809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 799809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 800809cb0faSAndreas Boehler } 801809cb0faSAndreas Boehler else 802809cb0faSAndreas Boehler { 803cb71a62aSAndreas Boehler // Actually add the values to the database 804a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 805a1a3b679SAndreas Boehler $uri = uniqid('dokuwiki-').'.ics'; 8061bb22c2bSAndreas Boehler $now = new \DateTime(); 807a1a3b679SAndreas Boehler 80851f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 80951f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 81051f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 81151f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 812cb71a62aSAndreas Boehler 813cb71a62aSAndreas Boehler // If successfully, update the sync token database 81455a741c0SAndreas Boehler if($res !== false) 81555a741c0SAndreas Boehler { 81655a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 817a1a3b679SAndreas Boehler return true; 818a1a3b679SAndreas Boehler } 819809cb0faSAndreas Boehler } 82055a741c0SAndreas Boehler return false; 82155a741c0SAndreas Boehler } 822a1a3b679SAndreas Boehler 823cb71a62aSAndreas Boehler /** 824cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 825cb71a62aSAndreas Boehler * 826cb71a62aSAndreas Boehler * @param string $calid The calendar ID 827cb71a62aSAndreas Boehler * 828cb71a62aSAndreas Boehler * @return array The calendar settings array 829cb71a62aSAndreas Boehler */ 830b269830cSAndreas Boehler public function getCalendarSettings($calid) 831b269830cSAndreas Boehler { 83213b16484SAndreas Boehler $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled FROM calendars WHERE id= ? "; 83351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 834b269830cSAndreas Boehler $row = $this->sqlite->res2row($res); 835b269830cSAndreas Boehler return $row; 836b269830cSAndreas Boehler } 837b269830cSAndreas Boehler 838cb71a62aSAndreas Boehler /** 83913b16484SAndreas Boehler * Retrieve the calendar status of a given calendar id 84013b16484SAndreas Boehler * 84113b16484SAndreas Boehler * @param string $calid The calendar ID 84213b16484SAndreas Boehler * @return boolean True if calendar is enabled, otherwise false 84313b16484SAndreas Boehler */ 84413b16484SAndreas Boehler public function getCalendarStatus($calid) 84513b16484SAndreas Boehler { 84613b16484SAndreas Boehler $query = "SELECT disabled FROM calendars WHERE id = ?"; 84713b16484SAndreas Boehler $res = $this->sqlite->query($query, $calid); 84813b16484SAndreas Boehler $row = $this->sqlite->res2row($res); 84913b16484SAndreas Boehler if($row['disabled'] == 1) 85013b16484SAndreas Boehler return false; 85113b16484SAndreas Boehler else 85213b16484SAndreas Boehler return true; 85313b16484SAndreas Boehler } 85413b16484SAndreas Boehler 85513b16484SAndreas Boehler /** 85613b16484SAndreas Boehler * Disable a calendar for a given page 85713b16484SAndreas Boehler * 85813b16484SAndreas Boehler * @param string $id The page ID 85913b16484SAndreas Boehler * 86013b16484SAndreas Boehler * @return boolean true on success, otherwise false 86113b16484SAndreas Boehler */ 86213b16484SAndreas Boehler public function disableCalendarForPage($id) 86313b16484SAndreas Boehler { 86413b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 86513b16484SAndreas Boehler if($calid === false) 86613b16484SAndreas Boehler return false; 86713b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 1 WHERE id = ?"; 86813b16484SAndreas Boehler $res = $this->sqlite->query($query, $calid); 86913b16484SAndreas Boehler if($res !== false) 87013b16484SAndreas Boehler return true; 87113b16484SAndreas Boehler return false; 87213b16484SAndreas Boehler } 87313b16484SAndreas Boehler 87413b16484SAndreas Boehler /** 87513b16484SAndreas Boehler * Enable a calendar for a given page 87613b16484SAndreas Boehler * 87713b16484SAndreas Boehler * @param string $id The page ID 87813b16484SAndreas Boehler * 87913b16484SAndreas Boehler * @return boolean true on success, otherwise false 88013b16484SAndreas Boehler */ 88113b16484SAndreas Boehler public function enableCalendarForPage($id) 88213b16484SAndreas Boehler { 88313b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 88413b16484SAndreas Boehler if($calid === false) 88513b16484SAndreas Boehler return false; 88613b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 0 WHERE id = ?"; 88713b16484SAndreas Boehler $res = $this->sqlite->query($query, $calid); 88813b16484SAndreas Boehler if($res !== false) 88913b16484SAndreas Boehler return true; 89013b16484SAndreas Boehler return false; 89113b16484SAndreas Boehler } 89213b16484SAndreas Boehler 89313b16484SAndreas Boehler /** 894cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 895cb71a62aSAndreas Boehler * based on the timezone setting. 896cb71a62aSAndreas Boehler * 897cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 898cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 899cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 900cb71a62aSAndreas Boehler * 901cb71a62aSAndreas Boehler * @param string $id The page ID to work with 902cb71a62aSAndreas Boehler * @param string $user The user ID to work with 903cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 904cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 9054a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 906cb71a62aSAndreas Boehler * 907cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 908cb71a62aSAndreas Boehler */ 9094a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 910a1a3b679SAndreas Boehler { 91182a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 91282a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 913bd883736SAndreas Boehler else 914bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 915a1a3b679SAndreas Boehler $data = array(); 916cb71a62aSAndreas Boehler 917a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 918a469597cSAndreas Boehler $startTs = null; 919a469597cSAndreas Boehler $endTs = null; 920a469597cSAndreas Boehler if($startDate !== null) 921a469597cSAndreas Boehler { 922a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 923a469597cSAndreas Boehler $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp()); 924a469597cSAndreas Boehler } 925a469597cSAndreas Boehler if($endDate !== null) 926a469597cSAndreas Boehler { 927a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 928a469597cSAndreas Boehler $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp()); 929a469597cSAndreas Boehler } 930cb71a62aSAndreas Boehler 9310b805092SAndreas Boehler // Load SabreDAV 9320b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 9330b805092SAndreas Boehler 9340b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 9350b805092SAndreas Boehler { 9360b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 9370b805092SAndreas Boehler if(is_null($wdc)) 9380b805092SAndreas Boehler return $data; 9390b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 9400b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 9410b805092SAndreas Boehler } 9420b805092SAndreas Boehler else 9430b805092SAndreas Boehler { 9440b805092SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 9450b805092SAndreas Boehler if(is_null($color)) 9460b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 9470b805092SAndreas Boehler 948*59b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 949*59b68239SAndreas Boehler if($enabled === false) 950*59b68239SAndreas Boehler return $data; 951*59b68239SAndreas Boehler 952cb71a62aSAndreas Boehler // Retrieve matching calendar objects 953a469597cSAndreas Boehler $res = $this->sqlite->query($query, $calid); 954a1a3b679SAndreas Boehler $arr = $this->sqlite->res2arr($res); 9550b805092SAndreas Boehler } 956cb71a62aSAndreas Boehler 957cb71a62aSAndreas Boehler // Parse individual calendar entries 958a1a3b679SAndreas Boehler foreach($arr as $row) 959a1a3b679SAndreas Boehler { 960a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 961a1a3b679SAndreas Boehler { 962b269830cSAndreas Boehler $entry = array(); 963a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 964ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 965cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 966ebc4eb57SAndreas Boehler if($recurrence != null) 967ebc4eb57SAndreas Boehler { 968ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 969ebc4eb57SAndreas Boehler $rEvents->rewind(); 970e9b7d302SAndreas Boehler while($rEvents->valid()) 971ebc4eb57SAndreas Boehler { 972ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 973cb71a62aSAndreas Boehler // If we are after the given time range, exit 974a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 975e9b7d302SAndreas Boehler break; 976cb71a62aSAndreas Boehler 977cb71a62aSAndreas Boehler // If we are before the given time range, continue 978a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 979ebc4eb57SAndreas Boehler { 980ebc4eb57SAndreas Boehler $rEvents->next(); 981ebc4eb57SAndreas Boehler continue; 982ebc4eb57SAndreas Boehler } 983cb71a62aSAndreas Boehler 984cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 985185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true); 986ebc4eb57SAndreas Boehler $rEvents->next(); 987ebc4eb57SAndreas Boehler } 988ebc4eb57SAndreas Boehler } 989ebc4eb57SAndreas Boehler else 990185e2535SAndreas Boehler $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color); 991ebc4eb57SAndreas Boehler } 992ebc4eb57SAndreas Boehler } 993ebc4eb57SAndreas Boehler return $data; 994ebc4eb57SAndreas Boehler } 995ebc4eb57SAndreas Boehler 996cb71a62aSAndreas Boehler /** 997cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 998cb71a62aSAndreas Boehler * 999cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 1000cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 1001cb71a62aSAndreas Boehler * @param string $uid The entry's UID 10023c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 1003cb71a62aSAndreas Boehler * 1004cb71a62aSAndreas Boehler * @return array The parse calendar entry 1005cb71a62aSAndreas Boehler */ 1006185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 1007ebc4eb57SAndreas Boehler { 1008ebc4eb57SAndreas Boehler $entry = array(); 1009ebc4eb57SAndreas Boehler $start = $event->DTSTART; 1010cb71a62aSAndreas Boehler // Parse only if the start date/time is present 1011b269830cSAndreas Boehler if($start !== null) 1012b269830cSAndreas Boehler { 1013b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 1014b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 1015bf0ad2b4SAndreas Boehler 1016bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 1017bf0ad2b4SAndreas Boehler // allDay is set to true 1018bf0ad2b4SAndreas Boehler // This should fix T23 1019b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 1020bf0ad2b4SAndreas Boehler { 1021b269830cSAndreas Boehler $entry['allDay'] = true; 1022bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 1023bf0ad2b4SAndreas Boehler } 1024b269830cSAndreas Boehler else 1025bf0ad2b4SAndreas Boehler { 1026b269830cSAndreas Boehler $entry['allDay'] = false; 1027bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 1028bf0ad2b4SAndreas Boehler } 1029b269830cSAndreas Boehler } 1030ebc4eb57SAndreas Boehler $end = $event->DTEND; 1031bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 1032b269830cSAndreas Boehler if($end !== null) 1033b269830cSAndreas Boehler { 1034b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 1035b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 1036bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 1037bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 1038bf0ad2b4SAndreas Boehler else 1039b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1040b269830cSAndreas Boehler } 1041ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 10420eebc909SAndreas Boehler if($description !== null) 10430eebc909SAndreas Boehler $entry['description'] = (string)$description; 10440eebc909SAndreas Boehler else 10450eebc909SAndreas Boehler $entry['description'] = ''; 10464ecb526cSAndreas Boehler $attachments = $event->ATTACH; 10474ecb526cSAndreas Boehler if($attachments !== null) 10484ecb526cSAndreas Boehler { 10494ecb526cSAndreas Boehler $entry['attachments'] = array(); 10504ecb526cSAndreas Boehler foreach($attachments as $attachment) 10514ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 10524ecb526cSAndreas Boehler } 1053ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 10542b7be5bdSAndreas Boehler $entry['location'] = (string)$event->location; 1055ebc4eb57SAndreas Boehler $entry['id'] = $uid; 1056185e2535SAndreas Boehler $entry['page'] = $page; 1057185e2535SAndreas Boehler $entry['color'] = $color; 10583c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 1059185e2535SAndreas Boehler 1060ebc4eb57SAndreas Boehler return $entry; 1061a1a3b679SAndreas Boehler } 1062a1a3b679SAndreas Boehler 1063cb71a62aSAndreas Boehler /** 1064cb71a62aSAndreas Boehler * Retrieve an event by its UID 1065cb71a62aSAndreas Boehler * 1066cb71a62aSAndreas Boehler * @param string $uid The event's UID 1067cb71a62aSAndreas Boehler * 1068cb71a62aSAndreas Boehler * @return mixed The table row with the given event 1069cb71a62aSAndreas Boehler */ 1070a1a3b679SAndreas Boehler public function getEventWithUid($uid) 1071a1a3b679SAndreas Boehler { 107251f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 107351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 1074a1a3b679SAndreas Boehler $row = $this->sqlite->res2row($res); 1075a1a3b679SAndreas Boehler return $row; 1076a1a3b679SAndreas Boehler } 1077a1a3b679SAndreas Boehler 1078cb71a62aSAndreas Boehler /** 1079d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 1080*59b68239SAndreas Boehler * calendar data! This is mainly needed for the sync support. 1081d5703f5aSAndreas Boehler * 1082d5703f5aSAndreas Boehler * @param int $calid The calendar ID 1083d5703f5aSAndreas Boehler * 1084d5703f5aSAndreas Boehler * @return mixed The result 1085d5703f5aSAndreas Boehler */ 1086d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 1087d5703f5aSAndreas Boehler { 1088d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 1089d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1090d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1091d5703f5aSAndreas Boehler return $arr; 1092d5703f5aSAndreas Boehler } 1093d5703f5aSAndreas Boehler 1094d5703f5aSAndreas Boehler /** 1095d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 1096d5703f5aSAndreas Boehler * 1097d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1098d5703f5aSAndreas Boehler * @param string $uri The object's URI 1099d5703f5aSAndreas Boehler * 1100d5703f5aSAndreas Boehler * @return mixed The result 1101d5703f5aSAndreas Boehler */ 1102d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1103d5703f5aSAndreas Boehler { 1104d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1105d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1106d5703f5aSAndreas Boehler $row = $this->sqlite->res2row($res); 1107d5703f5aSAndreas Boehler return $row; 1108d5703f5aSAndreas Boehler } 1109d5703f5aSAndreas Boehler 1110d5703f5aSAndreas Boehler /** 1111d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1112d5703f5aSAndreas Boehler * This is mainly neede for sync. 1113d5703f5aSAndreas Boehler * 1114d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1115d5703f5aSAndreas Boehler * @param array $uris An array of URIs 1116d5703f5aSAndreas Boehler * 1117d5703f5aSAndreas Boehler * @return mixed The result 1118d5703f5aSAndreas Boehler */ 1119d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1120d5703f5aSAndreas Boehler { 1121d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1122d5703f5aSAndreas Boehler // Inserting a whole bunch of question marks 1123d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1124d5703f5aSAndreas Boehler $query .= ')'; 1125d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 1126d5703f5aSAndreas Boehler 1127d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $vals); 1128d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1129d5703f5aSAndreas Boehler return $arr; 1130d5703f5aSAndreas Boehler } 1131d5703f5aSAndreas Boehler 1132d5703f5aSAndreas Boehler /** 1133cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1134cb71a62aSAndreas Boehler * 1135cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1136cb71a62aSAndreas Boehler * 1137cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1138cb71a62aSAndreas Boehler */ 1139f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1140f69bb449SAndreas Boehler { 1141*59b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 1142*59b68239SAndreas Boehler if($enabled === false) 1143*59b68239SAndreas Boehler return false; 11447e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 114551f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1146f69bb449SAndreas Boehler $arr = $this->sqlite->res2arr($res); 1147f69bb449SAndreas Boehler return $arr; 1148f69bb449SAndreas Boehler } 1149f69bb449SAndreas Boehler 1150cb71a62aSAndreas Boehler /** 1151cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1152cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1153cb71a62aSAndreas Boehler * 1154cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1155cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1156cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1157cb71a62aSAndreas Boehler * 1158cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1159cb71a62aSAndreas Boehler */ 1160a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1161a1a3b679SAndreas Boehler { 116282a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 116382a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 116482a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1165a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1166bd883736SAndreas Boehler else 1167bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1168cb71a62aSAndreas Boehler 1169cb71a62aSAndreas Boehler // Parse dates 1170b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1171b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1172b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1173b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1174cb71a62aSAndreas Boehler 1175cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 117655a741c0SAndreas Boehler $uid = $params['uid']; 1177809cb0faSAndreas Boehler 1178809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1179809cb0faSAndreas Boehler { 1180809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1181809cb0faSAndreas Boehler if(is_null($wdc)) 1182809cb0faSAndreas Boehler return false; 1183809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1184809cb0faSAndreas Boehler } 1185809cb0faSAndreas Boehler else 1186809cb0faSAndreas Boehler { 118755a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1188809cb0faSAndreas Boehler } 1189cb71a62aSAndreas Boehler 1190cb71a62aSAndreas Boehler // Load SabreDAV 11919bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1192a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1193a1a3b679SAndreas Boehler return false; 119455a741c0SAndreas Boehler $uri = $event['uri']; 119555a741c0SAndreas Boehler $calid = $event['calendarid']; 1196cb71a62aSAndreas Boehler 1197cb71a62aSAndreas Boehler // Parse the existing event 1198a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1199b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1200cb71a62aSAndreas Boehler 1201cb71a62aSAndreas Boehler // Set the new event values 1202b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1203b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 12040eebc909SAndreas Boehler $description = $params['eventdescription']; 12052b7be5bdSAndreas Boehler $location = $params['eventlocation']; 1206cb71a62aSAndreas Boehler 1207cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 12080eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1209b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1210b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 12114ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 12122b7be5bdSAndreas Boehler $vevent->remove('LOCATION'); 1213cb71a62aSAndreas Boehler 12142b7be5bdSAndreas Boehler // Add new time stamps, description and location 1215b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1216b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 12170eebc909SAndreas Boehler if($description !== '') 12180eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 12192b7be5bdSAndreas Boehler if($location !== '') 12202b7be5bdSAndreas Boehler $vevent->add('LOCATION', $location); 1221cb71a62aSAndreas Boehler 12224ecb526cSAndreas Boehler // Add attachments 12234ecb526cSAndreas Boehler $attachments = $params['attachments']; 122482a48dfbSAndreas Boehler if(!is_null($attachments)) 12254ecb526cSAndreas Boehler foreach($attachments as $attachment) 12264ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 12274ecb526cSAndreas Boehler 1228cb71a62aSAndreas Boehler // Setup DTSTART 1229b269830cSAndreas Boehler $dtStart = new \DateTime(); 1230a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1231b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1232b269830cSAndreas Boehler if($params['allday'] != '1') 1233b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1234cb71a62aSAndreas Boehler 12354ecb526cSAndreas Boehler // Setup DTEND 1236b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1237a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1238b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1239b269830cSAndreas Boehler if($params['allday'] != '1') 1240b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1241cb71a62aSAndreas Boehler 1242b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1243b269830cSAndreas Boehler if($params['allday'] == '1') 1244b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1245b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1246b269830cSAndreas Boehler $vevent->remove('DTEND'); 1247b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1248b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1249cb71a62aSAndreas Boehler 1250cb71a62aSAndreas Boehler // Remove the time for allday events 1251b269830cSAndreas Boehler if($params['allday'] == '1') 1252b269830cSAndreas Boehler { 1253b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1254b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1255b269830cSAndreas Boehler } 1256a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1257809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1258809cb0faSAndreas Boehler { 1259809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1260809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1261809cb0faSAndreas Boehler } 1262809cb0faSAndreas Boehler else 1263809cb0faSAndreas Boehler { 1264809cb0faSAndreas Boehler $now = new DateTime(); 1265cb71a62aSAndreas Boehler // Actually write to the database 126651f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 126751f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 126851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 126951f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 127055a741c0SAndreas Boehler if($res !== false) 127155a741c0SAndreas Boehler { 127255a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1273a1a3b679SAndreas Boehler return true; 1274a1a3b679SAndreas Boehler } 1275809cb0faSAndreas Boehler } 127655a741c0SAndreas Boehler return false; 127755a741c0SAndreas Boehler } 1278a1a3b679SAndreas Boehler 1279cb71a62aSAndreas Boehler /** 1280d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1281d5703f5aSAndreas Boehler * 1282d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1283d5703f5aSAndreas Boehler * @param string $uri The object's URI 1284d5703f5aSAndreas Boehler * 1285d5703f5aSAndreas Boehler * @return true 1286d5703f5aSAndreas Boehler */ 1287d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1288d5703f5aSAndreas Boehler { 1289d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 1290d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $calid, $uri); 1291d5703f5aSAndreas Boehler if($res !== false) 1292d5703f5aSAndreas Boehler { 1293d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1294d5703f5aSAndreas Boehler } 1295d5703f5aSAndreas Boehler return true; 1296d5703f5aSAndreas Boehler } 1297d5703f5aSAndreas Boehler 1298d5703f5aSAndreas Boehler /** 1299cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1300cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1301cb71a62aSAndreas Boehler * 1302cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1303cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1304cb71a62aSAndreas Boehler * 1305cb71a62aSAndreas Boehler * @return boolean True 1306cb71a62aSAndreas Boehler */ 1307a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1308a1a3b679SAndreas Boehler { 1309a1a3b679SAndreas Boehler $uid = $params['uid']; 1310809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1311809cb0faSAndreas Boehler { 1312809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1313809cb0faSAndreas Boehler if(is_null($wdc)) 1314809cb0faSAndreas Boehler return false; 1315809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1316809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1317809cb0faSAndreas Boehler return $result; 1318809cb0faSAndreas Boehler } 131955a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 13202c14b82bSAndreas Boehler $calid = $event['calendarid']; 132155a741c0SAndreas Boehler $uri = $event['uri']; 132251f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 132351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uid); 132455a741c0SAndreas Boehler if($res !== false) 132555a741c0SAndreas Boehler { 132655a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 132755a741c0SAndreas Boehler } 1328a1a3b679SAndreas Boehler return true; 1329a1a3b679SAndreas Boehler } 1330a1a3b679SAndreas Boehler 1331cb71a62aSAndreas Boehler /** 1332cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1333cb71a62aSAndreas Boehler * 1334cb71a62aSAndreas Boehler * @param string $calid The calendar id 1335cb71a62aSAndreas Boehler * 1336cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1337cb71a62aSAndreas Boehler */ 133855a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 133955a741c0SAndreas Boehler { 1340b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 134155a741c0SAndreas Boehler if(isset($row['synctoken'])) 134255a741c0SAndreas Boehler return $row['synctoken']; 134355a741c0SAndreas Boehler return false; 134455a741c0SAndreas Boehler } 134555a741c0SAndreas Boehler 1346cb71a62aSAndreas Boehler /** 1347cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1348cb71a62aSAndreas Boehler * an operation code as stored in the database 1349cb71a62aSAndreas Boehler * 1350cb71a62aSAndreas Boehler * @param string $operationName The operation name 1351cb71a62aSAndreas Boehler * 1352cb71a62aSAndreas Boehler * @return mixed The operation code or false 1353cb71a62aSAndreas Boehler */ 135455a741c0SAndreas Boehler public function operationNameToOperation($operationName) 135555a741c0SAndreas Boehler { 135655a741c0SAndreas Boehler switch($operationName) 135755a741c0SAndreas Boehler { 135855a741c0SAndreas Boehler case 'added': 135955a741c0SAndreas Boehler return 1; 136055a741c0SAndreas Boehler break; 136155a741c0SAndreas Boehler case 'modified': 136255a741c0SAndreas Boehler return 2; 136355a741c0SAndreas Boehler break; 136455a741c0SAndreas Boehler case 'deleted': 136555a741c0SAndreas Boehler return 3; 136655a741c0SAndreas Boehler break; 136755a741c0SAndreas Boehler } 136855a741c0SAndreas Boehler return false; 136955a741c0SAndreas Boehler } 137055a741c0SAndreas Boehler 1371cb71a62aSAndreas Boehler /** 1372cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1373cb71a62aSAndreas Boehler * operation that was performed. 1374cb71a62aSAndreas Boehler * 1375cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1376cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1377cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1378cb71a62aSAndreas Boehler * 1379cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1380cb71a62aSAndreas Boehler */ 138155a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 138255a741c0SAndreas Boehler { 138355a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 138455a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 138555a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 138655a741c0SAndreas Boehler return false; 138755a741c0SAndreas Boehler $values = array($uri, 138855a741c0SAndreas Boehler $currentToken, 138955a741c0SAndreas Boehler $calid, 139055a741c0SAndreas Boehler $operationCode 139155a741c0SAndreas Boehler ); 139251f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 139351f4febbSAndreas Boehler $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 139455a741c0SAndreas Boehler if($res === false) 139555a741c0SAndreas Boehler return false; 139655a741c0SAndreas Boehler $currentToken++; 139751f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 139851f4febbSAndreas Boehler $res = $this->sqlite->query($query, $currentToken, $calid); 139955a741c0SAndreas Boehler return ($res !== false); 140055a741c0SAndreas Boehler } 140155a741c0SAndreas Boehler 1402cb71a62aSAndreas Boehler /** 1403cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1404cb71a62aSAndreas Boehler * 1405cb71a62aSAndreas Boehler * @param string $id The page's ID 1406cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1407cb71a62aSAndreas Boehler * 1408cb71a62aSAndreas Boehler * @return mixed The sync url or false 1409cb71a62aSAndreas Boehler */ 1410b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1411b269830cSAndreas Boehler { 141234a47953SAndreas Boehler if(is_null($userid)) 141334a47953SAndreas Boehler { 141434a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 141534a47953SAndreas Boehler { 141634a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 141734a47953SAndreas Boehler } 141834a47953SAndreas Boehler else 141934a47953SAndreas Boehler { 142034a47953SAndreas Boehler return false; 142134a47953SAndreas Boehler } 142234a47953SAndreas Boehler } 1423b269830cSAndreas Boehler 1424b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1425b269830cSAndreas Boehler if($calid === false) 1426b269830cSAndreas Boehler return false; 1427b269830cSAndreas Boehler 1428b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1429b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1430b269830cSAndreas Boehler return false; 1431b269830cSAndreas Boehler 1432b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1433b269830cSAndreas Boehler return $syncurl; 1434b269830cSAndreas Boehler } 1435b269830cSAndreas Boehler 1436cb71a62aSAndreas Boehler /** 1437cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1438cb71a62aSAndreas Boehler * 1439cb71a62aSAndreas Boehler * @param string $id the page ID 1440cb71a62aSAndreas Boehler * 1441cb71a62aSAndreas Boehler * @return mixed The private URL or false 1442cb71a62aSAndreas Boehler */ 1443f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1444f69bb449SAndreas Boehler { 1445f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1446f69bb449SAndreas Boehler if($calid === false) 1447f69bb449SAndreas Boehler return false; 1448f69bb449SAndreas Boehler 1449f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1450f69bb449SAndreas Boehler } 1451f69bb449SAndreas Boehler 1452cb71a62aSAndreas Boehler /** 1453cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1454cb71a62aSAndreas Boehler * 1455cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1456cb71a62aSAndreas Boehler * 1457cb71a62aSAndreas Boehler * @return mixed The private URL or false 1458cb71a62aSAndreas Boehler */ 1459f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1460f69bb449SAndreas Boehler { 1461185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1462185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 146351f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 146451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $calid); 1465f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1466f69bb449SAndreas Boehler if(!isset($row['url'])) 1467f69bb449SAndreas Boehler { 1468f69bb449SAndreas Boehler $url = uniqid("dokuwiki-").".ics"; 146951f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 147051f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url, $calid); 1471f69bb449SAndreas Boehler if($res === false) 1472f69bb449SAndreas Boehler return false; 1473f69bb449SAndreas Boehler } 1474f69bb449SAndreas Boehler else 1475f69bb449SAndreas Boehler { 1476f69bb449SAndreas Boehler $url = $row['url']; 1477f69bb449SAndreas Boehler } 1478185e2535SAndreas Boehler 1479185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1480185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1481185e2535SAndreas Boehler return $url; 1482f69bb449SAndreas Boehler } 1483f69bb449SAndreas Boehler 1484cb71a62aSAndreas Boehler /** 1485cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1486cb71a62aSAndreas Boehler * 1487cb71a62aSAndreas Boehler * @param string $url The private URL 1488cb71a62aSAndreas Boehler * 1489cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1490cb71a62aSAndreas Boehler */ 1491f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1492f69bb449SAndreas Boehler { 149351f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 149451f4febbSAndreas Boehler $res = $this->sqlite->query($query, $url); 1495f69bb449SAndreas Boehler $row = $this->sqlite->res2row($res); 1496f69bb449SAndreas Boehler if(!isset($row['calid'])) 1497f69bb449SAndreas Boehler return false; 1498f69bb449SAndreas Boehler return $row['calid']; 1499f69bb449SAndreas Boehler } 1500f69bb449SAndreas Boehler 1501cb71a62aSAndreas Boehler /** 1502cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1503cb71a62aSAndreas Boehler * 15047e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1505cb71a62aSAndreas Boehler * 1506cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1507cb71a62aSAndreas Boehler */ 1508f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1509f69bb449SAndreas Boehler { 1510f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1511f69bb449SAndreas Boehler if($calSettings === false) 1512f69bb449SAndreas Boehler return false; 1513f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1514f69bb449SAndreas Boehler if($events === false) 1515f69bb449SAndreas Boehler return false; 1516f69bb449SAndreas Boehler 15177e0b8590SAndreas Boehler // Load SabreDAV 15187e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 15197e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 15207e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1521f69bb449SAndreas Boehler foreach($events as $event) 1522f69bb449SAndreas Boehler { 15237e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 15247e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 15257e0b8590SAndreas Boehler $out .= $evt->serialize(); 1526f69bb449SAndreas Boehler } 15277e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1528f69bb449SAndreas Boehler return $out; 1529f69bb449SAndreas Boehler } 1530f69bb449SAndreas Boehler 15317c7c6b0bSAndreas Boehler /** 15327c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 15337c7c6b0bSAndreas Boehler * 15347c7c6b0bSAndreas Boehler * @param string $key The key to query 153521d04f73SAndreas Boehler * @return mixed The option set, null if not found 15367c7c6b0bSAndreas Boehler */ 15377c7c6b0bSAndreas Boehler public function getConfig($key) 15387c7c6b0bSAndreas Boehler { 15397c7c6b0bSAndreas Boehler return $this->getConf($key); 15407c7c6b0bSAndreas Boehler } 15417c7c6b0bSAndreas Boehler 1542d5703f5aSAndreas Boehler /** 1543d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1544d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1545d5703f5aSAndreas Boehler * 1546d5703f5aSAndreas Boehler * Returns an array with the following keys: 1547d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1548d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1549d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1550d5703f5aSAndreas Boehler * * firstOccurence 1551d5703f5aSAndreas Boehler * * lastOccurence 1552d5703f5aSAndreas Boehler * * uid - value of the UID property 1553d5703f5aSAndreas Boehler * 1554d5703f5aSAndreas Boehler * @param string $calendarData 1555d5703f5aSAndreas Boehler * @return array 1556d5703f5aSAndreas Boehler */ 1557d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1558d5703f5aSAndreas Boehler { 1559d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1560d5703f5aSAndreas Boehler 1561d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1562d5703f5aSAndreas Boehler $componentType = null; 1563d5703f5aSAndreas Boehler $component = null; 1564d5703f5aSAndreas Boehler $firstOccurence = null; 1565d5703f5aSAndreas Boehler $lastOccurence = null; 1566d5703f5aSAndreas Boehler $uid = null; 1567d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1568d5703f5aSAndreas Boehler { 1569d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1570d5703f5aSAndreas Boehler { 1571d5703f5aSAndreas Boehler $componentType = $component->name; 1572d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1573d5703f5aSAndreas Boehler break; 1574d5703f5aSAndreas Boehler } 1575d5703f5aSAndreas Boehler } 1576d5703f5aSAndreas Boehler if (!$componentType) 1577d5703f5aSAndreas Boehler { 1578d5703f5aSAndreas Boehler return false; 1579d5703f5aSAndreas Boehler } 1580d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1581d5703f5aSAndreas Boehler { 1582d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1583d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1584d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1585d5703f5aSAndreas Boehler { 1586d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1587d5703f5aSAndreas Boehler { 1588d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1589d5703f5aSAndreas Boehler } 1590d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1591d5703f5aSAndreas Boehler { 1592d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1593d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1594d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1595d5703f5aSAndreas Boehler } 1596d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1597d5703f5aSAndreas Boehler { 1598d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1599d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1600d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1601d5703f5aSAndreas Boehler } 1602d5703f5aSAndreas Boehler else 1603d5703f5aSAndreas Boehler { 1604d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1605d5703f5aSAndreas Boehler } 1606d5703f5aSAndreas Boehler } 1607d5703f5aSAndreas Boehler else 1608d5703f5aSAndreas Boehler { 1609d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1610d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1611d5703f5aSAndreas Boehler if ($it->isInfinite()) 1612d5703f5aSAndreas Boehler { 1613d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1614d5703f5aSAndreas Boehler } 1615d5703f5aSAndreas Boehler else 1616d5703f5aSAndreas Boehler { 1617d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1618d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1619d5703f5aSAndreas Boehler { 1620d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1621d5703f5aSAndreas Boehler $it->next(); 1622d5703f5aSAndreas Boehler } 1623d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1624d5703f5aSAndreas Boehler } 1625d5703f5aSAndreas Boehler } 1626d5703f5aSAndreas Boehler } 1627d5703f5aSAndreas Boehler 1628d5703f5aSAndreas Boehler return array( 1629d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1630d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1631d5703f5aSAndreas Boehler 'componentType' => $componentType, 1632d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1633d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1634d5703f5aSAndreas Boehler 'uid' => $uid, 1635d5703f5aSAndreas Boehler ); 1636d5703f5aSAndreas Boehler 1637d5703f5aSAndreas Boehler } 1638d5703f5aSAndreas Boehler 1639d5703f5aSAndreas Boehler /** 1640d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1641d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1642d5703f5aSAndreas Boehler * 1643d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1644d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1645d5703f5aSAndreas Boehler * 1646d5703f5aSAndreas Boehler * @return mixed The result 1647d5703f5aSAndreas Boehler */ 1648d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1649d5703f5aSAndreas Boehler { 165039787131SAndreas Boehler dbglog('davcal::helper::calendarQuery'); 1651d5703f5aSAndreas Boehler $componentType = null; 1652d5703f5aSAndreas Boehler $requirePostFilter = true; 1653d5703f5aSAndreas Boehler $timeRange = null; 1654d5703f5aSAndreas Boehler 1655d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1656d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1657d5703f5aSAndreas Boehler { 1658d5703f5aSAndreas Boehler $requirePostFilter = false; 1659d5703f5aSAndreas Boehler } 1660d5703f5aSAndreas Boehler 1661d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1662d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1663d5703f5aSAndreas Boehler { 1664d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1665d5703f5aSAndreas Boehler 1666d5703f5aSAndreas Boehler // Checking if we need post-filters 1667d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1668d5703f5aSAndreas Boehler { 1669d5703f5aSAndreas Boehler $requirePostFilter = false; 1670d5703f5aSAndreas Boehler } 1671d5703f5aSAndreas Boehler // There was a time-range filter 1672d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1673d5703f5aSAndreas Boehler { 1674d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1675d5703f5aSAndreas Boehler 1676d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1677d5703f5aSAndreas Boehler // 100% accurate mysql query. 1678d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1679d5703f5aSAndreas Boehler { 1680d5703f5aSAndreas Boehler $requirePostFilter = false; 1681d5703f5aSAndreas Boehler } 1682d5703f5aSAndreas Boehler } 1683d5703f5aSAndreas Boehler 1684d5703f5aSAndreas Boehler } 1685d5703f5aSAndreas Boehler 1686d5703f5aSAndreas Boehler if ($requirePostFilter) 1687d5703f5aSAndreas Boehler { 1688d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1689d5703f5aSAndreas Boehler } 1690d5703f5aSAndreas Boehler else 1691d5703f5aSAndreas Boehler { 1692d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1693d5703f5aSAndreas Boehler } 1694d5703f5aSAndreas Boehler 1695d5703f5aSAndreas Boehler $values = array( 1696d5703f5aSAndreas Boehler $calendarId 1697d5703f5aSAndreas Boehler ); 1698d5703f5aSAndreas Boehler 1699d5703f5aSAndreas Boehler if ($componentType) 1700d5703f5aSAndreas Boehler { 1701d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1702d5703f5aSAndreas Boehler $values[] = $componentType; 1703d5703f5aSAndreas Boehler } 1704d5703f5aSAndreas Boehler 1705d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1706d5703f5aSAndreas Boehler { 1707d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1708d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1709d5703f5aSAndreas Boehler } 1710d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1711d5703f5aSAndreas Boehler { 1712d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1713d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1714d5703f5aSAndreas Boehler } 1715d5703f5aSAndreas Boehler 1716d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $values); 1717d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1718d5703f5aSAndreas Boehler 1719d5703f5aSAndreas Boehler $result = array(); 1720d5703f5aSAndreas Boehler foreach($arr as $row) 1721d5703f5aSAndreas Boehler { 1722d5703f5aSAndreas Boehler if ($requirePostFilter) 1723d5703f5aSAndreas Boehler { 1724d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1725d5703f5aSAndreas Boehler { 1726d5703f5aSAndreas Boehler continue; 1727d5703f5aSAndreas Boehler } 1728d5703f5aSAndreas Boehler } 1729d5703f5aSAndreas Boehler $result[] = $row['uri']; 1730d5703f5aSAndreas Boehler 1731d5703f5aSAndreas Boehler } 1732d5703f5aSAndreas Boehler 1733d5703f5aSAndreas Boehler return $result; 1734d5703f5aSAndreas Boehler } 1735d5703f5aSAndreas Boehler 1736d5703f5aSAndreas Boehler /** 1737d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1738d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1739d5703f5aSAndreas Boehler * 1740d5703f5aSAndreas Boehler * @param array $object 1741d5703f5aSAndreas Boehler * @param array $filters 1742d5703f5aSAndreas Boehler * @return bool 1743d5703f5aSAndreas Boehler */ 1744d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 1745d5703f5aSAndreas Boehler { 1746d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1747d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1748d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 1749d5703f5aSAndreas Boehler // well. 1750d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 1751d5703f5aSAndreas Boehler { 1752d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1753d5703f5aSAndreas Boehler } 1754d5703f5aSAndreas Boehler 1755d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1756d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1757d5703f5aSAndreas Boehler 175839787131SAndreas Boehler $res = $validator->validate($vObject, $filters); 175939787131SAndreas Boehler return $res; 1760d5703f5aSAndreas Boehler 1761d5703f5aSAndreas Boehler } 1762d5703f5aSAndreas Boehler 1763d5703f5aSAndreas Boehler /** 1764d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1765d5703f5aSAndreas Boehler * 1766d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1767d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 1768d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 1769d5703f5aSAndreas Boehler * @param int $limit The limit of changes 1770d5703f5aSAndreas Boehler * 1771d5703f5aSAndreas Boehler * @return array The result 1772d5703f5aSAndreas Boehler */ 1773d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1774d5703f5aSAndreas Boehler { 1775d5703f5aSAndreas Boehler // Current synctoken 1776d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1777d5703f5aSAndreas Boehler 1778d5703f5aSAndreas Boehler if ($currentToken === false) return null; 1779d5703f5aSAndreas Boehler 1780d5703f5aSAndreas Boehler $result = array( 1781d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 1782d5703f5aSAndreas Boehler 'added' => array(), 1783d5703f5aSAndreas Boehler 'modified' => array(), 1784d5703f5aSAndreas Boehler 'deleted' => array(), 1785d5703f5aSAndreas Boehler ); 1786d5703f5aSAndreas Boehler 1787d5703f5aSAndreas Boehler if ($syncToken) 1788d5703f5aSAndreas Boehler { 1789d5703f5aSAndreas Boehler 1790d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 1791d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 1792d5703f5aSAndreas Boehler 1793d5703f5aSAndreas Boehler // Fetching all changes 1794d5703f5aSAndreas Boehler $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid); 1795d5703f5aSAndreas Boehler if($res === false) 1796d5703f5aSAndreas Boehler return null; 1797d5703f5aSAndreas Boehler 1798d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1799d5703f5aSAndreas Boehler $changes = array(); 1800d5703f5aSAndreas Boehler 1801d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 1802d5703f5aSAndreas Boehler // last change on a node is relevant. 1803d5703f5aSAndreas Boehler foreach($arr as $row) 1804d5703f5aSAndreas Boehler { 1805d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 1806d5703f5aSAndreas Boehler } 1807d5703f5aSAndreas Boehler 1808d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 1809d5703f5aSAndreas Boehler { 1810d5703f5aSAndreas Boehler switch ($operation) 1811d5703f5aSAndreas Boehler { 1812d5703f5aSAndreas Boehler case 1 : 1813d5703f5aSAndreas Boehler $result['added'][] = $uri; 1814d5703f5aSAndreas Boehler break; 1815d5703f5aSAndreas Boehler case 2 : 1816d5703f5aSAndreas Boehler $result['modified'][] = $uri; 1817d5703f5aSAndreas Boehler break; 1818d5703f5aSAndreas Boehler case 3 : 1819d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 1820d5703f5aSAndreas Boehler break; 1821d5703f5aSAndreas Boehler } 1822d5703f5aSAndreas Boehler 1823d5703f5aSAndreas Boehler } 1824d5703f5aSAndreas Boehler } 1825d5703f5aSAndreas Boehler else 1826d5703f5aSAndreas Boehler { 1827d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 1828d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1829d5703f5aSAndreas Boehler $res = $this->sqlite->query($query); 1830d5703f5aSAndreas Boehler $arr = $this->sqlite->res2arr($res); 1831d5703f5aSAndreas Boehler 1832d5703f5aSAndreas Boehler $result['added'] = $arr; 1833d5703f5aSAndreas Boehler } 1834d5703f5aSAndreas Boehler return $result; 1835d5703f5aSAndreas Boehler } 1836d5703f5aSAndreas Boehler 1837a1a3b679SAndreas Boehler} 1838