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 */ 19306344e5SGerrit Uitslag public function __construct() { 20*c42afaebSscottleechua \dokuwiki\Logger::debug('DAVCAL', 'Helper initialized', __FILE__, __LINE__); 215f2c3e2dSAndreas Boehler } 225f2c3e2dSAndreas Boehler 235f2c3e2dSAndreas Boehler /** Establish and initialize the database if not already done 245f2c3e2dSAndreas Boehler * @return sqlite interface or false 255f2c3e2dSAndreas Boehler */ 265f2c3e2dSAndreas Boehler private function getDB() 275f2c3e2dSAndreas Boehler { 285f2c3e2dSAndreas Boehler if($this->sqlite === null) 295f2c3e2dSAndreas Boehler { 305f2c3e2dSAndreas Boehler $this->sqlite = plugin_load('helper', 'sqlite'); 31a1a3b679SAndreas Boehler if(!$this->sqlite) 32a1a3b679SAndreas Boehler { 33*c42afaebSscottleechua \dokuwiki\Logger::error('DAVCAL', 'This plugin requires the sqlite plugin. Please install it.', __FILE__, __LINE__); 345f2c3e2dSAndreas Boehler msg('This plugin requires the sqlite plugin. Please install it.', -1); 355f2c3e2dSAndreas Boehler return false; 36a1a3b679SAndreas Boehler } 37a1a3b679SAndreas Boehler if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/')) 38a1a3b679SAndreas Boehler { 395f2c3e2dSAndreas Boehler $this->sqlite = null; 40*c42afaebSscottleechua \dokuwiki\Logger::error('DAVCAL', 'Error initialising the SQLite DB for DAVCal', __FILE__, __LINE__); 415f2c3e2dSAndreas Boehler return false; 42a1a3b679SAndreas Boehler } 43a1a3b679SAndreas Boehler } 445f2c3e2dSAndreas Boehler return $this->sqlite; 455f2c3e2dSAndreas Boehler } 46a1a3b679SAndreas Boehler 47cb71a62aSAndreas Boehler /** 48185e2535SAndreas Boehler * Retrieve meta data for a given page 49185e2535SAndreas Boehler * 50185e2535SAndreas Boehler * @param string $id optional The page ID 51185e2535SAndreas Boehler * @return array The metadata 52185e2535SAndreas Boehler */ 53185e2535SAndreas Boehler private function getMeta($id = null) { 54185e2535SAndreas Boehler global $ID; 55185e2535SAndreas Boehler global $INFO; 56185e2535SAndreas Boehler 57185e2535SAndreas Boehler if ($id === null) $id = $ID; 58185e2535SAndreas Boehler 59185e2535SAndreas Boehler if($ID === $id && $INFO['meta']) { 60185e2535SAndreas Boehler $meta = $INFO['meta']; 61185e2535SAndreas Boehler } else { 62185e2535SAndreas Boehler $meta = p_get_metadata($id); 63185e2535SAndreas Boehler } 64185e2535SAndreas Boehler 65185e2535SAndreas Boehler return $meta; 66185e2535SAndreas Boehler } 67185e2535SAndreas Boehler 68185e2535SAndreas Boehler /** 69185e2535SAndreas Boehler * Retrieve the meta data for a given page 70185e2535SAndreas Boehler * 71185e2535SAndreas Boehler * @param string $id optional The page ID 72185e2535SAndreas Boehler * @return array with meta data 73185e2535SAndreas Boehler */ 74185e2535SAndreas Boehler public function getCalendarMetaForPage($id = null) 75185e2535SAndreas Boehler { 76185e2535SAndreas Boehler if(is_null($id)) 77185e2535SAndreas Boehler { 78185e2535SAndreas Boehler global $ID; 79185e2535SAndreas Boehler $id = $ID; 80185e2535SAndreas Boehler } 81185e2535SAndreas Boehler 82185e2535SAndreas Boehler $meta = $this->getMeta($id); 83185e2535SAndreas Boehler if(isset($meta['plugin_davcal'])) 84185e2535SAndreas Boehler return $meta['plugin_davcal']; 85185e2535SAndreas Boehler else 86185e2535SAndreas Boehler return array(); 87185e2535SAndreas Boehler } 88185e2535SAndreas Boehler 89185e2535SAndreas Boehler /** 90d71c9934SAndreas Boehler * Check the permission of a user for a given calendar ID 91d71c9934SAndreas Boehler * 92d71c9934SAndreas Boehler * @param string $id The calendar ID to check 93d71c9934SAndreas Boehler * @return int AUTH_* constants 94d71c9934SAndreas Boehler */ 95d71c9934SAndreas Boehler public function checkCalendarPermission($id) 96d71c9934SAndreas Boehler { 97d4992453SAndreas Boehler if(strpos($id, 'webdav://') === 0) 98d71c9934SAndreas Boehler { 99d71c9934SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 100d71c9934SAndreas Boehler if(is_null($wdc)) 101d71c9934SAndreas Boehler return AUTH_NONE; 102d4992453SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 103d71c9934SAndreas Boehler $settings = $wdc->getConnection($connectionId); 104d71c9934SAndreas Boehler if($settings === false) 105d71c9934SAndreas Boehler return AUTH_NONE; 10658d0e54eSAndreas Boehler // Check if webdavclient has permissions attached to a page 10758d0e54eSAndreas Boehler if(!empty($settings['permission'])) 10858d0e54eSAndreas Boehler { 10958d0e54eSAndreas Boehler $perm = auth_quickaclcheck($settings['permission']); 11058d0e54eSAndreas Boehler // In case the page has more than read permission, but the 11158d0e54eSAndreas Boehler // calendar is read-only, we need to modify the permission here. 11258d0e54eSAndreas Boehler if($perm > AUTH_READ && $settings['write'] == 0) 11358d0e54eSAndreas Boehler $perm = AUTH_READ; 11458d0e54eSAndreas Boehler return $perm; 11558d0e54eSAndreas Boehler } 116d71c9934SAndreas Boehler if($settings['write'] === '1') 117d71c9934SAndreas Boehler return AUTH_CREATE; 118d71c9934SAndreas Boehler return AUTH_READ; 119d71c9934SAndreas Boehler } 120d71c9934SAndreas Boehler else 121d71c9934SAndreas Boehler { 122d71c9934SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 123d71c9934SAndreas Boehler // We return AUTH_READ if the calendar does not exist. This makes 124d71c9934SAndreas Boehler // davcal happy when there are just included calendars 125d71c9934SAndreas Boehler if($calid === false) 126d71c9934SAndreas Boehler return AUTH_READ; 127d71c9934SAndreas Boehler return auth_quickaclcheck($id); 128d71c9934SAndreas Boehler } 129d71c9934SAndreas Boehler } 130d71c9934SAndreas Boehler 131d71c9934SAndreas Boehler /** 13280e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 13380e1ddf7SAndreas Boehler * user has at least read permission. 13480e1ddf7SAndreas Boehler * 13580e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 13680e1ddf7SAndreas Boehler * @return array with filtered calendar pages 13780e1ddf7SAndreas Boehler */ 13880e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 13980e1ddf7SAndreas Boehler { 14080e1ddf7SAndreas Boehler $retList = array(); 14180e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 14280e1ddf7SAndreas Boehler { 14358d0e54eSAndreas Boehler if($this->checkCalendarPermission($page) >= AUTH_READ) 14480e1ddf7SAndreas Boehler { 14580e1ddf7SAndreas Boehler $retList[$page] = $data; 14680e1ddf7SAndreas Boehler } 14780e1ddf7SAndreas Boehler } 14880e1ddf7SAndreas Boehler return $retList; 14980e1ddf7SAndreas Boehler } 15080e1ddf7SAndreas Boehler 15180e1ddf7SAndreas Boehler /** 152185e2535SAndreas Boehler * Get all calendar pages used by a given page 153185e2535SAndreas Boehler * based on the stored metadata 154185e2535SAndreas Boehler * 155185e2535SAndreas Boehler * @param string $id optional The page id 156185e2535SAndreas Boehler * @return mixed The pages as array or false 157185e2535SAndreas Boehler */ 158185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 159185e2535SAndreas Boehler { 160185e2535SAndreas Boehler if(is_null($id)) 161185e2535SAndreas Boehler { 162185e2535SAndreas Boehler global $ID; 163185e2535SAndreas Boehler $id = $ID; 164185e2535SAndreas Boehler } 165185e2535SAndreas Boehler 166185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 1670b805092SAndreas Boehler 168185e2535SAndreas Boehler if(isset($meta['id'])) 169ed764890SAndreas Boehler { 170ed764890SAndreas Boehler // Filter the list of pages by permission 17180e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 17280e1ddf7SAndreas Boehler if(empty($pages)) 173ed764890SAndreas Boehler return false; 17480e1ddf7SAndreas Boehler return $pages; 175ed764890SAndreas Boehler } 176185e2535SAndreas Boehler return false; 177185e2535SAndreas Boehler } 178185e2535SAndreas Boehler 179185e2535SAndreas Boehler /** 180185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 181185e2535SAndreas Boehler * for an array of page ids 182185e2535SAndreas Boehler * 183185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 184185e2535SAndreas Boehler * @return array The list 185185e2535SAndreas Boehler */ 186185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 187185e2535SAndreas Boehler { 188185e2535SAndreas Boehler $data = array(); 1894a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 190185e2535SAndreas Boehler { 1910b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1920b805092SAndreas Boehler { 1930b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1940b805092SAndreas Boehler if(is_null($wdc)) 1950b805092SAndreas Boehler continue; 1960b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1970b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 1982393a702SAndreas Boehler if($settings === false) 1992393a702SAndreas Boehler continue; 2000b805092SAndreas Boehler $name = $settings['displayname']; 201d71c9934SAndreas Boehler $write = ($settings['write'] === '1'); 2020b805092SAndreas Boehler $calid = $connectionId; 203cd2f100dSAndreas Boehler $color = '#3a87ad'; 2040b805092SAndreas Boehler } 2050b805092SAndreas Boehler else 2060b805092SAndreas Boehler { 207185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 208185e2535SAndreas Boehler if($calid !== false) 209185e2535SAndreas Boehler { 210185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 211185e2535SAndreas Boehler $name = $settings['displayname']; 212cd2f100dSAndreas Boehler $color = $settings['calendarcolor']; 213ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 2140b805092SAndreas Boehler } 2150b805092SAndreas Boehler else 2160b805092SAndreas Boehler { 2170b805092SAndreas Boehler continue; 2180b805092SAndreas Boehler } 2190b805092SAndreas Boehler } 220185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 221ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 222185e2535SAndreas Boehler } 223185e2535SAndreas Boehler return $data; 224185e2535SAndreas Boehler } 225185e2535SAndreas Boehler 226185e2535SAndreas Boehler /** 227185e2535SAndreas Boehler * Get the saved calendar color for a given page. 228185e2535SAndreas Boehler * 229185e2535SAndreas Boehler * @param string $id optional The page ID 230185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 231185e2535SAndreas Boehler */ 232185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 233185e2535SAndreas Boehler { 234185e2535SAndreas Boehler if(is_null($id)) 235185e2535SAndreas Boehler { 236185e2535SAndreas Boehler global $ID; 237185e2535SAndreas Boehler $id = $ID; 238185e2535SAndreas Boehler } 239185e2535SAndreas Boehler 240185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 241185e2535SAndreas Boehler if($calid === false) 242185e2535SAndreas Boehler return false; 243185e2535SAndreas Boehler 244185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 245185e2535SAndreas Boehler } 246185e2535SAndreas Boehler 247185e2535SAndreas Boehler /** 248185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 249185e2535SAndreas Boehler * 250185e2535SAndreas Boehler * @param string $id optional The calendar ID 251185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 252185e2535SAndreas Boehler */ 253185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 254185e2535SAndreas Boehler { 255185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 256185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 257185e2535SAndreas Boehler 258185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 259185e2535SAndreas Boehler 260185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 261185e2535SAndreas Boehler return false; 262185e2535SAndreas Boehler 263185e2535SAndreas Boehler $color = $row['calendarcolor']; 264185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 265185e2535SAndreas Boehler return $color; 266185e2535SAndreas Boehler } 267185e2535SAndreas Boehler 268185e2535SAndreas Boehler /** 269e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 270e86c8dd3SAndreas Boehler * @param string $user the user name 271e86c8dd3SAndreas Boehler * @return the URL to the principal sync 272e86c8dd3SAndreas Boehler */ 273e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 274e86c8dd3SAndreas Boehler { 275e86c8dd3SAndreas Boehler if(is_null($user)) 276e86c8dd3SAndreas Boehler return false; 277e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 278e86c8dd3SAndreas Boehler return $url; 279e86c8dd3SAndreas Boehler } 280e86c8dd3SAndreas Boehler 281e86c8dd3SAndreas Boehler /** 282185e2535SAndreas Boehler * Set the calendar color for a given page. 283185e2535SAndreas Boehler * 284185e2535SAndreas Boehler * @param string $color The color definition 285185e2535SAndreas Boehler * @param string $id optional The page ID 286185e2535SAndreas Boehler * @return boolean True on success, otherwise false 287185e2535SAndreas Boehler */ 288185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 289185e2535SAndreas Boehler { 290185e2535SAndreas Boehler if(is_null($id)) 291185e2535SAndreas Boehler { 292185e2535SAndreas Boehler global $ID; 293185e2535SAndreas Boehler $id = $ID; 294185e2535SAndreas Boehler } 295185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 296185e2535SAndreas Boehler if($calid === false) 297185e2535SAndreas Boehler return false; 298185e2535SAndreas Boehler 2995f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3005f2c3e2dSAndreas Boehler if(!$sqlite) 3015f2c3e2dSAndreas Boehler return false; 30251f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 30351f4febbSAndreas Boehler " WHERE id = ?"; 3045f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $color, $calid); 305185e2535SAndreas Boehler if($res !== false) 306185e2535SAndreas Boehler { 307185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 308185e2535SAndreas Boehler return true; 309185e2535SAndreas Boehler } 310185e2535SAndreas Boehler return false; 311185e2535SAndreas Boehler } 312185e2535SAndreas Boehler 313185e2535SAndreas Boehler /** 314cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 315cb71a62aSAndreas Boehler * page id. 316cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 317cb71a62aSAndreas Boehler * 318cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 319cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 320cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 321cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 322cb71a62aSAndreas Boehler * 323cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 324cb71a62aSAndreas Boehler */ 325a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 326a1a3b679SAndreas Boehler { 327a1a3b679SAndreas Boehler if(is_null($id)) 328a1a3b679SAndreas Boehler { 329a1a3b679SAndreas Boehler global $ID; 330a1a3b679SAndreas Boehler $id = $ID; 331a1a3b679SAndreas Boehler } 332a1a3b679SAndreas Boehler if(is_null($userid)) 33334a47953SAndreas Boehler { 33434a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 33534a47953SAndreas Boehler { 336a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 33734a47953SAndreas Boehler } 33834a47953SAndreas Boehler else 33934a47953SAndreas Boehler { 34034a47953SAndreas Boehler $userid = uniqid('davcal-'); 34134a47953SAndreas Boehler } 34234a47953SAndreas Boehler } 343a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 344a1a3b679SAndreas Boehler if($calid === false) 345a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 346a1a3b679SAndreas Boehler 3475f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3485f2c3e2dSAndreas Boehler if(!$sqlite) 3495f2c3e2dSAndreas Boehler return false; 35051f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 3515f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $name, $description, $calid); 352b269830cSAndreas Boehler if($res !== false) 353b269830cSAndreas Boehler return true; 354b269830cSAndreas Boehler return false; 355a1a3b679SAndreas Boehler } 356a1a3b679SAndreas Boehler 357cb71a62aSAndreas Boehler /** 358d5703f5aSAndreas Boehler * Update a calendar's displayname 359d5703f5aSAndreas Boehler * 360d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 361d5703f5aSAndreas Boehler * @param string $name The new calendar name 362d5703f5aSAndreas Boehler * 363d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 364d5703f5aSAndreas Boehler */ 365d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 366d5703f5aSAndreas Boehler { 3675f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3685f2c3e2dSAndreas Boehler if(!$sqlite) 3695f2c3e2dSAndreas Boehler return false; 370d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 3715f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $name); 372d5703f5aSAndreas Boehler if($res !== false) 373d5703f5aSAndreas Boehler { 374d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 375d5703f5aSAndreas Boehler return true; 376d5703f5aSAndreas Boehler } 377d5703f5aSAndreas Boehler return false; 378d5703f5aSAndreas Boehler } 379d5703f5aSAndreas Boehler 380d5703f5aSAndreas Boehler /** 381d5703f5aSAndreas Boehler * Update the calendar description 382d5703f5aSAndreas Boehler * 383d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 384d5703f5aSAndreas Boehler * @param string $description The new calendar's description 385d5703f5aSAndreas Boehler * 386d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 387d5703f5aSAndreas Boehler */ 388d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 389d5703f5aSAndreas Boehler { 3905f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3915f2c3e2dSAndreas Boehler if(!$sqlite) 3925f2c3e2dSAndreas Boehler return false; 393d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 3945f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $description); 395d5703f5aSAndreas Boehler if($res !== false) 396d5703f5aSAndreas Boehler { 397d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 398d5703f5aSAndreas Boehler return true; 399d5703f5aSAndreas Boehler } 400d5703f5aSAndreas Boehler return false; 401d5703f5aSAndreas Boehler } 402d5703f5aSAndreas Boehler 403d5703f5aSAndreas Boehler /** 404d5703f5aSAndreas Boehler * Update a calendar's timezone information 405d5703f5aSAndreas Boehler * 406d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 407d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 408d5703f5aSAndreas Boehler * 409d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 410d5703f5aSAndreas Boehler */ 411d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 412d5703f5aSAndreas Boehler { 4135f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4145f2c3e2dSAndreas Boehler if(!$sqlite) 4155f2c3e2dSAndreas Boehler return false; 416d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 4175f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $timezone); 418d5703f5aSAndreas Boehler if($res !== false) 419d5703f5aSAndreas Boehler { 420d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 421d5703f5aSAndreas Boehler return true; 422d5703f5aSAndreas Boehler } 423d5703f5aSAndreas Boehler return false; 424d5703f5aSAndreas Boehler } 425d5703f5aSAndreas Boehler 426d5703f5aSAndreas Boehler /** 427cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 428cb71a62aSAndreas Boehler * 429cb71a62aSAndreas Boehler * @param array $settings The settings array to store 430cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 431cb71a62aSAndreas Boehler * 432cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 433cb71a62aSAndreas Boehler */ 434a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 435a495d34cSAndreas Boehler { 436a495d34cSAndreas Boehler if(is_null($userid)) 43734a47953SAndreas Boehler { 43834a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 43934a47953SAndreas Boehler { 440a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 44134a47953SAndreas Boehler } 44234a47953SAndreas Boehler else 44334a47953SAndreas Boehler { 44434a47953SAndreas Boehler return false; 44534a47953SAndreas Boehler } 44634a47953SAndreas Boehler } 4475f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4485f2c3e2dSAndreas Boehler if(!$sqlite) 4495f2c3e2dSAndreas Boehler return false; 4505f2c3e2dSAndreas Boehler $sqlite->query("BEGIN TRANSACTION"); 451a495d34cSAndreas Boehler 45251f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 4535f2c3e2dSAndreas Boehler $sqlite->query($query, $userid); 454bd883736SAndreas Boehler 455a495d34cSAndreas Boehler foreach($settings as $key => $value) 456a495d34cSAndreas Boehler { 45751f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 4585f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $userid, $key, $value); 459a495d34cSAndreas Boehler if($res === false) 460a495d34cSAndreas Boehler return false; 461a495d34cSAndreas Boehler } 4625f2c3e2dSAndreas Boehler $sqlite->query("COMMIT TRANSACTION"); 463185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 464a495d34cSAndreas Boehler return true; 465a495d34cSAndreas Boehler } 466a495d34cSAndreas Boehler 467cb71a62aSAndreas Boehler /** 468cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 469cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 470cb71a62aSAndreas Boehler * 471cb71a62aSAndreas Boehler * timezone => local 472cb71a62aSAndreas Boehler * weeknumbers => 0 473cb71a62aSAndreas Boehler * workweek => 0 474cb71a62aSAndreas Boehler * 475cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 476cb71a62aSAndreas Boehler * 477cb71a62aSAndreas Boehler * @return array The settings array 478cb71a62aSAndreas Boehler */ 479a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 480a495d34cSAndreas Boehler { 481bd883736SAndreas Boehler // Some sane default settings 482bd883736SAndreas Boehler $settings = array( 483fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 484fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 485fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4861d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4871d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 488bd883736SAndreas Boehler ); 48934a47953SAndreas Boehler if(is_null($userid)) 49034a47953SAndreas Boehler { 49134a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 49234a47953SAndreas Boehler { 49334a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 49434a47953SAndreas Boehler } 49534a47953SAndreas Boehler else 49634a47953SAndreas Boehler { 49734a47953SAndreas Boehler return $settings; 49834a47953SAndreas Boehler } 49934a47953SAndreas Boehler } 50034a47953SAndreas Boehler 5015f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5025f2c3e2dSAndreas Boehler if(!$sqlite) 5035f2c3e2dSAndreas Boehler return false; 50434a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 50534a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 50651f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 5075f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $userid); 5085f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 509a495d34cSAndreas Boehler foreach($arr as $row) 510a495d34cSAndreas Boehler { 511a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 512a495d34cSAndreas Boehler } 513185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 514a495d34cSAndreas Boehler return $settings; 515a495d34cSAndreas Boehler } 516a495d34cSAndreas Boehler 517cb71a62aSAndreas Boehler /** 518cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 519cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 520cb71a62aSAndreas Boehler * 521cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 522cb71a62aSAndreas Boehler * 523cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 524cb71a62aSAndreas Boehler */ 525a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 526a1a3b679SAndreas Boehler { 527a1a3b679SAndreas Boehler if(is_null($id)) 528a1a3b679SAndreas Boehler { 529a1a3b679SAndreas Boehler global $ID; 530a1a3b679SAndreas Boehler $id = $ID; 531a1a3b679SAndreas Boehler } 532a1a3b679SAndreas Boehler 533185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 534185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 535185e2535SAndreas Boehler 5365f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5375f2c3e2dSAndreas Boehler if(!$sqlite) 5385f2c3e2dSAndreas Boehler return false; 53951f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 5405f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $id); 5415f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 542a1a3b679SAndreas Boehler if(isset($row['calid'])) 543185e2535SAndreas Boehler { 544185e2535SAndreas Boehler $calid = $row['calid']; 545185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 546185e2535SAndreas Boehler return $calid; 547185e2535SAndreas Boehler } 548a1a3b679SAndreas Boehler return false; 549a1a3b679SAndreas Boehler } 550a1a3b679SAndreas Boehler 551cb71a62aSAndreas Boehler /** 552cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 553cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 554cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 555cb71a62aSAndreas Boehler * 556cb71a62aSAndreas Boehler * @return array The mapping array 557cb71a62aSAndreas Boehler */ 558a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 559a1a3b679SAndreas Boehler { 5605f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5615f2c3e2dSAndreas Boehler if(!$sqlite) 5625f2c3e2dSAndreas Boehler return false; 563a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 5645f2c3e2dSAndreas Boehler $res = $sqlite->query($query); 5655f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 566a1a3b679SAndreas Boehler return $arr; 567a1a3b679SAndreas Boehler } 568a1a3b679SAndreas Boehler 569cb71a62aSAndreas Boehler /** 570cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 571cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 572cb71a62aSAndreas Boehler * user name is actually split from the URI component. 573cb71a62aSAndreas Boehler * 574cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 575cb71a62aSAndreas Boehler * and applied accordingly. 576cb71a62aSAndreas Boehler * 577cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 578cb71a62aSAndreas Boehler * 579cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 580cb71a62aSAndreas Boehler */ 581a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 582a1a3b679SAndreas Boehler { 58334a47953SAndreas Boehler global $auth; 584a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 585a1a3b679SAndreas Boehler $user = end($user); 586a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 587a1a3b679SAndreas Boehler $calids = array(); 58834a47953SAndreas Boehler $ud = $auth->getUserData($user); 58934a47953SAndreas Boehler $groups = $ud['grps']; 590a1a3b679SAndreas Boehler foreach($mapping as $row) 591a1a3b679SAndreas Boehler { 592a1a3b679SAndreas Boehler $id = $row['calid']; 59313b16484SAndreas Boehler $enabled = $this->getCalendarStatus($id); 59413b16484SAndreas Boehler if($enabled == false) 59513b16484SAndreas Boehler continue; 596a1a3b679SAndreas Boehler $page = $row['page']; 59734a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 598a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 599a1a3b679SAndreas Boehler { 600a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 601a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 602a1a3b679SAndreas Boehler } 603a1a3b679SAndreas Boehler } 604a1a3b679SAndreas Boehler return $calids; 605a1a3b679SAndreas Boehler } 606a1a3b679SAndreas Boehler 607cb71a62aSAndreas Boehler /** 608cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 609cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 610cb71a62aSAndreas Boehler * 611cb71a62aSAndreas Boehler * @param string $name The calendar's name 612cb71a62aSAndreas Boehler * @param string $description The calendar's description 613cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 614cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 615cb71a62aSAndreas Boehler * 616cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 617cb71a62aSAndreas Boehler */ 618a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 619a1a3b679SAndreas Boehler { 620a1a3b679SAndreas Boehler if(is_null($id)) 621a1a3b679SAndreas Boehler { 622a1a3b679SAndreas Boehler global $ID; 623a1a3b679SAndreas Boehler $id = $ID; 624a1a3b679SAndreas Boehler } 625a1a3b679SAndreas Boehler if(is_null($userid)) 62634a47953SAndreas Boehler { 62734a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 62834a47953SAndreas Boehler { 629a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 63034a47953SAndreas Boehler } 63134a47953SAndreas Boehler else 63234a47953SAndreas Boehler { 63334a47953SAndreas Boehler $userid = uniqid('davcal-'); 63434a47953SAndreas Boehler } 63534a47953SAndreas Boehler } 636a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 637a1a3b679SAndreas Boehler $name, 638a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 639a1a3b679SAndreas Boehler $description, 640a1a3b679SAndreas Boehler 'VEVENT,VTODO', 64155a741c0SAndreas Boehler 0, 64255a741c0SAndreas Boehler 1); 6435f2c3e2dSAndreas Boehler 6445f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6455f2c3e2dSAndreas Boehler if(!$sqlite) 6465f2c3e2dSAndreas Boehler return false; 64751f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 64851f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 6495f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]); 65055a741c0SAndreas Boehler if($res === false) 65155a741c0SAndreas Boehler return false; 652cb71a62aSAndreas Boehler 653cb71a62aSAndreas Boehler // Get the new calendar ID 65451f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 65551f4febbSAndreas Boehler "uri = ? AND description = ?"; 6565f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3]); 6575f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 658cb71a62aSAndreas Boehler 659cb71a62aSAndreas Boehler // Update the pagetocalendarmapping table with the new calendar ID 660a1a3b679SAndreas Boehler if(isset($row['id'])) 661a1a3b679SAndreas Boehler { 66251f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 6635f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $id, $row['id']); 66455a741c0SAndreas Boehler return ($res !== false); 665a1a3b679SAndreas Boehler } 666a1a3b679SAndreas Boehler 667a1a3b679SAndreas Boehler return false; 668a1a3b679SAndreas Boehler } 669a1a3b679SAndreas Boehler 670cb71a62aSAndreas Boehler /** 671d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 672d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 673d5703f5aSAndreas Boehler * 674d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 675d5703f5aSAndreas Boehler * 676d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 677d5703f5aSAndreas Boehler * @param string $uri The new object URI 678d5703f5aSAndreas Boehler * @param string $ics The ICS file 679d5703f5aSAndreas Boehler * 680d5703f5aSAndreas Boehler * @return mixed The etag. 681d5703f5aSAndreas Boehler */ 682d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 683d5703f5aSAndreas Boehler { 684d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 685d5703f5aSAndreas Boehler 6865f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6875f2c3e2dSAndreas Boehler if(!$sqlite) 6885f2c3e2dSAndreas Boehler return false; 689d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 6905f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 691d5703f5aSAndreas Boehler $calid, 692d5703f5aSAndreas Boehler $uri, 693d5703f5aSAndreas Boehler $ics, 694d5703f5aSAndreas Boehler time(), 695d5703f5aSAndreas Boehler $extraData['etag'], 696d5703f5aSAndreas Boehler $extraData['size'], 697d5703f5aSAndreas Boehler $extraData['componentType'], 698d5703f5aSAndreas Boehler $extraData['firstOccurence'], 699d5703f5aSAndreas Boehler $extraData['lastOccurence'], 700d5703f5aSAndreas Boehler $extraData['uid']); 701d5703f5aSAndreas Boehler // If successfully, update the sync token database 702d5703f5aSAndreas Boehler if($res !== false) 703d5703f5aSAndreas Boehler { 704d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 705d5703f5aSAndreas Boehler } 706d5703f5aSAndreas Boehler return $extraData['etag']; 707d5703f5aSAndreas Boehler } 708d5703f5aSAndreas Boehler 709d5703f5aSAndreas Boehler /** 710d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 711d5703f5aSAndreas Boehler * needed for the sync support. 712d5703f5aSAndreas Boehler * 713d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 714d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 715d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 716d5703f5aSAndreas Boehler */ 717d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 718d5703f5aSAndreas Boehler { 719d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 720d5703f5aSAndreas Boehler 7215f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 7225f2c3e2dSAndreas Boehler if(!$sqlite) 7235f2c3e2dSAndreas Boehler return false; 724d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 7255f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 726d5703f5aSAndreas Boehler $ics, 727d5703f5aSAndreas Boehler time(), 728d5703f5aSAndreas Boehler $extraData['etag'], 729d5703f5aSAndreas Boehler $extraData['size'], 730d5703f5aSAndreas Boehler $extraData['componentType'], 731d5703f5aSAndreas Boehler $extraData['firstOccurence'], 732d5703f5aSAndreas Boehler $extraData['lastOccurence'], 733d5703f5aSAndreas Boehler $extraData['uid'], 734d5703f5aSAndreas Boehler $calid, 735d5703f5aSAndreas Boehler $uri 736d5703f5aSAndreas Boehler ); 737d5703f5aSAndreas Boehler if($res !== false) 738d5703f5aSAndreas Boehler { 739d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 740d5703f5aSAndreas Boehler } 741d5703f5aSAndreas Boehler return $extraData['etag']; 742d5703f5aSAndreas Boehler } 743d5703f5aSAndreas Boehler 744d5703f5aSAndreas Boehler /** 745cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 746cb71a62aSAndreas Boehler * 747cb71a62aSAndreas Boehler * The parameter array needs to contain 748cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 74982a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 750cb71a62aSAndreas Boehler * eventfrom => The event's start date 751cb71a62aSAndreas Boehler * eventfromtime => The event's start time 752cb71a62aSAndreas Boehler * eventto => The event's end date 753cb71a62aSAndreas Boehler * eventtotime => The event's end time 754cb71a62aSAndreas Boehler * eventname => The event's name 755cb71a62aSAndreas Boehler * eventdescription => The event's description 756cb71a62aSAndreas Boehler * 757cb71a62aSAndreas Boehler * @param string $id The page ID to work on 758cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 759cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 760cb71a62aSAndreas Boehler * 761cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 762cb71a62aSAndreas Boehler */ 763a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 764a1a3b679SAndreas Boehler { 76582a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 76682a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 76782a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 768a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 769bd883736SAndreas Boehler else 770bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 771cb71a62aSAndreas Boehler 772cb71a62aSAndreas Boehler // Retrieve dates from settings 773b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 774b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 775b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 776b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 777cb71a62aSAndreas Boehler 778cb71a62aSAndreas Boehler // Load SabreDAV 7799bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 780a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 781cb71a62aSAndreas Boehler 782cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 783a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 784b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 785b269830cSAndreas Boehler $event->add('UID', $uuid); 786a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 787cb71a62aSAndreas Boehler 788cb71a62aSAndreas Boehler // Add a description if requested 7890eebc909SAndreas Boehler $description = $params['eventdescription']; 7900eebc909SAndreas Boehler if($description !== '') 7910eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 792cb71a62aSAndreas Boehler 7932b7be5bdSAndreas Boehler // Add a location if requested 7942b7be5bdSAndreas Boehler $location = $params['eventlocation']; 7952b7be5bdSAndreas Boehler if($location !== '') 7962b7be5bdSAndreas Boehler $event->add('LOCATION', $location); 7972b7be5bdSAndreas Boehler 7984ecb526cSAndreas Boehler // Add attachments 7994ecb526cSAndreas Boehler $attachments = $params['attachments']; 80082a48dfbSAndreas Boehler if(!is_null($attachments)) 8014ecb526cSAndreas Boehler foreach($attachments as $attachment) 8024ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 8034ecb526cSAndreas Boehler 804cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 805b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 806b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 807b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 808b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 809cb71a62aSAndreas Boehler 810cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 811b269830cSAndreas Boehler $dtStart = new \DateTime(); 812a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 813b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 814cb71a62aSAndreas Boehler 815cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 816b269830cSAndreas Boehler if($params['allday'] != '1') 817b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 818cb71a62aSAndreas Boehler 819cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 820b269830cSAndreas Boehler $dtEnd = new \DateTime(); 821a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 822b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 823cb71a62aSAndreas Boehler 824cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 825b269830cSAndreas Boehler if($params['allday'] != '1') 826b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 827cb71a62aSAndreas Boehler 828b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 829b269830cSAndreas Boehler if($params['allday'] == '1') 830b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 831cb71a62aSAndreas Boehler 832cb71a62aSAndreas Boehler // Really add Start and End events 833b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 834b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 835cb71a62aSAndreas Boehler 836cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 837b269830cSAndreas Boehler if($params['allday'] == '1') 838b269830cSAndreas Boehler { 839b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 840b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 841b269830cSAndreas Boehler } 842cb71a62aSAndreas Boehler 843809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 844809cb0faSAndreas Boehler 845809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 846809cb0faSAndreas Boehler { 847809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 848809cb0faSAndreas Boehler if(is_null($wdc)) 849809cb0faSAndreas Boehler return false; 850809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 851809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 852809cb0faSAndreas Boehler } 853809cb0faSAndreas Boehler else 854809cb0faSAndreas Boehler { 855cb71a62aSAndreas Boehler // Actually add the values to the database 856a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 85702eea9b8SAndreas Boehler $uri = $uri = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 8581bb22c2bSAndreas Boehler $now = new \DateTime(); 859a1a3b679SAndreas Boehler 8605f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8615f2c3e2dSAndreas Boehler if(!$sqlite) 8625f2c3e2dSAndreas Boehler return false; 86351f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 8645f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 86551f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 86651f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 867cb71a62aSAndreas Boehler 868cb71a62aSAndreas Boehler // If successfully, update the sync token database 86955a741c0SAndreas Boehler if($res !== false) 87055a741c0SAndreas Boehler { 87155a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 872a1a3b679SAndreas Boehler return true; 873a1a3b679SAndreas Boehler } 874809cb0faSAndreas Boehler } 87555a741c0SAndreas Boehler return false; 87655a741c0SAndreas Boehler } 877a1a3b679SAndreas Boehler 878cb71a62aSAndreas Boehler /** 879cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 880cb71a62aSAndreas Boehler * 881cb71a62aSAndreas Boehler * @param string $calid The calendar ID 882cb71a62aSAndreas Boehler * 883cb71a62aSAndreas Boehler * @return array The calendar settings array 884cb71a62aSAndreas Boehler */ 885b269830cSAndreas Boehler public function getCalendarSettings($calid) 886b269830cSAndreas Boehler { 8875f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8885f2c3e2dSAndreas Boehler if(!$sqlite) 8895f2c3e2dSAndreas Boehler return false; 89013b16484SAndreas Boehler $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled FROM calendars WHERE id= ? "; 8915f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 8925f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 893b269830cSAndreas Boehler return $row; 894b269830cSAndreas Boehler } 895b269830cSAndreas Boehler 896cb71a62aSAndreas Boehler /** 89713b16484SAndreas Boehler * Retrieve the calendar status of a given calendar id 89813b16484SAndreas Boehler * 89913b16484SAndreas Boehler * @param string $calid The calendar ID 90013b16484SAndreas Boehler * @return boolean True if calendar is enabled, otherwise false 90113b16484SAndreas Boehler */ 90213b16484SAndreas Boehler public function getCalendarStatus($calid) 90313b16484SAndreas Boehler { 9045f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9055f2c3e2dSAndreas Boehler if(!$sqlite) 9065f2c3e2dSAndreas Boehler return false; 90713b16484SAndreas Boehler $query = "SELECT disabled FROM calendars WHERE id = ?"; 9085f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 9095f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 91013b16484SAndreas Boehler if($row['disabled'] == 1) 91113b16484SAndreas Boehler return false; 91213b16484SAndreas Boehler else 91313b16484SAndreas Boehler return true; 91413b16484SAndreas Boehler } 91513b16484SAndreas Boehler 91613b16484SAndreas Boehler /** 91713b16484SAndreas Boehler * Disable a calendar for a given page 91813b16484SAndreas Boehler * 91913b16484SAndreas Boehler * @param string $id The page ID 92013b16484SAndreas Boehler * 92113b16484SAndreas Boehler * @return boolean true on success, otherwise false 92213b16484SAndreas Boehler */ 92313b16484SAndreas Boehler public function disableCalendarForPage($id) 92413b16484SAndreas Boehler { 92513b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 92613b16484SAndreas Boehler if($calid === false) 92713b16484SAndreas Boehler return false; 9285f2c3e2dSAndreas Boehler 9295f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9305f2c3e2dSAndreas Boehler if(!$sqlite) 9315f2c3e2dSAndreas Boehler return false; 93213b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 1 WHERE id = ?"; 9335f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 93413b16484SAndreas Boehler if($res !== false) 93513b16484SAndreas Boehler return true; 93613b16484SAndreas Boehler return false; 93713b16484SAndreas Boehler } 93813b16484SAndreas Boehler 93913b16484SAndreas Boehler /** 94013b16484SAndreas Boehler * Enable a calendar for a given page 94113b16484SAndreas Boehler * 94213b16484SAndreas Boehler * @param string $id The page ID 94313b16484SAndreas Boehler * 94413b16484SAndreas Boehler * @return boolean true on success, otherwise false 94513b16484SAndreas Boehler */ 94613b16484SAndreas Boehler public function enableCalendarForPage($id) 94713b16484SAndreas Boehler { 94813b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 94913b16484SAndreas Boehler if($calid === false) 95013b16484SAndreas Boehler return false; 9515f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9525f2c3e2dSAndreas Boehler if(!$sqlite) 9535f2c3e2dSAndreas Boehler return false; 95413b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 0 WHERE id = ?"; 9555f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 95613b16484SAndreas Boehler if($res !== false) 95713b16484SAndreas Boehler return true; 95813b16484SAndreas Boehler return false; 95913b16484SAndreas Boehler } 96013b16484SAndreas Boehler 96113b16484SAndreas Boehler /** 962cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 963cb71a62aSAndreas Boehler * based on the timezone setting. 964cb71a62aSAndreas Boehler * 965cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 966cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 967cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 968cb71a62aSAndreas Boehler * 969cb71a62aSAndreas Boehler * @param string $id The page ID to work with 970cb71a62aSAndreas Boehler * @param string $user The user ID to work with 971cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 972cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 9734a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 974cb71a62aSAndreas Boehler * 975cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 976cb71a62aSAndreas Boehler */ 9774a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 978a1a3b679SAndreas Boehler { 97982a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 98082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 981bd883736SAndreas Boehler else 982bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 983a1a3b679SAndreas Boehler $data = array(); 984b8265976SAndreas Boehler $calname = 'unknown'; 985cb71a62aSAndreas Boehler 9865f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9875f2c3e2dSAndreas Boehler if(!$sqlite) 9885f2c3e2dSAndreas Boehler return false; 9895f2c3e2dSAndreas Boehler 990a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 991a469597cSAndreas Boehler $startTs = null; 992a469597cSAndreas Boehler $endTs = null; 993a469597cSAndreas Boehler if($startDate !== null) 994a469597cSAndreas Boehler { 995a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 9965f2c3e2dSAndreas Boehler $query .= " AND lastoccurence > ".$sqlite->quote_string($startTs->getTimestamp()); 997a469597cSAndreas Boehler } 998a469597cSAndreas Boehler if($endDate !== null) 999a469597cSAndreas Boehler { 1000a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 10015f2c3e2dSAndreas Boehler $query .= " AND firstoccurence < ".$sqlite->quote_string($endTs->getTimestamp()); 1002a469597cSAndreas Boehler } 1003cb71a62aSAndreas Boehler 10040b805092SAndreas Boehler // Load SabreDAV 10050b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 10060b805092SAndreas Boehler 10070b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 10080b805092SAndreas Boehler { 10090b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 10100b805092SAndreas Boehler if(is_null($wdc)) 10110b805092SAndreas Boehler return $data; 10120b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 10130b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 1014b8265976SAndreas Boehler $conn = $wdc->getConnection($connectionId); 1015b8265976SAndreas Boehler $calname = $conn['displayname']; 10160b805092SAndreas Boehler } 10170b805092SAndreas Boehler else 10180b805092SAndreas Boehler { 10190b805092SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 10200b805092SAndreas Boehler if(is_null($color)) 10210b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 10220b805092SAndreas Boehler 102359b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 102459b68239SAndreas Boehler if($enabled === false) 102559b68239SAndreas Boehler return $data; 102659b68239SAndreas Boehler 1027b8265976SAndreas Boehler $settings = $this->getCalendarSettings($calid); 1028b8265976SAndreas Boehler $calname = $settings['displayname']; 1029b8265976SAndreas Boehler 1030cb71a62aSAndreas Boehler // Retrieve matching calendar objects 10315f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 10325f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 10330b805092SAndreas Boehler } 1034cb71a62aSAndreas Boehler 1035cb71a62aSAndreas Boehler // Parse individual calendar entries 1036a1a3b679SAndreas Boehler foreach($arr as $row) 1037a1a3b679SAndreas Boehler { 1038a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 1039a1a3b679SAndreas Boehler { 1040b269830cSAndreas Boehler $entry = array(); 1041a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 1042ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 1043cb71a62aSAndreas Boehler // If it is a recurring event, pass it through Sabre's EventIterator 1044ebc4eb57SAndreas Boehler if($recurrence != null) 1045ebc4eb57SAndreas Boehler { 1046ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 1047ebc4eb57SAndreas Boehler $rEvents->rewind(); 1048e9b7d302SAndreas Boehler while($rEvents->valid()) 1049ebc4eb57SAndreas Boehler { 1050ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 1051cb71a62aSAndreas Boehler // If we are after the given time range, exit 1052a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 1053e9b7d302SAndreas Boehler break; 1054cb71a62aSAndreas Boehler 1055cb71a62aSAndreas Boehler // If we are before the given time range, continue 1056a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 1057ebc4eb57SAndreas Boehler { 1058ebc4eb57SAndreas Boehler $rEvents->next(); 1059ebc4eb57SAndreas Boehler continue; 1060ebc4eb57SAndreas Boehler } 1061cb71a62aSAndreas Boehler 1062cb71a62aSAndreas Boehler // If we are within the given time range, parse the event 1063b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1064b8265976SAndreas Boehler $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true)); 1065ebc4eb57SAndreas Boehler $rEvents->next(); 1066ebc4eb57SAndreas Boehler } 1067ebc4eb57SAndreas Boehler } 1068ebc4eb57SAndreas Boehler else 1069b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1070b8265976SAndreas Boehler $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color)); 1071ebc4eb57SAndreas Boehler } 1072ebc4eb57SAndreas Boehler } 1073ebc4eb57SAndreas Boehler return $data; 1074ebc4eb57SAndreas Boehler } 1075ebc4eb57SAndreas Boehler 1076cb71a62aSAndreas Boehler /** 1077cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 1078cb71a62aSAndreas Boehler * 1079cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 1080cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 1081cb71a62aSAndreas Boehler * @param string $uid The entry's UID 10823c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 1083cb71a62aSAndreas Boehler * 1084cb71a62aSAndreas Boehler * @return array The parse calendar entry 1085cb71a62aSAndreas Boehler */ 1086185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 1087ebc4eb57SAndreas Boehler { 1088ebc4eb57SAndreas Boehler $entry = array(); 1089ebc4eb57SAndreas Boehler $start = $event->DTSTART; 1090cb71a62aSAndreas Boehler // Parse only if the start date/time is present 1091b269830cSAndreas Boehler if($start !== null) 1092b269830cSAndreas Boehler { 1093b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 1094b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 1095bf0ad2b4SAndreas Boehler 1096bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 1097bf0ad2b4SAndreas Boehler // allDay is set to true 1098bf0ad2b4SAndreas Boehler // This should fix T23 1099b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 1100bf0ad2b4SAndreas Boehler { 1101b269830cSAndreas Boehler $entry['allDay'] = true; 1102bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 1103bf0ad2b4SAndreas Boehler } 1104b269830cSAndreas Boehler else 1105bf0ad2b4SAndreas Boehler { 1106b269830cSAndreas Boehler $entry['allDay'] = false; 1107bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 1108bf0ad2b4SAndreas Boehler } 1109b269830cSAndreas Boehler } 1110ebc4eb57SAndreas Boehler $end = $event->DTEND; 1111bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 1112b269830cSAndreas Boehler if($end !== null) 1113b269830cSAndreas Boehler { 1114b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 1115b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 1116bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 1117bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 1118bf0ad2b4SAndreas Boehler else 1119b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1120b269830cSAndreas Boehler } 1121954f1ffaSAndreas Boehler $duration = $event->DURATION; 1122954f1ffaSAndreas Boehler // Parse duration only if start is set, but end is missing 1123954f1ffaSAndreas Boehler if($start !== null && $end == null && $duration !== null) 1124954f1ffaSAndreas Boehler { 1125954f1ffaSAndreas Boehler $interval = $duration->getDateInterval(); 1126954f1ffaSAndreas Boehler $dtStart = $start->getDateTime(); 1127954f1ffaSAndreas Boehler $dtStart->setTimezone($timezone); 1128954f1ffaSAndreas Boehler $dtEnd = $dtStart->add($interval); 1129954f1ffaSAndreas Boehler $dtEnd->setTimezone($timezone); 1130954f1ffaSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1131954f1ffaSAndreas Boehler } 1132ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 11330eebc909SAndreas Boehler if($description !== null) 11340eebc909SAndreas Boehler $entry['description'] = (string)$description; 11350eebc909SAndreas Boehler else 11360eebc909SAndreas Boehler $entry['description'] = ''; 11374ecb526cSAndreas Boehler $attachments = $event->ATTACH; 11384ecb526cSAndreas Boehler if($attachments !== null) 11394ecb526cSAndreas Boehler { 11404ecb526cSAndreas Boehler $entry['attachments'] = array(); 11414ecb526cSAndreas Boehler foreach($attachments as $attachment) 11424ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 11434ecb526cSAndreas Boehler } 1144ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 11452b7be5bdSAndreas Boehler $entry['location'] = (string)$event->location; 1146ebc4eb57SAndreas Boehler $entry['id'] = $uid; 1147185e2535SAndreas Boehler $entry['page'] = $page; 1148185e2535SAndreas Boehler $entry['color'] = $color; 11493c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 1150185e2535SAndreas Boehler 1151ebc4eb57SAndreas Boehler return $entry; 1152a1a3b679SAndreas Boehler } 1153a1a3b679SAndreas Boehler 1154cb71a62aSAndreas Boehler /** 1155cb71a62aSAndreas Boehler * Retrieve an event by its UID 1156cb71a62aSAndreas Boehler * 1157cb71a62aSAndreas Boehler * @param string $uid The event's UID 1158cb71a62aSAndreas Boehler * 1159cb71a62aSAndreas Boehler * @return mixed The table row with the given event 1160cb71a62aSAndreas Boehler */ 1161a1a3b679SAndreas Boehler public function getEventWithUid($uid) 1162a1a3b679SAndreas Boehler { 11635f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11645f2c3e2dSAndreas Boehler if(!$sqlite) 11655f2c3e2dSAndreas Boehler return false; 116651f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 11675f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uid); 11685f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 1169a1a3b679SAndreas Boehler return $row; 1170a1a3b679SAndreas Boehler } 1171a1a3b679SAndreas Boehler 1172cb71a62aSAndreas Boehler /** 1173d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 117459b68239SAndreas Boehler * calendar data! This is mainly needed for the sync support. 1175d5703f5aSAndreas Boehler * 1176d5703f5aSAndreas Boehler * @param int $calid The calendar ID 1177d5703f5aSAndreas Boehler * 1178d5703f5aSAndreas Boehler * @return mixed The result 1179d5703f5aSAndreas Boehler */ 1180d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 1181d5703f5aSAndreas Boehler { 11825f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11835f2c3e2dSAndreas Boehler if(!$sqlite) 11845f2c3e2dSAndreas Boehler return false; 1185d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 11865f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 11875f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 1188d5703f5aSAndreas Boehler return $arr; 1189d5703f5aSAndreas Boehler } 1190d5703f5aSAndreas Boehler 1191d5703f5aSAndreas Boehler /** 1192d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 1193d5703f5aSAndreas Boehler * 1194d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1195d5703f5aSAndreas Boehler * @param string $uri The object's URI 1196d5703f5aSAndreas Boehler * 1197d5703f5aSAndreas Boehler * @return mixed The result 1198d5703f5aSAndreas Boehler */ 1199d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1200d5703f5aSAndreas Boehler { 12015f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12025f2c3e2dSAndreas Boehler if(!$sqlite) 12035f2c3e2dSAndreas Boehler return false; 1204d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 12055f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri); 12065f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 1207d5703f5aSAndreas Boehler return $row; 1208d5703f5aSAndreas Boehler } 1209d5703f5aSAndreas Boehler 1210d5703f5aSAndreas Boehler /** 1211d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1212d5703f5aSAndreas Boehler * This is mainly neede for sync. 1213d5703f5aSAndreas Boehler * 1214d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1215d5703f5aSAndreas Boehler * @param array $uris An array of URIs 1216d5703f5aSAndreas Boehler * 1217d5703f5aSAndreas Boehler * @return mixed The result 1218d5703f5aSAndreas Boehler */ 1219d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1220d5703f5aSAndreas Boehler { 12215f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12225f2c3e2dSAndreas Boehler if(!$sqlite) 12235f2c3e2dSAndreas Boehler return false; 1224d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1225d5703f5aSAndreas Boehler // Inserting a whole bunch of question marks 1226d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1227d5703f5aSAndreas Boehler $query .= ')'; 1228d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 1229d5703f5aSAndreas Boehler 12305f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $vals); 12315f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 1232d5703f5aSAndreas Boehler return $arr; 1233d5703f5aSAndreas Boehler } 1234d5703f5aSAndreas Boehler 1235d5703f5aSAndreas Boehler /** 1236cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1237cb71a62aSAndreas Boehler * 1238cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1239cb71a62aSAndreas Boehler * 1240cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1241cb71a62aSAndreas Boehler */ 1242f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1243f69bb449SAndreas Boehler { 124459b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 124559b68239SAndreas Boehler if($enabled === false) 124659b68239SAndreas Boehler return false; 12475f2c3e2dSAndreas Boehler 12485f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12495f2c3e2dSAndreas Boehler if(!$sqlite) 12505f2c3e2dSAndreas Boehler return false; 12517e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 12525f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 12535f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 1254f69bb449SAndreas Boehler return $arr; 1255f69bb449SAndreas Boehler } 1256f69bb449SAndreas Boehler 1257cb71a62aSAndreas Boehler /** 1258cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1259cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1260cb71a62aSAndreas Boehler * 1261cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1262cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1263cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1264cb71a62aSAndreas Boehler * 1265cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1266cb71a62aSAndreas Boehler */ 1267a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1268a1a3b679SAndreas Boehler { 126982a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 127082a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 127182a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1272a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1273bd883736SAndreas Boehler else 1274bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1275cb71a62aSAndreas Boehler 1276cb71a62aSAndreas Boehler // Parse dates 1277b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1278b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1279b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1280b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1281cb71a62aSAndreas Boehler 1282cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 128355a741c0SAndreas Boehler $uid = $params['uid']; 1284809cb0faSAndreas Boehler 1285809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1286809cb0faSAndreas Boehler { 1287809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1288809cb0faSAndreas Boehler if(is_null($wdc)) 1289809cb0faSAndreas Boehler return false; 1290809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1291809cb0faSAndreas Boehler } 1292809cb0faSAndreas Boehler else 1293809cb0faSAndreas Boehler { 129455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1295809cb0faSAndreas Boehler } 1296cb71a62aSAndreas Boehler 1297cb71a62aSAndreas Boehler // Load SabreDAV 12989bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1299a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1300a1a3b679SAndreas Boehler return false; 130155a741c0SAndreas Boehler $uri = $event['uri']; 130255a741c0SAndreas Boehler $calid = $event['calendarid']; 1303cb71a62aSAndreas Boehler 1304cb71a62aSAndreas Boehler // Parse the existing event 1305a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1306b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1307cb71a62aSAndreas Boehler 1308cb71a62aSAndreas Boehler // Set the new event values 1309b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1310b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 13110eebc909SAndreas Boehler $description = $params['eventdescription']; 13122b7be5bdSAndreas Boehler $location = $params['eventlocation']; 1313cb71a62aSAndreas Boehler 1314cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 13150eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1316b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1317b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 13184ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 13192b7be5bdSAndreas Boehler $vevent->remove('LOCATION'); 1320cb71a62aSAndreas Boehler 13212b7be5bdSAndreas Boehler // Add new time stamps, description and location 1322b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1323b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 13240eebc909SAndreas Boehler if($description !== '') 13250eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 13262b7be5bdSAndreas Boehler if($location !== '') 13272b7be5bdSAndreas Boehler $vevent->add('LOCATION', $location); 1328cb71a62aSAndreas Boehler 13294ecb526cSAndreas Boehler // Add attachments 13304ecb526cSAndreas Boehler $attachments = $params['attachments']; 133182a48dfbSAndreas Boehler if(!is_null($attachments)) 13324ecb526cSAndreas Boehler foreach($attachments as $attachment) 13334ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 13344ecb526cSAndreas Boehler 1335cb71a62aSAndreas Boehler // Setup DTSTART 1336b269830cSAndreas Boehler $dtStart = new \DateTime(); 1337a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1338b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1339b269830cSAndreas Boehler if($params['allday'] != '1') 1340b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1341cb71a62aSAndreas Boehler 13424ecb526cSAndreas Boehler // Setup DTEND 1343b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1344a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1345b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1346b269830cSAndreas Boehler if($params['allday'] != '1') 1347b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1348cb71a62aSAndreas Boehler 1349b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1350b269830cSAndreas Boehler if($params['allday'] == '1') 1351b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1352b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1353b269830cSAndreas Boehler $vevent->remove('DTEND'); 1354b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1355b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1356cb71a62aSAndreas Boehler 1357cb71a62aSAndreas Boehler // Remove the time for allday events 1358b269830cSAndreas Boehler if($params['allday'] == '1') 1359b269830cSAndreas Boehler { 1360b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1361b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1362b269830cSAndreas Boehler } 1363a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1364809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1365809cb0faSAndreas Boehler { 1366809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1367809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1368809cb0faSAndreas Boehler } 1369809cb0faSAndreas Boehler else 1370809cb0faSAndreas Boehler { 13715f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 13725f2c3e2dSAndreas Boehler if(!$sqlite) 13735f2c3e2dSAndreas Boehler return false; 1374809cb0faSAndreas Boehler $now = new DateTime(); 1375cb71a62aSAndreas Boehler // Actually write to the database 137651f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 137751f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 13785f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 137951f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 138055a741c0SAndreas Boehler if($res !== false) 138155a741c0SAndreas Boehler { 138255a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1383a1a3b679SAndreas Boehler return true; 1384a1a3b679SAndreas Boehler } 1385809cb0faSAndreas Boehler } 138655a741c0SAndreas Boehler return false; 138755a741c0SAndreas Boehler } 1388a1a3b679SAndreas Boehler 1389cb71a62aSAndreas Boehler /** 1390d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1391d5703f5aSAndreas Boehler * 1392d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1393d5703f5aSAndreas Boehler * @param string $uri The object's URI 1394d5703f5aSAndreas Boehler * 1395d5703f5aSAndreas Boehler * @return true 1396d5703f5aSAndreas Boehler */ 1397d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1398d5703f5aSAndreas Boehler { 13995f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14005f2c3e2dSAndreas Boehler if(!$sqlite) 14015f2c3e2dSAndreas Boehler return false; 1402d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 14035f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri); 1404d5703f5aSAndreas Boehler if($res !== false) 1405d5703f5aSAndreas Boehler { 1406d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1407d5703f5aSAndreas Boehler } 1408d5703f5aSAndreas Boehler return true; 1409d5703f5aSAndreas Boehler } 1410d5703f5aSAndreas Boehler 1411d5703f5aSAndreas Boehler /** 1412cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1413cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1414cb71a62aSAndreas Boehler * 1415cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1416cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1417cb71a62aSAndreas Boehler * 1418cb71a62aSAndreas Boehler * @return boolean True 1419cb71a62aSAndreas Boehler */ 1420a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1421a1a3b679SAndreas Boehler { 1422a1a3b679SAndreas Boehler $uid = $params['uid']; 1423809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1424809cb0faSAndreas Boehler { 1425809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1426809cb0faSAndreas Boehler if(is_null($wdc)) 1427809cb0faSAndreas Boehler return false; 1428809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1429809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1430809cb0faSAndreas Boehler return $result; 1431809cb0faSAndreas Boehler } 14325f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14335f2c3e2dSAndreas Boehler if(!$sqlite) 14345f2c3e2dSAndreas Boehler return false; 143555a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 14362c14b82bSAndreas Boehler $calid = $event['calendarid']; 143755a741c0SAndreas Boehler $uri = $event['uri']; 143851f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 14395f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uid); 144055a741c0SAndreas Boehler if($res !== false) 144155a741c0SAndreas Boehler { 144255a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 144355a741c0SAndreas Boehler } 1444a1a3b679SAndreas Boehler return true; 1445a1a3b679SAndreas Boehler } 1446a1a3b679SAndreas Boehler 1447cb71a62aSAndreas Boehler /** 1448cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1449cb71a62aSAndreas Boehler * 1450cb71a62aSAndreas Boehler * @param string $calid The calendar id 1451cb71a62aSAndreas Boehler * 1452cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1453cb71a62aSAndreas Boehler */ 145455a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 145555a741c0SAndreas Boehler { 1456b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 145755a741c0SAndreas Boehler if(isset($row['synctoken'])) 145855a741c0SAndreas Boehler return $row['synctoken']; 145955a741c0SAndreas Boehler return false; 146055a741c0SAndreas Boehler } 146155a741c0SAndreas Boehler 1462cb71a62aSAndreas Boehler /** 1463cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1464cb71a62aSAndreas Boehler * an operation code as stored in the database 1465cb71a62aSAndreas Boehler * 1466cb71a62aSAndreas Boehler * @param string $operationName The operation name 1467cb71a62aSAndreas Boehler * 1468cb71a62aSAndreas Boehler * @return mixed The operation code or false 1469cb71a62aSAndreas Boehler */ 147055a741c0SAndreas Boehler public function operationNameToOperation($operationName) 147155a741c0SAndreas Boehler { 147255a741c0SAndreas Boehler switch($operationName) 147355a741c0SAndreas Boehler { 147455a741c0SAndreas Boehler case 'added': 147555a741c0SAndreas Boehler return 1; 147655a741c0SAndreas Boehler break; 147755a741c0SAndreas Boehler case 'modified': 147855a741c0SAndreas Boehler return 2; 147955a741c0SAndreas Boehler break; 148055a741c0SAndreas Boehler case 'deleted': 148155a741c0SAndreas Boehler return 3; 148255a741c0SAndreas Boehler break; 148355a741c0SAndreas Boehler } 148455a741c0SAndreas Boehler return false; 148555a741c0SAndreas Boehler } 148655a741c0SAndreas Boehler 1487cb71a62aSAndreas Boehler /** 1488cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1489cb71a62aSAndreas Boehler * operation that was performed. 1490cb71a62aSAndreas Boehler * 1491cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1492cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1493cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1494cb71a62aSAndreas Boehler * 1495cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1496cb71a62aSAndreas Boehler */ 149755a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 149855a741c0SAndreas Boehler { 149955a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 150055a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 150155a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 150255a741c0SAndreas Boehler return false; 150355a741c0SAndreas Boehler $values = array($uri, 150455a741c0SAndreas Boehler $currentToken, 150555a741c0SAndreas Boehler $calid, 150655a741c0SAndreas Boehler $operationCode 150755a741c0SAndreas Boehler ); 15085f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 15095f2c3e2dSAndreas Boehler if(!$sqlite) 15105f2c3e2dSAndreas Boehler return false; 151151f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 15125f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 151355a741c0SAndreas Boehler if($res === false) 151455a741c0SAndreas Boehler return false; 151555a741c0SAndreas Boehler $currentToken++; 151651f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 15175f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $currentToken, $calid); 151855a741c0SAndreas Boehler return ($res !== false); 151955a741c0SAndreas Boehler } 152055a741c0SAndreas Boehler 1521cb71a62aSAndreas Boehler /** 1522cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1523cb71a62aSAndreas Boehler * 1524cb71a62aSAndreas Boehler * @param string $id The page's ID 1525cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1526cb71a62aSAndreas Boehler * 1527cb71a62aSAndreas Boehler * @return mixed The sync url or false 1528cb71a62aSAndreas Boehler */ 1529b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1530b269830cSAndreas Boehler { 153134a47953SAndreas Boehler if(is_null($userid)) 153234a47953SAndreas Boehler { 153334a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 153434a47953SAndreas Boehler { 153534a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 153634a47953SAndreas Boehler } 153734a47953SAndreas Boehler else 153834a47953SAndreas Boehler { 153934a47953SAndreas Boehler return false; 154034a47953SAndreas Boehler } 154134a47953SAndreas Boehler } 1542b269830cSAndreas Boehler 1543b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1544b269830cSAndreas Boehler if($calid === false) 1545b269830cSAndreas Boehler return false; 1546b269830cSAndreas Boehler 1547b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1548b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1549b269830cSAndreas Boehler return false; 1550b269830cSAndreas Boehler 1551b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1552b269830cSAndreas Boehler return $syncurl; 1553b269830cSAndreas Boehler } 1554b269830cSAndreas Boehler 1555cb71a62aSAndreas Boehler /** 1556cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1557cb71a62aSAndreas Boehler * 1558cb71a62aSAndreas Boehler * @param string $id the page ID 1559cb71a62aSAndreas Boehler * 1560cb71a62aSAndreas Boehler * @return mixed The private URL or false 1561cb71a62aSAndreas Boehler */ 1562f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1563f69bb449SAndreas Boehler { 1564a8a4aa52Sscottleechua // Check if this is an aggregated calendar (has multiple calendar pages) 1565a8a4aa52Sscottleechua $calendarPages = $this->getCalendarPagesByMeta($id); 1566a8a4aa52Sscottleechua 1567a8a4aa52Sscottleechua if($calendarPages !== false && count($calendarPages) > 1) 1568a8a4aa52Sscottleechua { 1569a8a4aa52Sscottleechua // This is an aggregated calendar - create a special private URL 1570a8a4aa52Sscottleechua return $this->getPrivateURLForAggregatedCalendar($id, $calendarPages); 1571a8a4aa52Sscottleechua } 1572a8a4aa52Sscottleechua 1573a8a4aa52Sscottleechua // Single calendar - use the original logic 1574f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1575f69bb449SAndreas Boehler if($calid === false) 1576f69bb449SAndreas Boehler return false; 1577f69bb449SAndreas Boehler 1578f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1579f69bb449SAndreas Boehler } 1580f69bb449SAndreas Boehler 1581cb71a62aSAndreas Boehler /** 1582cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1583cb71a62aSAndreas Boehler * 1584cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1585cb71a62aSAndreas Boehler * 1586cb71a62aSAndreas Boehler * @return mixed The private URL or false 1587cb71a62aSAndreas Boehler */ 1588f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1589f69bb449SAndreas Boehler { 1590185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1591185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 15925f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 15935f2c3e2dSAndreas Boehler if(!$sqlite) 15945f2c3e2dSAndreas Boehler return false; 159551f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 15965f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 15975f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 1598f69bb449SAndreas Boehler if(!isset($row['url'])) 1599f69bb449SAndreas Boehler { 160002eea9b8SAndreas Boehler $url = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 160151f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 16025f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $url, $calid); 1603f69bb449SAndreas Boehler if($res === false) 1604f69bb449SAndreas Boehler return false; 1605f69bb449SAndreas Boehler } 1606f69bb449SAndreas Boehler else 1607f69bb449SAndreas Boehler { 1608f69bb449SAndreas Boehler $url = $row['url']; 1609f69bb449SAndreas Boehler } 1610185e2535SAndreas Boehler 1611185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1612185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1613185e2535SAndreas Boehler return $url; 1614f69bb449SAndreas Boehler } 1615f69bb449SAndreas Boehler 1616cb71a62aSAndreas Boehler /** 1617a8a4aa52Sscottleechua * Return the private calendar's URL for an aggregated calendar 1618a8a4aa52Sscottleechua * 1619a8a4aa52Sscottleechua * @param string $id the page ID 1620a8a4aa52Sscottleechua * @param array $calendarPages the calendar pages in the aggregation 1621a8a4aa52Sscottleechua * 1622a8a4aa52Sscottleechua * @return mixed The private URL or false 1623a8a4aa52Sscottleechua */ 1624a8a4aa52Sscottleechua public function getPrivateURLForAggregatedCalendar($id, $calendarPages) 1625a8a4aa52Sscottleechua { 1626a8a4aa52Sscottleechua // Create a unique identifier for this aggregated calendar 1627a8a4aa52Sscottleechua $aggregateId = 'aggregated-' . md5($id . serialize($calendarPages)); 1628a8a4aa52Sscottleechua 1629a8a4aa52Sscottleechua if(isset($this->cachedValues['privateurl'][$aggregateId])) 1630a8a4aa52Sscottleechua return $this->cachedValues['privateurl'][$aggregateId]; 1631a8a4aa52Sscottleechua 1632a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1633a8a4aa52Sscottleechua if(!$sqlite) 1634a8a4aa52Sscottleechua return false; 1635a8a4aa52Sscottleechua 1636a8a4aa52Sscottleechua // Check if we already have a private URL for this aggregated calendar 1637a8a4aa52Sscottleechua $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 1638a8a4aa52Sscottleechua $res = $sqlite->query($query, $aggregateId); 1639a8a4aa52Sscottleechua $row = $sqlite->res2row($res); 1640a8a4aa52Sscottleechua 1641a8a4aa52Sscottleechua if(!isset($row['url'])) 1642a8a4aa52Sscottleechua { 1643a8a4aa52Sscottleechua $url = 'dokuwiki-aggregated-' . bin2hex(random_bytes(16)) . '.ics'; 1644a8a4aa52Sscottleechua $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 1645a8a4aa52Sscottleechua $res = $sqlite->query($query, $url, $aggregateId); 1646a8a4aa52Sscottleechua if($res === false) 1647a8a4aa52Sscottleechua return false; 1648a8a4aa52Sscottleechua 1649a8a4aa52Sscottleechua // Also store the mapping to the page ID for later retrieval 1650a8a4aa52Sscottleechua $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES(?, ?)"; 1651a8a4aa52Sscottleechua $res = $sqlite->query($query, $id, $aggregateId); 1652a8a4aa52Sscottleechua if($res === false) 1653a8a4aa52Sscottleechua return false; 1654a8a4aa52Sscottleechua } 1655a8a4aa52Sscottleechua else 1656a8a4aa52Sscottleechua { 1657a8a4aa52Sscottleechua $url = $row['url']; 1658a8a4aa52Sscottleechua } 1659a8a4aa52Sscottleechua 1660a8a4aa52Sscottleechua $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1661a8a4aa52Sscottleechua $this->cachedValues['privateurl'][$aggregateId] = $url; 1662a8a4aa52Sscottleechua return $url; 1663a8a4aa52Sscottleechua } 1664a8a4aa52Sscottleechua 1665a8a4aa52Sscottleechua /** 1666cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1667cb71a62aSAndreas Boehler * 1668cb71a62aSAndreas Boehler * @param string $url The private URL 1669cb71a62aSAndreas Boehler * 1670cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1671cb71a62aSAndreas Boehler */ 1672f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1673f69bb449SAndreas Boehler { 16745f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 16755f2c3e2dSAndreas Boehler if(!$sqlite) 16765f2c3e2dSAndreas Boehler return false; 167751f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 16785f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $url); 16795f2c3e2dSAndreas Boehler $row = $sqlite->res2row($res); 1680f69bb449SAndreas Boehler if(!isset($row['calid'])) 1681f69bb449SAndreas Boehler return false; 1682f69bb449SAndreas Boehler return $row['calid']; 1683f69bb449SAndreas Boehler } 1684f69bb449SAndreas Boehler 1685cb71a62aSAndreas Boehler /** 1686cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1687cb71a62aSAndreas Boehler * 16887e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1689cb71a62aSAndreas Boehler * 1690cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1691cb71a62aSAndreas Boehler */ 1692f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1693f69bb449SAndreas Boehler { 1694f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1695f69bb449SAndreas Boehler if($calSettings === false) 1696f69bb449SAndreas Boehler return false; 1697f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1698f69bb449SAndreas Boehler if($events === false) 1699f69bb449SAndreas Boehler return false; 1700f69bb449SAndreas Boehler 17017e0b8590SAndreas Boehler // Load SabreDAV 17027e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 17037e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 17047e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1705f69bb449SAndreas Boehler foreach($events as $event) 1706f69bb449SAndreas Boehler { 17077e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 17087e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 17097e0b8590SAndreas Boehler $out .= $evt->serialize(); 1710f69bb449SAndreas Boehler } 17117e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1712f69bb449SAndreas Boehler return $out; 1713f69bb449SAndreas Boehler } 1714f69bb449SAndreas Boehler 17157c7c6b0bSAndreas Boehler /** 1716a8a4aa52Sscottleechua * Return an aggregated calendar as ICS feed, combining multiple calendars. 1717a8a4aa52Sscottleechua * 1718a8a4aa52Sscottleechua * @param string $icsFile The ICS file name for the aggregated calendar 1719a8a4aa52Sscottleechua * 1720a8a4aa52Sscottleechua * @return mixed The combined calendar events as string or false 1721a8a4aa52Sscottleechua */ 1722a8a4aa52Sscottleechua public function getAggregatedCalendarAsICSFeed($icsFile) 1723a8a4aa52Sscottleechua { 1724a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1725a8a4aa52Sscottleechua if(!$sqlite) 1726a8a4aa52Sscottleechua return false; 1727a8a4aa52Sscottleechua 1728a8a4aa52Sscottleechua // Find the aggregated calendar ID from the URL 1729a8a4aa52Sscottleechua $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 1730a8a4aa52Sscottleechua $res = $sqlite->query($query, $icsFile); 1731a8a4aa52Sscottleechua $row = $sqlite->res2row($res); 1732a8a4aa52Sscottleechua 1733a8a4aa52Sscottleechua if(!isset($row['calid'])) 1734a8a4aa52Sscottleechua return false; 1735a8a4aa52Sscottleechua 1736a8a4aa52Sscottleechua $aggregateId = $row['calid']; 1737a8a4aa52Sscottleechua 1738a8a4aa52Sscottleechua // Get the page ID for this aggregated calendar 1739a8a4aa52Sscottleechua $query = "SELECT page FROM pagetocalendarmapping WHERE calid = ?"; 1740a8a4aa52Sscottleechua $res = $sqlite->query($query, $aggregateId); 1741a8a4aa52Sscottleechua $row = $sqlite->res2row($res); 1742a8a4aa52Sscottleechua 1743a8a4aa52Sscottleechua if(!isset($row['page'])) 1744a8a4aa52Sscottleechua return false; 1745a8a4aa52Sscottleechua 1746a8a4aa52Sscottleechua $pageId = $row['page']; 1747a8a4aa52Sscottleechua 1748a8a4aa52Sscottleechua // Get the calendar pages for this aggregated calendar 1749a8a4aa52Sscottleechua $calendarPages = $this->getCalendarPagesByMeta($pageId); 1750a8a4aa52Sscottleechua if($calendarPages === false || count($calendarPages) <= 1) 1751a8a4aa52Sscottleechua return false; 1752a8a4aa52Sscottleechua 1753a8a4aa52Sscottleechua // Load SabreDAV 1754a8a4aa52Sscottleechua require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1755a8a4aa52Sscottleechua 1756a8a4aa52Sscottleechua // Start building the combined ICS 1757a8a4aa52Sscottleechua $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 1758a8a4aa52Sscottleechua $out .= "Aggregated Calendar\r\n"; 1759a8a4aa52Sscottleechua 1760a8a4aa52Sscottleechua // Combine events from all calendars 1761a8a4aa52Sscottleechua foreach($calendarPages as $calPage => $color) 1762a8a4aa52Sscottleechua { 1763a8a4aa52Sscottleechua $calid = $this->getCalendarIdForPage($calPage); 1764a8a4aa52Sscottleechua if($calid === false) 1765a8a4aa52Sscottleechua continue; 1766a8a4aa52Sscottleechua 1767a8a4aa52Sscottleechua $events = $this->getAllCalendarEvents($calid); 1768a8a4aa52Sscottleechua if($events === false) 1769a8a4aa52Sscottleechua continue; 1770a8a4aa52Sscottleechua 1771a8a4aa52Sscottleechua foreach($events as $event) 1772a8a4aa52Sscottleechua { 1773a8a4aa52Sscottleechua $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1774a8a4aa52Sscottleechua $evt = $vcal->VEVENT; 1775a8a4aa52Sscottleechua $out .= $evt->serialize(); 1776a8a4aa52Sscottleechua } 1777a8a4aa52Sscottleechua } 1778a8a4aa52Sscottleechua 1779a8a4aa52Sscottleechua $out .= "END:VCALENDAR\r\n"; 1780a8a4aa52Sscottleechua return $out; 1781a8a4aa52Sscottleechua } 1782a8a4aa52Sscottleechua 1783a8a4aa52Sscottleechua /** 17847c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 17857c7c6b0bSAndreas Boehler * 17867c7c6b0bSAndreas Boehler * @param string $key The key to query 178721d04f73SAndreas Boehler * @return mixed The option set, null if not found 17887c7c6b0bSAndreas Boehler */ 17897c7c6b0bSAndreas Boehler public function getConfig($key) 17907c7c6b0bSAndreas Boehler { 17917c7c6b0bSAndreas Boehler return $this->getConf($key); 17927c7c6b0bSAndreas Boehler } 17937c7c6b0bSAndreas Boehler 1794d5703f5aSAndreas Boehler /** 1795d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1796d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1797d5703f5aSAndreas Boehler * 1798d5703f5aSAndreas Boehler * Returns an array with the following keys: 1799d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1800d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1801d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1802d5703f5aSAndreas Boehler * * firstOccurence 1803d5703f5aSAndreas Boehler * * lastOccurence 1804d5703f5aSAndreas Boehler * * uid - value of the UID property 1805d5703f5aSAndreas Boehler * 1806d5703f5aSAndreas Boehler * @param string $calendarData 1807d5703f5aSAndreas Boehler * @return array 1808d5703f5aSAndreas Boehler */ 1809d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1810d5703f5aSAndreas Boehler { 1811d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1812d5703f5aSAndreas Boehler 1813d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1814d5703f5aSAndreas Boehler $componentType = null; 1815d5703f5aSAndreas Boehler $component = null; 1816d5703f5aSAndreas Boehler $firstOccurence = null; 1817d5703f5aSAndreas Boehler $lastOccurence = null; 1818d5703f5aSAndreas Boehler $uid = null; 1819d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1820d5703f5aSAndreas Boehler { 1821d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1822d5703f5aSAndreas Boehler { 1823d5703f5aSAndreas Boehler $componentType = $component->name; 1824d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1825d5703f5aSAndreas Boehler break; 1826d5703f5aSAndreas Boehler } 1827d5703f5aSAndreas Boehler } 1828d5703f5aSAndreas Boehler if (!$componentType) 1829d5703f5aSAndreas Boehler { 1830d5703f5aSAndreas Boehler return false; 1831d5703f5aSAndreas Boehler } 1832d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1833d5703f5aSAndreas Boehler { 1834d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1835d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1836d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1837d5703f5aSAndreas Boehler { 1838d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1839d5703f5aSAndreas Boehler { 1840d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1841d5703f5aSAndreas Boehler } 1842d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1843d5703f5aSAndreas Boehler { 1844d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1845d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1846d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1847d5703f5aSAndreas Boehler } 1848d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1849d5703f5aSAndreas Boehler { 1850d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1851d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1852d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1853d5703f5aSAndreas Boehler } 1854d5703f5aSAndreas Boehler else 1855d5703f5aSAndreas Boehler { 1856d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1857d5703f5aSAndreas Boehler } 1858d5703f5aSAndreas Boehler } 1859d5703f5aSAndreas Boehler else 1860d5703f5aSAndreas Boehler { 1861d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1862d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1863d5703f5aSAndreas Boehler if ($it->isInfinite()) 1864d5703f5aSAndreas Boehler { 1865d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1866d5703f5aSAndreas Boehler } 1867d5703f5aSAndreas Boehler else 1868d5703f5aSAndreas Boehler { 1869d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1870d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1871d5703f5aSAndreas Boehler { 1872d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1873d5703f5aSAndreas Boehler $it->next(); 1874d5703f5aSAndreas Boehler } 1875d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1876d5703f5aSAndreas Boehler } 1877d5703f5aSAndreas Boehler } 1878d5703f5aSAndreas Boehler } 1879d5703f5aSAndreas Boehler 1880d5703f5aSAndreas Boehler return array( 1881d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1882d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1883d5703f5aSAndreas Boehler 'componentType' => $componentType, 1884d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1885d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1886d5703f5aSAndreas Boehler 'uid' => $uid, 1887d5703f5aSAndreas Boehler ); 1888d5703f5aSAndreas Boehler 1889d5703f5aSAndreas Boehler } 1890d5703f5aSAndreas Boehler 1891d5703f5aSAndreas Boehler /** 1892d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1893d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1894d5703f5aSAndreas Boehler * 1895d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1896d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1897d5703f5aSAndreas Boehler * 1898d5703f5aSAndreas Boehler * @return mixed The result 1899d5703f5aSAndreas Boehler */ 1900d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1901d5703f5aSAndreas Boehler { 1902*c42afaebSscottleechua \dokuwiki\Logger::debug('DAVCAL', 'Calendar query executed', __FILE__, __LINE__); 1903d5703f5aSAndreas Boehler $componentType = null; 1904d5703f5aSAndreas Boehler $requirePostFilter = true; 1905d5703f5aSAndreas Boehler $timeRange = null; 19065f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 19075f2c3e2dSAndreas Boehler if(!$sqlite) 19085f2c3e2dSAndreas Boehler return false; 1909d5703f5aSAndreas Boehler 1910d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1911d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1912d5703f5aSAndreas Boehler { 1913d5703f5aSAndreas Boehler $requirePostFilter = false; 1914d5703f5aSAndreas Boehler } 1915d5703f5aSAndreas Boehler 1916d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1917d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1918d5703f5aSAndreas Boehler { 1919d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1920d5703f5aSAndreas Boehler 1921d5703f5aSAndreas Boehler // Checking if we need post-filters 1922d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1923d5703f5aSAndreas Boehler { 1924d5703f5aSAndreas Boehler $requirePostFilter = false; 1925d5703f5aSAndreas Boehler } 1926d5703f5aSAndreas Boehler // There was a time-range filter 1927d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1928d5703f5aSAndreas Boehler { 1929d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1930d5703f5aSAndreas Boehler 1931d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1932d5703f5aSAndreas Boehler // 100% accurate mysql query. 1933d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1934d5703f5aSAndreas Boehler { 1935d5703f5aSAndreas Boehler $requirePostFilter = false; 1936d5703f5aSAndreas Boehler } 1937d5703f5aSAndreas Boehler } 1938d5703f5aSAndreas Boehler 1939d5703f5aSAndreas Boehler } 1940d5703f5aSAndreas Boehler 1941d5703f5aSAndreas Boehler if ($requirePostFilter) 1942d5703f5aSAndreas Boehler { 1943d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1944d5703f5aSAndreas Boehler } 1945d5703f5aSAndreas Boehler else 1946d5703f5aSAndreas Boehler { 1947d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1948d5703f5aSAndreas Boehler } 1949d5703f5aSAndreas Boehler 1950d5703f5aSAndreas Boehler $values = array( 1951d5703f5aSAndreas Boehler $calendarId 1952d5703f5aSAndreas Boehler ); 1953d5703f5aSAndreas Boehler 1954d5703f5aSAndreas Boehler if ($componentType) 1955d5703f5aSAndreas Boehler { 1956d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1957d5703f5aSAndreas Boehler $values[] = $componentType; 1958d5703f5aSAndreas Boehler } 1959d5703f5aSAndreas Boehler 1960d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1961d5703f5aSAndreas Boehler { 1962d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1963d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1964d5703f5aSAndreas Boehler } 1965d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1966d5703f5aSAndreas Boehler { 1967d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1968d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1969d5703f5aSAndreas Boehler } 1970d5703f5aSAndreas Boehler 19715f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $values); 19725f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 1973d5703f5aSAndreas Boehler 1974d5703f5aSAndreas Boehler $result = array(); 1975d5703f5aSAndreas Boehler foreach($arr as $row) 1976d5703f5aSAndreas Boehler { 1977d5703f5aSAndreas Boehler if ($requirePostFilter) 1978d5703f5aSAndreas Boehler { 1979d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1980d5703f5aSAndreas Boehler { 1981d5703f5aSAndreas Boehler continue; 1982d5703f5aSAndreas Boehler } 1983d5703f5aSAndreas Boehler } 1984d5703f5aSAndreas Boehler $result[] = $row['uri']; 1985d5703f5aSAndreas Boehler 1986d5703f5aSAndreas Boehler } 1987d5703f5aSAndreas Boehler 1988d5703f5aSAndreas Boehler return $result; 1989d5703f5aSAndreas Boehler } 1990d5703f5aSAndreas Boehler 1991d5703f5aSAndreas Boehler /** 1992d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1993d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1994d5703f5aSAndreas Boehler * 1995d5703f5aSAndreas Boehler * @param array $object 1996d5703f5aSAndreas Boehler * @param array $filters 1997d5703f5aSAndreas Boehler * @return bool 1998d5703f5aSAndreas Boehler */ 1999d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 2000d5703f5aSAndreas Boehler { 2001d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 2002d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 2003d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 2004d5703f5aSAndreas Boehler // well. 2005d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 2006d5703f5aSAndreas Boehler { 2007d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 2008d5703f5aSAndreas Boehler } 2009d5703f5aSAndreas Boehler 2010d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 2011d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 2012d5703f5aSAndreas Boehler 201339787131SAndreas Boehler $res = $validator->validate($vObject, $filters); 201439787131SAndreas Boehler return $res; 2015d5703f5aSAndreas Boehler 2016d5703f5aSAndreas Boehler } 2017d5703f5aSAndreas Boehler 2018d5703f5aSAndreas Boehler /** 2019d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 2020d5703f5aSAndreas Boehler * 2021d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 2022d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 2023d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 2024d5703f5aSAndreas Boehler * @param int $limit The limit of changes 2025d5703f5aSAndreas Boehler * 2026d5703f5aSAndreas Boehler * @return array The result 2027d5703f5aSAndreas Boehler */ 2028d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 2029d5703f5aSAndreas Boehler { 2030d5703f5aSAndreas Boehler // Current synctoken 2031d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 2032d5703f5aSAndreas Boehler 2033d5703f5aSAndreas Boehler if ($currentToken === false) return null; 2034d5703f5aSAndreas Boehler 2035d5703f5aSAndreas Boehler $result = array( 2036d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 2037d5703f5aSAndreas Boehler 'added' => array(), 2038d5703f5aSAndreas Boehler 'modified' => array(), 2039d5703f5aSAndreas Boehler 'deleted' => array(), 2040d5703f5aSAndreas Boehler ); 20415f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 20425f2c3e2dSAndreas Boehler if(!$sqlite) 20435f2c3e2dSAndreas Boehler return false; 2044d5703f5aSAndreas Boehler 2045d5703f5aSAndreas Boehler if ($syncToken) 2046d5703f5aSAndreas Boehler { 2047d5703f5aSAndreas Boehler 2048d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 2049d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 2050d5703f5aSAndreas Boehler 2051d5703f5aSAndreas Boehler // Fetching all changes 20525f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $syncToken, $currentToken, $calid); 2053d5703f5aSAndreas Boehler if($res === false) 2054d5703f5aSAndreas Boehler return null; 2055d5703f5aSAndreas Boehler 20565f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 2057d5703f5aSAndreas Boehler $changes = array(); 2058d5703f5aSAndreas Boehler 2059d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 2060d5703f5aSAndreas Boehler // last change on a node is relevant. 2061d5703f5aSAndreas Boehler foreach($arr as $row) 2062d5703f5aSAndreas Boehler { 2063d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 2064d5703f5aSAndreas Boehler } 2065d5703f5aSAndreas Boehler 2066d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 2067d5703f5aSAndreas Boehler { 2068d5703f5aSAndreas Boehler switch ($operation) 2069d5703f5aSAndreas Boehler { 2070d5703f5aSAndreas Boehler case 1 : 2071d5703f5aSAndreas Boehler $result['added'][] = $uri; 2072d5703f5aSAndreas Boehler break; 2073d5703f5aSAndreas Boehler case 2 : 2074d5703f5aSAndreas Boehler $result['modified'][] = $uri; 2075d5703f5aSAndreas Boehler break; 2076d5703f5aSAndreas Boehler case 3 : 2077d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 2078d5703f5aSAndreas Boehler break; 2079d5703f5aSAndreas Boehler } 2080d5703f5aSAndreas Boehler 2081d5703f5aSAndreas Boehler } 2082d5703f5aSAndreas Boehler } 2083d5703f5aSAndreas Boehler else 2084d5703f5aSAndreas Boehler { 2085d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 2086d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 20875f2c3e2dSAndreas Boehler $res = $sqlite->query($query); 20885f2c3e2dSAndreas Boehler $arr = $sqlite->res2arr($res); 2089d5703f5aSAndreas Boehler 2090d5703f5aSAndreas Boehler $result['added'] = $arr; 2091d5703f5aSAndreas Boehler } 2092d5703f5aSAndreas Boehler return $result; 2093d5703f5aSAndreas Boehler } 2094d5703f5aSAndreas Boehler 2095a1a3b679SAndreas Boehler} 2096