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() { 205f2c3e2dSAndreas Boehler } 215f2c3e2dSAndreas Boehler 225f2c3e2dSAndreas Boehler /** Establish and initialize the database if not already done 235f2c3e2dSAndreas Boehler * @return sqlite interface or false 245f2c3e2dSAndreas Boehler */ 255f2c3e2dSAndreas Boehler private function getDB() 265f2c3e2dSAndreas Boehler { 275f2c3e2dSAndreas Boehler if($this->sqlite === null) 285f2c3e2dSAndreas Boehler { 297e2d2436Sscottleechua $this->sqlite = new \dokuwiki\plugin\sqlite\SQLiteDB('davcal', DOKU_PLUGIN.'davcal/db/'); 30a1a3b679SAndreas Boehler if(!$this->sqlite) 31a1a3b679SAndreas Boehler { 32c42afaebSscottleechua \dokuwiki\Logger::error('DAVCAL', 'This plugin requires the sqlite plugin. Please install it.', __FILE__, __LINE__); 335f2c3e2dSAndreas Boehler msg('This plugin requires the sqlite plugin. Please install it.', -1); 345f2c3e2dSAndreas Boehler return false; 35a1a3b679SAndreas Boehler } 36a1a3b679SAndreas Boehler } 375f2c3e2dSAndreas Boehler return $this->sqlite; 385f2c3e2dSAndreas Boehler } 39a1a3b679SAndreas Boehler 40cb71a62aSAndreas Boehler /** 41185e2535SAndreas Boehler * Retrieve meta data for a given page 42185e2535SAndreas Boehler * 43185e2535SAndreas Boehler * @param string $id optional The page ID 44185e2535SAndreas Boehler * @return array The metadata 45185e2535SAndreas Boehler */ 46185e2535SAndreas Boehler private function getMeta($id = null) { 47185e2535SAndreas Boehler global $ID; 48185e2535SAndreas Boehler global $INFO; 49185e2535SAndreas Boehler 50185e2535SAndreas Boehler if ($id === null) $id = $ID; 51185e2535SAndreas Boehler 52185e2535SAndreas Boehler if($ID === $id && $INFO['meta']) { 53185e2535SAndreas Boehler $meta = $INFO['meta']; 54185e2535SAndreas Boehler } else { 55185e2535SAndreas Boehler $meta = p_get_metadata($id); 56185e2535SAndreas Boehler } 57185e2535SAndreas Boehler 58185e2535SAndreas Boehler return $meta; 59185e2535SAndreas Boehler } 60185e2535SAndreas Boehler 61185e2535SAndreas Boehler /** 62185e2535SAndreas Boehler * Retrieve the meta data for a given page 63185e2535SAndreas Boehler * 64185e2535SAndreas Boehler * @param string $id optional The page ID 65185e2535SAndreas Boehler * @return array with meta data 66185e2535SAndreas Boehler */ 67185e2535SAndreas Boehler public function getCalendarMetaForPage($id = null) 68185e2535SAndreas Boehler { 69185e2535SAndreas Boehler if(is_null($id)) 70185e2535SAndreas Boehler { 71185e2535SAndreas Boehler global $ID; 72185e2535SAndreas Boehler $id = $ID; 73185e2535SAndreas Boehler } 74185e2535SAndreas Boehler 75185e2535SAndreas Boehler $meta = $this->getMeta($id); 76185e2535SAndreas Boehler if(isset($meta['plugin_davcal'])) 77185e2535SAndreas Boehler return $meta['plugin_davcal']; 78185e2535SAndreas Boehler else 79185e2535SAndreas Boehler return array(); 80185e2535SAndreas Boehler } 81185e2535SAndreas Boehler 82185e2535SAndreas Boehler /** 83d71c9934SAndreas Boehler * Check the permission of a user for a given calendar ID 84d71c9934SAndreas Boehler * 85d71c9934SAndreas Boehler * @param string $id The calendar ID to check 86d71c9934SAndreas Boehler * @return int AUTH_* constants 87d71c9934SAndreas Boehler */ 88d71c9934SAndreas Boehler public function checkCalendarPermission($id) 89d71c9934SAndreas Boehler { 90d4992453SAndreas Boehler if(strpos($id, 'webdav://') === 0) 91d71c9934SAndreas Boehler { 92d71c9934SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 93d71c9934SAndreas Boehler if(is_null($wdc)) 94d71c9934SAndreas Boehler return AUTH_NONE; 95d4992453SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 96d71c9934SAndreas Boehler $settings = $wdc->getConnection($connectionId); 97d71c9934SAndreas Boehler if($settings === false) 98d71c9934SAndreas Boehler return AUTH_NONE; 9958d0e54eSAndreas Boehler // Check if webdavclient has permissions attached to a page 10058d0e54eSAndreas Boehler if(!empty($settings['permission'])) 10158d0e54eSAndreas Boehler { 10258d0e54eSAndreas Boehler $perm = auth_quickaclcheck($settings['permission']); 10358d0e54eSAndreas Boehler // In case the page has more than read permission, but the 10458d0e54eSAndreas Boehler // calendar is read-only, we need to modify the permission here. 10558d0e54eSAndreas Boehler if($perm > AUTH_READ && $settings['write'] == 0) 10658d0e54eSAndreas Boehler $perm = AUTH_READ; 10758d0e54eSAndreas Boehler return $perm; 10858d0e54eSAndreas Boehler } 109d71c9934SAndreas Boehler if($settings['write'] === '1') 110d71c9934SAndreas Boehler return AUTH_CREATE; 111d71c9934SAndreas Boehler return AUTH_READ; 112d71c9934SAndreas Boehler } 113d71c9934SAndreas Boehler else 114d71c9934SAndreas Boehler { 115d71c9934SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 116d71c9934SAndreas Boehler // We return AUTH_READ if the calendar does not exist. This makes 117d71c9934SAndreas Boehler // davcal happy when there are just included calendars 118d71c9934SAndreas Boehler if($calid === false) 119d71c9934SAndreas Boehler return AUTH_READ; 120d71c9934SAndreas Boehler return auth_quickaclcheck($id); 121d71c9934SAndreas Boehler } 122d71c9934SAndreas Boehler } 123d71c9934SAndreas Boehler 124d71c9934SAndreas Boehler /** 12580e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 12680e1ddf7SAndreas Boehler * user has at least read permission. 12780e1ddf7SAndreas Boehler * 12880e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 12980e1ddf7SAndreas Boehler * @return array with filtered calendar pages 13080e1ddf7SAndreas Boehler */ 13180e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 13280e1ddf7SAndreas Boehler { 13380e1ddf7SAndreas Boehler $retList = array(); 13480e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 13580e1ddf7SAndreas Boehler { 13658d0e54eSAndreas Boehler if($this->checkCalendarPermission($page) >= AUTH_READ) 13780e1ddf7SAndreas Boehler { 13880e1ddf7SAndreas Boehler $retList[$page] = $data; 13980e1ddf7SAndreas Boehler } 14080e1ddf7SAndreas Boehler } 14180e1ddf7SAndreas Boehler return $retList; 14280e1ddf7SAndreas Boehler } 14380e1ddf7SAndreas Boehler 14480e1ddf7SAndreas Boehler /** 145185e2535SAndreas Boehler * Get all calendar pages used by a given page 146185e2535SAndreas Boehler * based on the stored metadata 147185e2535SAndreas Boehler * 148185e2535SAndreas Boehler * @param string $id optional The page id 149*ebcdb60aSscottleechua * @param bool $skipPermissionCheck optional Skip permission filtering (for private URLs) 150185e2535SAndreas Boehler * @return mixed The pages as array or false 151185e2535SAndreas Boehler */ 152*ebcdb60aSscottleechua public function getCalendarPagesByMeta($id = null, $skipPermissionCheck = false) 153185e2535SAndreas Boehler { 154185e2535SAndreas Boehler if(is_null($id)) 155185e2535SAndreas Boehler { 156185e2535SAndreas Boehler global $ID; 157185e2535SAndreas Boehler $id = $ID; 158185e2535SAndreas Boehler } 159185e2535SAndreas Boehler 160185e2535SAndreas Boehler $meta = $this->getCalendarMetaForPage($id); 1610b805092SAndreas Boehler 162185e2535SAndreas Boehler if(isset($meta['id'])) 163ed764890SAndreas Boehler { 164*ebcdb60aSscottleechua if($skipPermissionCheck) 165*ebcdb60aSscottleechua { 166*ebcdb60aSscottleechua // For private URLs, return all pages without permission filtering 167*ebcdb60aSscottleechua return $meta['id']; 168*ebcdb60aSscottleechua } 169*ebcdb60aSscottleechua else 170*ebcdb60aSscottleechua { 171ed764890SAndreas Boehler // Filter the list of pages by permission 17280e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 17380e1ddf7SAndreas Boehler if(empty($pages)) 174ed764890SAndreas Boehler return false; 17580e1ddf7SAndreas Boehler return $pages; 176ed764890SAndreas Boehler } 177*ebcdb60aSscottleechua } 178185e2535SAndreas Boehler return false; 179185e2535SAndreas Boehler } 180185e2535SAndreas Boehler 181185e2535SAndreas Boehler /** 182185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 183185e2535SAndreas Boehler * for an array of page ids 184185e2535SAndreas Boehler * 185185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 186185e2535SAndreas Boehler * @return array The list 187185e2535SAndreas Boehler */ 188185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 189185e2535SAndreas Boehler { 190185e2535SAndreas Boehler $data = array(); 1914a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 192185e2535SAndreas Boehler { 1930b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1940b805092SAndreas Boehler { 1950b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1960b805092SAndreas Boehler if(is_null($wdc)) 1970b805092SAndreas Boehler continue; 1980b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1990b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 2002393a702SAndreas Boehler if($settings === false) 2012393a702SAndreas Boehler continue; 2020b805092SAndreas Boehler $name = $settings['displayname']; 203d71c9934SAndreas Boehler $write = ($settings['write'] === '1'); 2040b805092SAndreas Boehler $calid = $connectionId; 205cd2f100dSAndreas Boehler $color = '#3a87ad'; 2060b805092SAndreas Boehler } 2070b805092SAndreas Boehler else 2080b805092SAndreas Boehler { 209185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 210185e2535SAndreas Boehler if($calid !== false) 211185e2535SAndreas Boehler { 212185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 213185e2535SAndreas Boehler $name = $settings['displayname']; 214cd2f100dSAndreas Boehler $color = $settings['calendarcolor']; 215ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 2160b805092SAndreas Boehler } 2170b805092SAndreas Boehler else 2180b805092SAndreas Boehler { 2190b805092SAndreas Boehler continue; 2200b805092SAndreas Boehler } 2210b805092SAndreas Boehler } 222185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 223ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 224185e2535SAndreas Boehler } 225185e2535SAndreas Boehler return $data; 226185e2535SAndreas Boehler } 227185e2535SAndreas Boehler 228185e2535SAndreas Boehler /** 229185e2535SAndreas Boehler * Get the saved calendar color for a given page. 230185e2535SAndreas Boehler * 231185e2535SAndreas Boehler * @param string $id optional The page ID 232185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 233185e2535SAndreas Boehler */ 234185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 235185e2535SAndreas Boehler { 236185e2535SAndreas Boehler if(is_null($id)) 237185e2535SAndreas Boehler { 238185e2535SAndreas Boehler global $ID; 239185e2535SAndreas Boehler $id = $ID; 240185e2535SAndreas Boehler } 241185e2535SAndreas Boehler 242185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 243185e2535SAndreas Boehler if($calid === false) 244185e2535SAndreas Boehler return false; 245185e2535SAndreas Boehler 246185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 247185e2535SAndreas Boehler } 248185e2535SAndreas Boehler 249185e2535SAndreas Boehler /** 250185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 251185e2535SAndreas Boehler * 252185e2535SAndreas Boehler * @param string $id optional The calendar ID 253185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 254185e2535SAndreas Boehler */ 255185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 256185e2535SAndreas Boehler { 257185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 258185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 259185e2535SAndreas Boehler 260185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 261185e2535SAndreas Boehler 262185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 263185e2535SAndreas Boehler return false; 264185e2535SAndreas Boehler 265185e2535SAndreas Boehler $color = $row['calendarcolor']; 266185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 267185e2535SAndreas Boehler return $color; 268185e2535SAndreas Boehler } 269185e2535SAndreas Boehler 270185e2535SAndreas Boehler /** 271e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 272e86c8dd3SAndreas Boehler * @param string $user the user name 273e86c8dd3SAndreas Boehler * @return the URL to the principal sync 274e86c8dd3SAndreas Boehler */ 275e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 276e86c8dd3SAndreas Boehler { 277e86c8dd3SAndreas Boehler if(is_null($user)) 278e86c8dd3SAndreas Boehler return false; 279e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 280e86c8dd3SAndreas Boehler return $url; 281e86c8dd3SAndreas Boehler } 282e86c8dd3SAndreas Boehler 283e86c8dd3SAndreas Boehler /** 284185e2535SAndreas Boehler * Set the calendar color for a given page. 285185e2535SAndreas Boehler * 286185e2535SAndreas Boehler * @param string $color The color definition 287185e2535SAndreas Boehler * @param string $id optional The page ID 288185e2535SAndreas Boehler * @return boolean True on success, otherwise false 289185e2535SAndreas Boehler */ 290185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 291185e2535SAndreas Boehler { 292185e2535SAndreas Boehler if(is_null($id)) 293185e2535SAndreas Boehler { 294185e2535SAndreas Boehler global $ID; 295185e2535SAndreas Boehler $id = $ID; 296185e2535SAndreas Boehler } 297185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 298185e2535SAndreas Boehler if($calid === false) 299185e2535SAndreas Boehler return false; 300185e2535SAndreas Boehler 3015f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3025f2c3e2dSAndreas Boehler if(!$sqlite) 3035f2c3e2dSAndreas Boehler return false; 30451f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 30551f4febbSAndreas Boehler " WHERE id = ?"; 3065f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $color, $calid); 307185e2535SAndreas Boehler if($res !== false) 308185e2535SAndreas Boehler { 309185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 310185e2535SAndreas Boehler return true; 311185e2535SAndreas Boehler } 312185e2535SAndreas Boehler return false; 313185e2535SAndreas Boehler } 314185e2535SAndreas Boehler 315185e2535SAndreas Boehler /** 316cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 317cb71a62aSAndreas Boehler * page id. 318cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 319cb71a62aSAndreas Boehler * 320cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 321cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 322cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 323cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 324cb71a62aSAndreas Boehler * 325cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 326cb71a62aSAndreas Boehler */ 327a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 328a1a3b679SAndreas Boehler { 329a1a3b679SAndreas Boehler if(is_null($id)) 330a1a3b679SAndreas Boehler { 331a1a3b679SAndreas Boehler global $ID; 332a1a3b679SAndreas Boehler $id = $ID; 333a1a3b679SAndreas Boehler } 334a1a3b679SAndreas Boehler if(is_null($userid)) 33534a47953SAndreas Boehler { 33634a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 33734a47953SAndreas Boehler { 338a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 33934a47953SAndreas Boehler } 34034a47953SAndreas Boehler else 34134a47953SAndreas Boehler { 34234a47953SAndreas Boehler $userid = uniqid('davcal-'); 34334a47953SAndreas Boehler } 34434a47953SAndreas Boehler } 345a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 346a1a3b679SAndreas Boehler if($calid === false) 347a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 348a1a3b679SAndreas Boehler 3495f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3505f2c3e2dSAndreas Boehler if(!$sqlite) 3515f2c3e2dSAndreas Boehler return false; 35251f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 3535f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $name, $description, $calid); 354b269830cSAndreas Boehler if($res !== false) 355b269830cSAndreas Boehler return true; 356b269830cSAndreas Boehler return false; 357a1a3b679SAndreas Boehler } 358a1a3b679SAndreas Boehler 359cb71a62aSAndreas Boehler /** 360d5703f5aSAndreas Boehler * Update a calendar's displayname 361d5703f5aSAndreas Boehler * 362d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 363d5703f5aSAndreas Boehler * @param string $name The new calendar name 364d5703f5aSAndreas Boehler * 365d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 366d5703f5aSAndreas Boehler */ 367d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 368d5703f5aSAndreas Boehler { 3695f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3705f2c3e2dSAndreas Boehler if(!$sqlite) 3715f2c3e2dSAndreas Boehler return false; 372d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 3735f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $name); 374d5703f5aSAndreas Boehler if($res !== false) 375d5703f5aSAndreas Boehler { 376d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 377d5703f5aSAndreas Boehler return true; 378d5703f5aSAndreas Boehler } 379d5703f5aSAndreas Boehler return false; 380d5703f5aSAndreas Boehler } 381d5703f5aSAndreas Boehler 382d5703f5aSAndreas Boehler /** 383d5703f5aSAndreas Boehler * Update the calendar description 384d5703f5aSAndreas Boehler * 385d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 386d5703f5aSAndreas Boehler * @param string $description The new calendar's description 387d5703f5aSAndreas Boehler * 388d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 389d5703f5aSAndreas Boehler */ 390d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 391d5703f5aSAndreas Boehler { 3925f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3935f2c3e2dSAndreas Boehler if(!$sqlite) 3945f2c3e2dSAndreas Boehler return false; 395d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 3965f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $description); 397d5703f5aSAndreas Boehler if($res !== false) 398d5703f5aSAndreas Boehler { 399d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 400d5703f5aSAndreas Boehler return true; 401d5703f5aSAndreas Boehler } 402d5703f5aSAndreas Boehler return false; 403d5703f5aSAndreas Boehler } 404d5703f5aSAndreas Boehler 405d5703f5aSAndreas Boehler /** 406d5703f5aSAndreas Boehler * Update a calendar's timezone information 407d5703f5aSAndreas Boehler * 408d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 409d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 410d5703f5aSAndreas Boehler * 411d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 412d5703f5aSAndreas Boehler */ 413d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 414d5703f5aSAndreas Boehler { 4155f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4165f2c3e2dSAndreas Boehler if(!$sqlite) 4175f2c3e2dSAndreas Boehler return false; 418d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 4195f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $timezone); 420d5703f5aSAndreas Boehler if($res !== false) 421d5703f5aSAndreas Boehler { 422d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 423d5703f5aSAndreas Boehler return true; 424d5703f5aSAndreas Boehler } 425d5703f5aSAndreas Boehler return false; 426d5703f5aSAndreas Boehler } 427d5703f5aSAndreas Boehler 428d5703f5aSAndreas Boehler /** 429cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 430cb71a62aSAndreas Boehler * 431cb71a62aSAndreas Boehler * @param array $settings The settings array to store 432cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 433cb71a62aSAndreas Boehler * 434cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 435cb71a62aSAndreas Boehler */ 436a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 437a495d34cSAndreas Boehler { 438a495d34cSAndreas Boehler if(is_null($userid)) 43934a47953SAndreas Boehler { 44034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 44134a47953SAndreas Boehler { 442a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 44334a47953SAndreas Boehler } 44434a47953SAndreas Boehler else 44534a47953SAndreas Boehler { 44634a47953SAndreas Boehler return false; 44734a47953SAndreas Boehler } 44834a47953SAndreas Boehler } 4495f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4505f2c3e2dSAndreas Boehler if(!$sqlite) 4515f2c3e2dSAndreas Boehler return false; 4525f2c3e2dSAndreas Boehler $sqlite->query("BEGIN TRANSACTION"); 453a495d34cSAndreas Boehler 45451f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 4555f2c3e2dSAndreas Boehler $sqlite->query($query, $userid); 456bd883736SAndreas Boehler 457a495d34cSAndreas Boehler foreach($settings as $key => $value) 458a495d34cSAndreas Boehler { 45951f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 4605f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $userid, $key, $value); 461a495d34cSAndreas Boehler if($res === false) 462a495d34cSAndreas Boehler return false; 463a495d34cSAndreas Boehler } 4645f2c3e2dSAndreas Boehler $sqlite->query("COMMIT TRANSACTION"); 465185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 466a495d34cSAndreas Boehler return true; 467a495d34cSAndreas Boehler } 468a495d34cSAndreas Boehler 469cb71a62aSAndreas Boehler /** 470cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 471cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 472cb71a62aSAndreas Boehler * 473cb71a62aSAndreas Boehler * timezone => local 474cb71a62aSAndreas Boehler * weeknumbers => 0 475cb71a62aSAndreas Boehler * workweek => 0 476cb71a62aSAndreas Boehler * 477cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 478cb71a62aSAndreas Boehler * 479cb71a62aSAndreas Boehler * @return array The settings array 480cb71a62aSAndreas Boehler */ 481a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 482a495d34cSAndreas Boehler { 483bd883736SAndreas Boehler $settings = array( 484fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 485fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 486fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4871d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4881d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 489bd883736SAndreas Boehler ); 49034a47953SAndreas Boehler if(is_null($userid)) 49134a47953SAndreas Boehler { 49234a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 49334a47953SAndreas Boehler { 49434a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 49534a47953SAndreas Boehler } 49634a47953SAndreas Boehler else 49734a47953SAndreas Boehler { 49834a47953SAndreas Boehler return $settings; 49934a47953SAndreas Boehler } 50034a47953SAndreas Boehler } 50134a47953SAndreas Boehler 5025f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5035f2c3e2dSAndreas Boehler if(!$sqlite) 5045f2c3e2dSAndreas Boehler return false; 50534a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 50634a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 50751f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 50861fec75eSscottleechua $arr = $sqlite->queryAll($query, [$userid]); 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 = ?"; 54061fec75eSscottleechua $row = $sqlite->queryRecord($query, [$id]); 541a1a3b679SAndreas Boehler if(isset($row['calid'])) 542185e2535SAndreas Boehler { 543185e2535SAndreas Boehler $calid = $row['calid']; 544185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 545185e2535SAndreas Boehler return $calid; 546185e2535SAndreas Boehler } 547a1a3b679SAndreas Boehler return false; 548a1a3b679SAndreas Boehler } 549a1a3b679SAndreas Boehler 550cb71a62aSAndreas Boehler /** 551cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 552cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 553cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 554cb71a62aSAndreas Boehler * 555cb71a62aSAndreas Boehler * @return array The mapping array 556cb71a62aSAndreas Boehler */ 557a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 558a1a3b679SAndreas Boehler { 5595f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5605f2c3e2dSAndreas Boehler if(!$sqlite) 5615f2c3e2dSAndreas Boehler return false; 562a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 56361fec75eSscottleechua $arr = $sqlite->queryAll($query, []); 564a1a3b679SAndreas Boehler return $arr; 565a1a3b679SAndreas Boehler } 566a1a3b679SAndreas Boehler 567cb71a62aSAndreas Boehler /** 568cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 569cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 570cb71a62aSAndreas Boehler * user name is actually split from the URI component. 571cb71a62aSAndreas Boehler * 572cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 573cb71a62aSAndreas Boehler * and applied accordingly. 574cb71a62aSAndreas Boehler * 575cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 576cb71a62aSAndreas Boehler * 577cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 578cb71a62aSAndreas Boehler */ 579a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 580a1a3b679SAndreas Boehler { 58134a47953SAndreas Boehler global $auth; 582a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 583a1a3b679SAndreas Boehler $user = end($user); 584a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 585a1a3b679SAndreas Boehler $calids = array(); 58634a47953SAndreas Boehler $ud = $auth->getUserData($user); 58734a47953SAndreas Boehler $groups = $ud['grps']; 588a1a3b679SAndreas Boehler foreach($mapping as $row) 589a1a3b679SAndreas Boehler { 590a1a3b679SAndreas Boehler $id = $row['calid']; 59113b16484SAndreas Boehler $enabled = $this->getCalendarStatus($id); 59213b16484SAndreas Boehler if($enabled == false) 59313b16484SAndreas Boehler continue; 594a1a3b679SAndreas Boehler $page = $row['page']; 59534a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 596a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 597a1a3b679SAndreas Boehler { 598a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 599a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 600a1a3b679SAndreas Boehler } 601a1a3b679SAndreas Boehler } 602a1a3b679SAndreas Boehler return $calids; 603a1a3b679SAndreas Boehler } 604a1a3b679SAndreas Boehler 605cb71a62aSAndreas Boehler /** 606cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 607cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 608cb71a62aSAndreas Boehler * 609cb71a62aSAndreas Boehler * @param string $name The calendar's name 610cb71a62aSAndreas Boehler * @param string $description The calendar's description 611cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 612cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 613cb71a62aSAndreas Boehler * 614cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 615cb71a62aSAndreas Boehler */ 616a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 617a1a3b679SAndreas Boehler { 618a1a3b679SAndreas Boehler if(is_null($id)) 619a1a3b679SAndreas Boehler { 620a1a3b679SAndreas Boehler global $ID; 621a1a3b679SAndreas Boehler $id = $ID; 622a1a3b679SAndreas Boehler } 623a1a3b679SAndreas Boehler if(is_null($userid)) 62434a47953SAndreas Boehler { 62534a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 62634a47953SAndreas Boehler { 627a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 62834a47953SAndreas Boehler } 62934a47953SAndreas Boehler else 63034a47953SAndreas Boehler { 63134a47953SAndreas Boehler $userid = uniqid('davcal-'); 63234a47953SAndreas Boehler } 63334a47953SAndreas Boehler } 634a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 635a1a3b679SAndreas Boehler $name, 636a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 637a1a3b679SAndreas Boehler $description, 638a1a3b679SAndreas Boehler 'VEVENT,VTODO', 63955a741c0SAndreas Boehler 0, 64055a741c0SAndreas Boehler 1); 6415f2c3e2dSAndreas Boehler 6425f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6435f2c3e2dSAndreas Boehler if(!$sqlite) 6445f2c3e2dSAndreas Boehler return false; 64551f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 64651f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 64761fec75eSscottleechua $sqlite->query($query, $values); 648cb71a62aSAndreas Boehler 64951f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 65051f4febbSAndreas Boehler "uri = ? AND description = ?"; 65161fec75eSscottleechua $row = $sqlite->queryRecord($query, array($values[0], $values[1], $values[2], $values[3])); 652cb71a62aSAndreas Boehler 653a1a3b679SAndreas Boehler if(isset($row['id'])) 654a1a3b679SAndreas Boehler { 65551f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 65661fec75eSscottleechua $sqlite->query($query, array($id, $row['id'])); 65761fec75eSscottleechua return true; 658a1a3b679SAndreas Boehler } 659a1a3b679SAndreas Boehler 660a1a3b679SAndreas Boehler return false; 661a1a3b679SAndreas Boehler } 662a1a3b679SAndreas Boehler 663cb71a62aSAndreas Boehler /** 664d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 665d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 666d5703f5aSAndreas Boehler * 667d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 668d5703f5aSAndreas Boehler * 669d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 670d5703f5aSAndreas Boehler * @param string $uri The new object URI 671d5703f5aSAndreas Boehler * @param string $ics The ICS file 672d5703f5aSAndreas Boehler * 673d5703f5aSAndreas Boehler * @return mixed The etag. 674d5703f5aSAndreas Boehler */ 675d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 676d5703f5aSAndreas Boehler { 677d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 678d5703f5aSAndreas Boehler 6795f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6805f2c3e2dSAndreas Boehler if(!$sqlite) 6815f2c3e2dSAndreas Boehler return false; 682d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 6835f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 684d5703f5aSAndreas Boehler $calid, 685d5703f5aSAndreas Boehler $uri, 686d5703f5aSAndreas Boehler $ics, 687d5703f5aSAndreas Boehler time(), 688d5703f5aSAndreas Boehler $extraData['etag'], 689d5703f5aSAndreas Boehler $extraData['size'], 690d5703f5aSAndreas Boehler $extraData['componentType'], 691d5703f5aSAndreas Boehler $extraData['firstOccurence'], 692d5703f5aSAndreas Boehler $extraData['lastOccurence'], 693d5703f5aSAndreas Boehler $extraData['uid']); 694d5703f5aSAndreas Boehler // If successfully, update the sync token database 695d5703f5aSAndreas Boehler if($res !== false) 696d5703f5aSAndreas Boehler { 697d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 698d5703f5aSAndreas Boehler } 699d5703f5aSAndreas Boehler return $extraData['etag']; 700d5703f5aSAndreas Boehler } 701d5703f5aSAndreas Boehler 702d5703f5aSAndreas Boehler /** 703d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 704d5703f5aSAndreas Boehler * needed for the sync support. 705d5703f5aSAndreas Boehler * 706d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 707d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 708d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 709d5703f5aSAndreas Boehler */ 710d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 711d5703f5aSAndreas Boehler { 712d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 713d5703f5aSAndreas Boehler 7145f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 7155f2c3e2dSAndreas Boehler if(!$sqlite) 7165f2c3e2dSAndreas Boehler return false; 717d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 7185f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 719d5703f5aSAndreas Boehler $ics, 720d5703f5aSAndreas Boehler time(), 721d5703f5aSAndreas Boehler $extraData['etag'], 722d5703f5aSAndreas Boehler $extraData['size'], 723d5703f5aSAndreas Boehler $extraData['componentType'], 724d5703f5aSAndreas Boehler $extraData['firstOccurence'], 725d5703f5aSAndreas Boehler $extraData['lastOccurence'], 726d5703f5aSAndreas Boehler $extraData['uid'], 727d5703f5aSAndreas Boehler $calid, 728d5703f5aSAndreas Boehler $uri 729d5703f5aSAndreas Boehler ); 730d5703f5aSAndreas Boehler if($res !== false) 731d5703f5aSAndreas Boehler { 732d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 733d5703f5aSAndreas Boehler } 734d5703f5aSAndreas Boehler return $extraData['etag']; 735d5703f5aSAndreas Boehler } 736d5703f5aSAndreas Boehler 737d5703f5aSAndreas Boehler /** 738cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 739cb71a62aSAndreas Boehler * 740cb71a62aSAndreas Boehler * The parameter array needs to contain 741cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 74282a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 743cb71a62aSAndreas Boehler * eventfrom => The event's start date 744cb71a62aSAndreas Boehler * eventfromtime => The event's start time 745cb71a62aSAndreas Boehler * eventto => The event's end date 746cb71a62aSAndreas Boehler * eventtotime => The event's end time 747cb71a62aSAndreas Boehler * eventname => The event's name 748cb71a62aSAndreas Boehler * eventdescription => The event's description 749cb71a62aSAndreas Boehler * 750cb71a62aSAndreas Boehler * @param string $id The page ID to work on 751cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 752cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 753cb71a62aSAndreas Boehler * 754cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 755cb71a62aSAndreas Boehler */ 756a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 757a1a3b679SAndreas Boehler { 75882a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 75982a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 76082a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 761a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 762bd883736SAndreas Boehler else 763bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 764cb71a62aSAndreas Boehler 765cb71a62aSAndreas Boehler // Retrieve dates from settings 766b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 767b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 768b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 769b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 770cb71a62aSAndreas Boehler 771cb71a62aSAndreas Boehler // Load SabreDAV 7729bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 773a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 774cb71a62aSAndreas Boehler 775cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 776a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 777b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 778b269830cSAndreas Boehler $event->add('UID', $uuid); 779a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 780cb71a62aSAndreas Boehler 781cb71a62aSAndreas Boehler // Add a description if requested 7820eebc909SAndreas Boehler $description = $params['eventdescription']; 7830eebc909SAndreas Boehler if($description !== '') 7840eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 785cb71a62aSAndreas Boehler 7862b7be5bdSAndreas Boehler // Add a location if requested 7872b7be5bdSAndreas Boehler $location = $params['eventlocation']; 7882b7be5bdSAndreas Boehler if($location !== '') 7892b7be5bdSAndreas Boehler $event->add('LOCATION', $location); 7902b7be5bdSAndreas Boehler 7914ecb526cSAndreas Boehler // Add attachments 7924ecb526cSAndreas Boehler $attachments = $params['attachments']; 79382a48dfbSAndreas Boehler if(!is_null($attachments)) 7944ecb526cSAndreas Boehler foreach($attachments as $attachment) 7954ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 7964ecb526cSAndreas Boehler 797cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 798b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 799b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 800b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 801b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 802cb71a62aSAndreas Boehler 803cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 804b269830cSAndreas Boehler $dtStart = new \DateTime(); 805a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 806b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 807cb71a62aSAndreas Boehler 808cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 809b269830cSAndreas Boehler if($params['allday'] != '1') 810b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 811cb71a62aSAndreas Boehler 812cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 813b269830cSAndreas Boehler $dtEnd = new \DateTime(); 814a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 815b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 816cb71a62aSAndreas Boehler 817cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 818b269830cSAndreas Boehler if($params['allday'] != '1') 819b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 820cb71a62aSAndreas Boehler 821b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 822b269830cSAndreas Boehler if($params['allday'] == '1') 823b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 824cb71a62aSAndreas Boehler 825cb71a62aSAndreas Boehler // Really add Start and End events 826b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 827b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 828cb71a62aSAndreas Boehler 829cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 830b269830cSAndreas Boehler if($params['allday'] == '1') 831b269830cSAndreas Boehler { 832b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 833b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 834b269830cSAndreas Boehler } 835cb71a62aSAndreas Boehler 836809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 837809cb0faSAndreas Boehler 838809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 839809cb0faSAndreas Boehler { 840809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 841809cb0faSAndreas Boehler if(is_null($wdc)) 842809cb0faSAndreas Boehler return false; 843809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 844809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 845809cb0faSAndreas Boehler } 846809cb0faSAndreas Boehler else 847809cb0faSAndreas Boehler { 848cb71a62aSAndreas Boehler // Actually add the values to the database 849a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 85002eea9b8SAndreas Boehler $uri = $uri = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 8511bb22c2bSAndreas Boehler $now = new \DateTime(); 852a1a3b679SAndreas Boehler 8535f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8545f2c3e2dSAndreas Boehler if(!$sqlite) 8555f2c3e2dSAndreas Boehler return false; 85651f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 8575f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 85851f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 85951f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 860cb71a62aSAndreas Boehler 861cb71a62aSAndreas Boehler // If successfully, update the sync token database 86255a741c0SAndreas Boehler if($res !== false) 86355a741c0SAndreas Boehler { 86455a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 865a1a3b679SAndreas Boehler return true; 866a1a3b679SAndreas Boehler } 867809cb0faSAndreas Boehler } 86855a741c0SAndreas Boehler return false; 86955a741c0SAndreas Boehler } 870a1a3b679SAndreas Boehler 871cb71a62aSAndreas Boehler /** 872cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 873cb71a62aSAndreas Boehler * 874cb71a62aSAndreas Boehler * @param string $calid The calendar ID 875cb71a62aSAndreas Boehler * 876cb71a62aSAndreas Boehler * @return array The calendar settings array 877cb71a62aSAndreas Boehler */ 878b269830cSAndreas Boehler public function getCalendarSettings($calid) 879b269830cSAndreas Boehler { 8805f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8815f2c3e2dSAndreas Boehler if(!$sqlite) 8825f2c3e2dSAndreas Boehler return false; 883d99db92eSscottleechua $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled, timezone FROM calendars WHERE id= ? "; 88461fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 885b269830cSAndreas Boehler return $row; 886b269830cSAndreas Boehler } 887b269830cSAndreas Boehler 888cb71a62aSAndreas Boehler /** 88913b16484SAndreas Boehler * Retrieve the calendar status of a given calendar id 89013b16484SAndreas Boehler * 89113b16484SAndreas Boehler * @param string $calid The calendar ID 89213b16484SAndreas Boehler * @return boolean True if calendar is enabled, otherwise false 89313b16484SAndreas Boehler */ 89413b16484SAndreas Boehler public function getCalendarStatus($calid) 89513b16484SAndreas Boehler { 8965f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8975f2c3e2dSAndreas Boehler if(!$sqlite) 8985f2c3e2dSAndreas Boehler return false; 89913b16484SAndreas Boehler $query = "SELECT disabled FROM calendars WHERE id = ?"; 90061fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 90161fec75eSscottleechua if($row && $row['disabled'] == 1) 90213b16484SAndreas Boehler return false; 90313b16484SAndreas Boehler else 90413b16484SAndreas Boehler return true; 90513b16484SAndreas Boehler } 90613b16484SAndreas Boehler 90713b16484SAndreas Boehler /** 90813b16484SAndreas Boehler * Disable a calendar for a given page 90913b16484SAndreas Boehler * 91013b16484SAndreas Boehler * @param string $id The page ID 91113b16484SAndreas Boehler * 91213b16484SAndreas Boehler * @return boolean true on success, otherwise false 91313b16484SAndreas Boehler */ 91413b16484SAndreas Boehler public function disableCalendarForPage($id) 91513b16484SAndreas Boehler { 91613b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 91713b16484SAndreas Boehler if($calid === false) 91813b16484SAndreas Boehler return false; 9195f2c3e2dSAndreas Boehler 9205f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9215f2c3e2dSAndreas Boehler if(!$sqlite) 9225f2c3e2dSAndreas Boehler return false; 92313b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 1 WHERE id = ?"; 9245f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 92513b16484SAndreas Boehler if($res !== false) 92613b16484SAndreas Boehler return true; 92713b16484SAndreas Boehler return false; 92813b16484SAndreas Boehler } 92913b16484SAndreas Boehler 93013b16484SAndreas Boehler /** 93113b16484SAndreas Boehler * Enable a calendar for a given page 93213b16484SAndreas Boehler * 93313b16484SAndreas Boehler * @param string $id The page ID 93413b16484SAndreas Boehler * 93513b16484SAndreas Boehler * @return boolean true on success, otherwise false 93613b16484SAndreas Boehler */ 93713b16484SAndreas Boehler public function enableCalendarForPage($id) 93813b16484SAndreas Boehler { 93913b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 94013b16484SAndreas Boehler if($calid === false) 94113b16484SAndreas Boehler return false; 9425f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9435f2c3e2dSAndreas Boehler if(!$sqlite) 9445f2c3e2dSAndreas Boehler return false; 94513b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 0 WHERE id = ?"; 9465f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 94713b16484SAndreas Boehler if($res !== false) 94813b16484SAndreas Boehler return true; 94913b16484SAndreas Boehler return false; 95013b16484SAndreas Boehler } 95113b16484SAndreas Boehler 95213b16484SAndreas Boehler /** 953cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 954cb71a62aSAndreas Boehler * based on the timezone setting. 955cb71a62aSAndreas Boehler * 956cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 957cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 958cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 959cb71a62aSAndreas Boehler * 960cb71a62aSAndreas Boehler * @param string $id The page ID to work with 961cb71a62aSAndreas Boehler * @param string $user The user ID to work with 962cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 963cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 9644a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 965cb71a62aSAndreas Boehler * 966cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 967cb71a62aSAndreas Boehler */ 9684a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 969a1a3b679SAndreas Boehler { 97082a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 97182a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 972bd883736SAndreas Boehler else 973bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 974a1a3b679SAndreas Boehler $data = array(); 975b8265976SAndreas Boehler $calname = 'unknown'; 976cb71a62aSAndreas Boehler 9775f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9785f2c3e2dSAndreas Boehler if(!$sqlite) 9795f2c3e2dSAndreas Boehler return false; 9805f2c3e2dSAndreas Boehler 98161fec75eSscottleechua $calid = $this->getCalendarIdForPage($id); 982a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 9837e2d2436Sscottleechua $params = array($calid); 984a469597cSAndreas Boehler $startTs = null; 985a469597cSAndreas Boehler $endTs = null; 986a469597cSAndreas Boehler if($startDate !== null) 987a469597cSAndreas Boehler { 988a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 9897e2d2436Sscottleechua $query .= " AND lastoccurence > ?"; 9907e2d2436Sscottleechua $params[] = $startTs->getTimestamp(); 991a469597cSAndreas Boehler } 992a469597cSAndreas Boehler if($endDate !== null) 993a469597cSAndreas Boehler { 994a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 9957e2d2436Sscottleechua $query .= " AND firstoccurence < ?"; 9967e2d2436Sscottleechua $params[] = $endTs->getTimestamp(); 997a469597cSAndreas Boehler } 998cb71a62aSAndreas Boehler 9990b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 10000b805092SAndreas Boehler 10010b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 10020b805092SAndreas Boehler { 10030b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 10040b805092SAndreas Boehler if(is_null($wdc)) 10050b805092SAndreas Boehler return $data; 10060b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 10070b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 1008b8265976SAndreas Boehler $conn = $wdc->getConnection($connectionId); 1009b8265976SAndreas Boehler $calname = $conn['displayname']; 10100b805092SAndreas Boehler } 10110b805092SAndreas Boehler else 10120b805092SAndreas Boehler { 10130b805092SAndreas Boehler if(is_null($color)) 10140b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 10150b805092SAndreas Boehler 101659b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 101759b68239SAndreas Boehler if($enabled === false) 101859b68239SAndreas Boehler return $data; 101959b68239SAndreas Boehler 1020b8265976SAndreas Boehler $settings = $this->getCalendarSettings($calid); 1021b8265976SAndreas Boehler $calname = $settings['displayname']; 1022b8265976SAndreas Boehler 102361fec75eSscottleechua $arr = $sqlite->queryAll($query, $params); 10240b805092SAndreas Boehler } 1025cb71a62aSAndreas Boehler 1026a1a3b679SAndreas Boehler foreach($arr as $row) 1027a1a3b679SAndreas Boehler { 1028a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 1029a1a3b679SAndreas Boehler { 1030b269830cSAndreas Boehler $entry = array(); 1031a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 1032ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 1033ebc4eb57SAndreas Boehler if($recurrence != null) 1034ebc4eb57SAndreas Boehler { 1035ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 1036ebc4eb57SAndreas Boehler $rEvents->rewind(); 1037e9b7d302SAndreas Boehler while($rEvents->valid()) 1038ebc4eb57SAndreas Boehler { 1039ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 1040a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 1041e9b7d302SAndreas Boehler break; 1042a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 1043ebc4eb57SAndreas Boehler { 1044ebc4eb57SAndreas Boehler $rEvents->next(); 1045ebc4eb57SAndreas Boehler continue; 1046ebc4eb57SAndreas Boehler } 1047b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1048b8265976SAndreas Boehler $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true)); 1049ebc4eb57SAndreas Boehler $rEvents->next(); 1050ebc4eb57SAndreas Boehler } 1051ebc4eb57SAndreas Boehler } 1052ebc4eb57SAndreas Boehler else 1053b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1054b8265976SAndreas Boehler $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color)); 1055ebc4eb57SAndreas Boehler } 1056ebc4eb57SAndreas Boehler } 1057ebc4eb57SAndreas Boehler return $data; 1058ebc4eb57SAndreas Boehler } 1059ebc4eb57SAndreas Boehler 1060cb71a62aSAndreas Boehler /** 1061cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 1062cb71a62aSAndreas Boehler * 1063cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 1064cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 1065cb71a62aSAndreas Boehler * @param string $uid The entry's UID 10663c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 1067cb71a62aSAndreas Boehler * 1068cb71a62aSAndreas Boehler * @return array The parse calendar entry 1069cb71a62aSAndreas Boehler */ 1070185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 1071ebc4eb57SAndreas Boehler { 1072ebc4eb57SAndreas Boehler $entry = array(); 1073ebc4eb57SAndreas Boehler $start = $event->DTSTART; 1074cb71a62aSAndreas Boehler // Parse only if the start date/time is present 1075b269830cSAndreas Boehler if($start !== null) 1076b269830cSAndreas Boehler { 1077b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 1078b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 1079bf0ad2b4SAndreas Boehler 1080bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 1081bf0ad2b4SAndreas Boehler // allDay is set to true 1082bf0ad2b4SAndreas Boehler // This should fix T23 1083b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 1084bf0ad2b4SAndreas Boehler { 1085b269830cSAndreas Boehler $entry['allDay'] = true; 1086bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 1087bf0ad2b4SAndreas Boehler } 1088b269830cSAndreas Boehler else 1089bf0ad2b4SAndreas Boehler { 1090b269830cSAndreas Boehler $entry['allDay'] = false; 1091bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 1092bf0ad2b4SAndreas Boehler } 1093b269830cSAndreas Boehler } 1094ebc4eb57SAndreas Boehler $end = $event->DTEND; 1095bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 1096b269830cSAndreas Boehler if($end !== null) 1097b269830cSAndreas Boehler { 1098b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 1099b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 1100bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 1101bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 1102bf0ad2b4SAndreas Boehler else 1103b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1104b269830cSAndreas Boehler } 1105954f1ffaSAndreas Boehler $duration = $event->DURATION; 1106954f1ffaSAndreas Boehler // Parse duration only if start is set, but end is missing 1107954f1ffaSAndreas Boehler if($start !== null && $end == null && $duration !== null) 1108954f1ffaSAndreas Boehler { 1109954f1ffaSAndreas Boehler $interval = $duration->getDateInterval(); 1110954f1ffaSAndreas Boehler $dtStart = $start->getDateTime(); 1111954f1ffaSAndreas Boehler $dtStart->setTimezone($timezone); 1112954f1ffaSAndreas Boehler $dtEnd = $dtStart->add($interval); 1113954f1ffaSAndreas Boehler $dtEnd->setTimezone($timezone); 1114954f1ffaSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1115954f1ffaSAndreas Boehler } 1116ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 11170eebc909SAndreas Boehler if($description !== null) 11180eebc909SAndreas Boehler $entry['description'] = (string)$description; 11190eebc909SAndreas Boehler else 11200eebc909SAndreas Boehler $entry['description'] = ''; 11214ecb526cSAndreas Boehler $attachments = $event->ATTACH; 11224ecb526cSAndreas Boehler if($attachments !== null) 11234ecb526cSAndreas Boehler { 11244ecb526cSAndreas Boehler $entry['attachments'] = array(); 11254ecb526cSAndreas Boehler foreach($attachments as $attachment) 11264ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 11274ecb526cSAndreas Boehler } 1128ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 11292b7be5bdSAndreas Boehler $entry['location'] = (string)$event->location; 1130ebc4eb57SAndreas Boehler $entry['id'] = $uid; 1131185e2535SAndreas Boehler $entry['page'] = $page; 1132185e2535SAndreas Boehler $entry['color'] = $color; 11333c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 1134185e2535SAndreas Boehler 1135ebc4eb57SAndreas Boehler return $entry; 1136a1a3b679SAndreas Boehler } 1137a1a3b679SAndreas Boehler 1138cb71a62aSAndreas Boehler /** 1139cb71a62aSAndreas Boehler * Retrieve an event by its UID 1140cb71a62aSAndreas Boehler * 1141cb71a62aSAndreas Boehler * @param string $uid The event's UID 1142cb71a62aSAndreas Boehler * 1143cb71a62aSAndreas Boehler * @return mixed The table row with the given event 1144cb71a62aSAndreas Boehler */ 1145a1a3b679SAndreas Boehler public function getEventWithUid($uid) 1146a1a3b679SAndreas Boehler { 11475f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11485f2c3e2dSAndreas Boehler if(!$sqlite) 11495f2c3e2dSAndreas Boehler return false; 115051f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 115161fec75eSscottleechua $row = $sqlite->queryRecord($query, [$uid]); 1152a1a3b679SAndreas Boehler return $row; 1153a1a3b679SAndreas Boehler } 1154a1a3b679SAndreas Boehler 1155cb71a62aSAndreas Boehler /** 1156d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 115759b68239SAndreas Boehler * calendar data! This is mainly needed for the sync support. 1158d5703f5aSAndreas Boehler * 1159d5703f5aSAndreas Boehler * @param int $calid The calendar ID 1160d5703f5aSAndreas Boehler * 1161d5703f5aSAndreas Boehler * @return mixed The result 1162d5703f5aSAndreas Boehler */ 1163d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 1164d5703f5aSAndreas Boehler { 11655f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11665f2c3e2dSAndreas Boehler if(!$sqlite) 11675f2c3e2dSAndreas Boehler return false; 1168d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 116961fec75eSscottleechua $arr = $sqlite->queryAll($query, [$calid]); 1170d5703f5aSAndreas Boehler return $arr; 1171d5703f5aSAndreas Boehler } 1172d5703f5aSAndreas Boehler 1173d5703f5aSAndreas Boehler /** 1174d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 1175d5703f5aSAndreas Boehler * 1176d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1177d5703f5aSAndreas Boehler * @param string $uri The object's URI 1178d5703f5aSAndreas Boehler * 1179d5703f5aSAndreas Boehler * @return mixed The result 1180d5703f5aSAndreas Boehler */ 1181d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1182d5703f5aSAndreas Boehler { 11835f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11845f2c3e2dSAndreas Boehler if(!$sqlite) 11855f2c3e2dSAndreas Boehler return false; 1186d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 118761fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid, $uri]); 1188d5703f5aSAndreas Boehler return $row; 1189d5703f5aSAndreas Boehler } 1190d5703f5aSAndreas Boehler 1191d5703f5aSAndreas Boehler /** 1192d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1193d5703f5aSAndreas Boehler * This is mainly neede for sync. 1194d5703f5aSAndreas Boehler * 1195d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1196d5703f5aSAndreas Boehler * @param array $uris An array of URIs 1197d5703f5aSAndreas Boehler * 1198d5703f5aSAndreas Boehler * @return mixed The result 1199d5703f5aSAndreas Boehler */ 1200d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1201d5703f5aSAndreas Boehler { 12025f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12035f2c3e2dSAndreas Boehler if(!$sqlite) 12045f2c3e2dSAndreas Boehler return false; 1205d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1206d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1207d5703f5aSAndreas Boehler $query .= ')'; 1208d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 120961fec75eSscottleechua $arr = $sqlite->queryAll($query, $vals); 1210d5703f5aSAndreas Boehler return $arr; 1211d5703f5aSAndreas Boehler } 1212d5703f5aSAndreas Boehler 1213d5703f5aSAndreas Boehler /** 1214cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1215cb71a62aSAndreas Boehler * 1216cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1217cb71a62aSAndreas Boehler * 1218cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1219cb71a62aSAndreas Boehler */ 1220f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1221f69bb449SAndreas Boehler { 122259b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 122359b68239SAndreas Boehler if($enabled === false) 122459b68239SAndreas Boehler return false; 12255f2c3e2dSAndreas Boehler 12265f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12275f2c3e2dSAndreas Boehler if(!$sqlite) 12285f2c3e2dSAndreas Boehler return false; 12297e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 123061fec75eSscottleechua $arr = $sqlite->queryAll($query, [$calid]); 1231f69bb449SAndreas Boehler return $arr; 1232f69bb449SAndreas Boehler } 1233f69bb449SAndreas Boehler 1234cb71a62aSAndreas Boehler /** 1235cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1236cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1237cb71a62aSAndreas Boehler * 1238cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1239cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1240cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1241cb71a62aSAndreas Boehler * 1242cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1243cb71a62aSAndreas Boehler */ 1244a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1245a1a3b679SAndreas Boehler { 124682a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 124782a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 124882a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1249a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1250bd883736SAndreas Boehler else 1251bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1252cb71a62aSAndreas Boehler 1253cb71a62aSAndreas Boehler // Parse dates 1254b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1255b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1256b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1257b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1258cb71a62aSAndreas Boehler 1259cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 126055a741c0SAndreas Boehler $uid = $params['uid']; 1261809cb0faSAndreas Boehler 1262809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1263809cb0faSAndreas Boehler { 1264809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1265809cb0faSAndreas Boehler if(is_null($wdc)) 1266809cb0faSAndreas Boehler return false; 1267809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1268809cb0faSAndreas Boehler } 1269809cb0faSAndreas Boehler else 1270809cb0faSAndreas Boehler { 127155a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1272809cb0faSAndreas Boehler } 1273cb71a62aSAndreas Boehler 1274cb71a62aSAndreas Boehler // Load SabreDAV 12759bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1276a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1277a1a3b679SAndreas Boehler return false; 127855a741c0SAndreas Boehler $uri = $event['uri']; 127955a741c0SAndreas Boehler $calid = $event['calendarid']; 1280cb71a62aSAndreas Boehler 1281cb71a62aSAndreas Boehler // Parse the existing event 1282a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1283b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1284cb71a62aSAndreas Boehler 1285cb71a62aSAndreas Boehler // Set the new event values 1286b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1287b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 12880eebc909SAndreas Boehler $description = $params['eventdescription']; 12892b7be5bdSAndreas Boehler $location = $params['eventlocation']; 1290cb71a62aSAndreas Boehler 1291cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 12920eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1293b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1294b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 12954ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 12962b7be5bdSAndreas Boehler $vevent->remove('LOCATION'); 1297cb71a62aSAndreas Boehler 12982b7be5bdSAndreas Boehler // Add new time stamps, description and location 1299b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1300b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 13010eebc909SAndreas Boehler if($description !== '') 13020eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 13032b7be5bdSAndreas Boehler if($location !== '') 13042b7be5bdSAndreas Boehler $vevent->add('LOCATION', $location); 1305cb71a62aSAndreas Boehler 13064ecb526cSAndreas Boehler // Add attachments 13074ecb526cSAndreas Boehler $attachments = $params['attachments']; 130882a48dfbSAndreas Boehler if(!is_null($attachments)) 13094ecb526cSAndreas Boehler foreach($attachments as $attachment) 13104ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 13114ecb526cSAndreas Boehler 1312cb71a62aSAndreas Boehler // Setup DTSTART 1313b269830cSAndreas Boehler $dtStart = new \DateTime(); 1314a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1315b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1316b269830cSAndreas Boehler if($params['allday'] != '1') 1317b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1318cb71a62aSAndreas Boehler 13194ecb526cSAndreas Boehler // Setup DTEND 1320b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1321a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1322b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1323b269830cSAndreas Boehler if($params['allday'] != '1') 1324b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1325cb71a62aSAndreas Boehler 1326b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1327b269830cSAndreas Boehler if($params['allday'] == '1') 1328b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1329b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1330b269830cSAndreas Boehler $vevent->remove('DTEND'); 1331b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1332b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1333cb71a62aSAndreas Boehler 1334cb71a62aSAndreas Boehler // Remove the time for allday events 1335b269830cSAndreas Boehler if($params['allday'] == '1') 1336b269830cSAndreas Boehler { 1337b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1338b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1339b269830cSAndreas Boehler } 1340a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1341809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1342809cb0faSAndreas Boehler { 1343809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1344809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1345809cb0faSAndreas Boehler } 1346809cb0faSAndreas Boehler else 1347809cb0faSAndreas Boehler { 13485f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 13495f2c3e2dSAndreas Boehler if(!$sqlite) 13505f2c3e2dSAndreas Boehler return false; 1351809cb0faSAndreas Boehler $now = new DateTime(); 1352cb71a62aSAndreas Boehler // Actually write to the database 135351f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 135451f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 13555f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 135651f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 135755a741c0SAndreas Boehler if($res !== false) 135855a741c0SAndreas Boehler { 135955a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1360a1a3b679SAndreas Boehler return true; 1361a1a3b679SAndreas Boehler } 1362809cb0faSAndreas Boehler } 136355a741c0SAndreas Boehler return false; 136455a741c0SAndreas Boehler } 1365a1a3b679SAndreas Boehler 1366cb71a62aSAndreas Boehler /** 1367d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1368d5703f5aSAndreas Boehler * 1369d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1370d5703f5aSAndreas Boehler * @param string $uri The object's URI 1371d5703f5aSAndreas Boehler * 1372d5703f5aSAndreas Boehler * @return true 1373d5703f5aSAndreas Boehler */ 1374d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1375d5703f5aSAndreas Boehler { 13765f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 13775f2c3e2dSAndreas Boehler if(!$sqlite) 13785f2c3e2dSAndreas Boehler return false; 1379d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 13805f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri); 1381d5703f5aSAndreas Boehler if($res !== false) 1382d5703f5aSAndreas Boehler { 1383d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1384d5703f5aSAndreas Boehler } 1385d5703f5aSAndreas Boehler return true; 1386d5703f5aSAndreas Boehler } 1387d5703f5aSAndreas Boehler 1388d5703f5aSAndreas Boehler /** 1389cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1390cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1391cb71a62aSAndreas Boehler * 1392cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1393cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1394cb71a62aSAndreas Boehler * 1395cb71a62aSAndreas Boehler * @return boolean True 1396cb71a62aSAndreas Boehler */ 1397a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1398a1a3b679SAndreas Boehler { 1399a1a3b679SAndreas Boehler $uid = $params['uid']; 1400809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1401809cb0faSAndreas Boehler { 1402809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1403809cb0faSAndreas Boehler if(is_null($wdc)) 1404809cb0faSAndreas Boehler return false; 1405809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1406809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1407809cb0faSAndreas Boehler return $result; 1408809cb0faSAndreas Boehler } 14095f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14105f2c3e2dSAndreas Boehler if(!$sqlite) 14115f2c3e2dSAndreas Boehler return false; 141255a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 14132c14b82bSAndreas Boehler $calid = $event['calendarid']; 141455a741c0SAndreas Boehler $uri = $event['uri']; 141551f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 14165f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uid); 141755a741c0SAndreas Boehler if($res !== false) 141855a741c0SAndreas Boehler { 141955a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 142055a741c0SAndreas Boehler } 1421a1a3b679SAndreas Boehler return true; 1422a1a3b679SAndreas Boehler } 1423a1a3b679SAndreas Boehler 1424cb71a62aSAndreas Boehler /** 1425cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1426cb71a62aSAndreas Boehler * 1427cb71a62aSAndreas Boehler * @param string $calid The calendar id 1428cb71a62aSAndreas Boehler * 1429cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1430cb71a62aSAndreas Boehler */ 143155a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 143255a741c0SAndreas Boehler { 1433b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 143455a741c0SAndreas Boehler if(isset($row['synctoken'])) 143555a741c0SAndreas Boehler return $row['synctoken']; 143655a741c0SAndreas Boehler return false; 143755a741c0SAndreas Boehler } 143855a741c0SAndreas Boehler 1439cb71a62aSAndreas Boehler /** 1440cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1441cb71a62aSAndreas Boehler * an operation code as stored in the database 1442cb71a62aSAndreas Boehler * 1443cb71a62aSAndreas Boehler * @param string $operationName The operation name 1444cb71a62aSAndreas Boehler * 1445cb71a62aSAndreas Boehler * @return mixed The operation code or false 1446cb71a62aSAndreas Boehler */ 144755a741c0SAndreas Boehler public function operationNameToOperation($operationName) 144855a741c0SAndreas Boehler { 144955a741c0SAndreas Boehler switch($operationName) 145055a741c0SAndreas Boehler { 145155a741c0SAndreas Boehler case 'added': 145255a741c0SAndreas Boehler return 1; 145355a741c0SAndreas Boehler break; 145455a741c0SAndreas Boehler case 'modified': 145555a741c0SAndreas Boehler return 2; 145655a741c0SAndreas Boehler break; 145755a741c0SAndreas Boehler case 'deleted': 145855a741c0SAndreas Boehler return 3; 145955a741c0SAndreas Boehler break; 146055a741c0SAndreas Boehler } 146155a741c0SAndreas Boehler return false; 146255a741c0SAndreas Boehler } 146355a741c0SAndreas Boehler 1464cb71a62aSAndreas Boehler /** 1465cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1466cb71a62aSAndreas Boehler * operation that was performed. 1467cb71a62aSAndreas Boehler * 1468cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1469cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1470cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1471cb71a62aSAndreas Boehler * 1472cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1473cb71a62aSAndreas Boehler */ 147455a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 147555a741c0SAndreas Boehler { 147655a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 147755a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 147855a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 147955a741c0SAndreas Boehler return false; 148055a741c0SAndreas Boehler $values = array($uri, 148155a741c0SAndreas Boehler $currentToken, 148255a741c0SAndreas Boehler $calid, 148355a741c0SAndreas Boehler $operationCode 148455a741c0SAndreas Boehler ); 14855f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14865f2c3e2dSAndreas Boehler if(!$sqlite) 14875f2c3e2dSAndreas Boehler return false; 148851f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 14895f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 149055a741c0SAndreas Boehler if($res === false) 149155a741c0SAndreas Boehler return false; 149255a741c0SAndreas Boehler $currentToken++; 149351f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 14945f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $currentToken, $calid); 149555a741c0SAndreas Boehler return ($res !== false); 149655a741c0SAndreas Boehler } 149755a741c0SAndreas Boehler 1498cb71a62aSAndreas Boehler /** 1499cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1500cb71a62aSAndreas Boehler * 1501cb71a62aSAndreas Boehler * @param string $id The page's ID 1502cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1503cb71a62aSAndreas Boehler * 1504cb71a62aSAndreas Boehler * @return mixed The sync url or false 1505cb71a62aSAndreas Boehler */ 1506b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1507b269830cSAndreas Boehler { 1508415f5c8fSscottleechua if(is_null($user)) 150934a47953SAndreas Boehler { 151034a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 151134a47953SAndreas Boehler { 1512415f5c8fSscottleechua $user = $_SERVER['REMOTE_USER']; 151334a47953SAndreas Boehler } 151434a47953SAndreas Boehler else 151534a47953SAndreas Boehler { 151634a47953SAndreas Boehler return false; 151734a47953SAndreas Boehler } 151834a47953SAndreas Boehler } 1519b269830cSAndreas Boehler 1520b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1521b269830cSAndreas Boehler if($calid === false) 1522b269830cSAndreas Boehler return false; 1523b269830cSAndreas Boehler 1524b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1525b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1526b269830cSAndreas Boehler return false; 1527b269830cSAndreas Boehler 1528b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1529b269830cSAndreas Boehler return $syncurl; 1530b269830cSAndreas Boehler } 1531b269830cSAndreas Boehler 1532cb71a62aSAndreas Boehler /** 1533cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1534cb71a62aSAndreas Boehler * 1535cb71a62aSAndreas Boehler * @param string $id the page ID 1536cb71a62aSAndreas Boehler * 1537cb71a62aSAndreas Boehler * @return mixed The private URL or false 1538cb71a62aSAndreas Boehler */ 1539f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1540f69bb449SAndreas Boehler { 1541a8a4aa52Sscottleechua // Check if this is an aggregated calendar (has multiple calendar pages) 1542a8a4aa52Sscottleechua $calendarPages = $this->getCalendarPagesByMeta($id); 1543a8a4aa52Sscottleechua 1544a8a4aa52Sscottleechua if($calendarPages !== false && count($calendarPages) > 1) 1545a8a4aa52Sscottleechua { 1546a8a4aa52Sscottleechua // This is an aggregated calendar - create a special private URL 1547a8a4aa52Sscottleechua return $this->getPrivateURLForAggregatedCalendar($id, $calendarPages); 1548a8a4aa52Sscottleechua } 1549a8a4aa52Sscottleechua 1550a8a4aa52Sscottleechua // Single calendar - use the original logic 1551f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1552f69bb449SAndreas Boehler if($calid === false) 1553f69bb449SAndreas Boehler return false; 1554f69bb449SAndreas Boehler 1555f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1556f69bb449SAndreas Boehler } 1557f69bb449SAndreas Boehler 1558cb71a62aSAndreas Boehler /** 1559cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1560cb71a62aSAndreas Boehler * 1561cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1562cb71a62aSAndreas Boehler * 1563cb71a62aSAndreas Boehler * @return mixed The private URL or false 1564cb71a62aSAndreas Boehler */ 1565f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1566f69bb449SAndreas Boehler { 1567185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1568185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 15695f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 15705f2c3e2dSAndreas Boehler if(!$sqlite) 15715f2c3e2dSAndreas Boehler return false; 157251f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 157361fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 1574f69bb449SAndreas Boehler if(!isset($row['url'])) 1575f69bb449SAndreas Boehler { 157602eea9b8SAndreas Boehler $url = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 157751f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 157861fec75eSscottleechua $sqlite->query($query, [$url, $calid]); 1579f69bb449SAndreas Boehler } 1580f69bb449SAndreas Boehler else 1581f69bb449SAndreas Boehler { 1582f69bb449SAndreas Boehler $url = $row['url']; 1583f69bb449SAndreas Boehler } 1584185e2535SAndreas Boehler 1585185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1586185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1587185e2535SAndreas Boehler return $url; 1588f69bb449SAndreas Boehler } 1589f69bb449SAndreas Boehler 1590cb71a62aSAndreas Boehler /** 1591a8a4aa52Sscottleechua * Return the private calendar's URL for an aggregated calendar 1592a8a4aa52Sscottleechua * 1593a8a4aa52Sscottleechua * @param string $id the page ID 1594a8a4aa52Sscottleechua * @param array $calendarPages the calendar pages in the aggregation 1595a8a4aa52Sscottleechua * 1596a8a4aa52Sscottleechua * @return mixed The private URL or false 1597a8a4aa52Sscottleechua */ 1598a8a4aa52Sscottleechua public function getPrivateURLForAggregatedCalendar($id, $calendarPages) 1599a8a4aa52Sscottleechua { 1600a8a4aa52Sscottleechua $aggregateId = 'aggregated-' . md5($id . serialize($calendarPages)); 1601a8a4aa52Sscottleechua 1602a8a4aa52Sscottleechua if(isset($this->cachedValues['privateurl'][$aggregateId])) 1603a8a4aa52Sscottleechua return $this->cachedValues['privateurl'][$aggregateId]; 1604a8a4aa52Sscottleechua 1605a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1606a8a4aa52Sscottleechua if(!$sqlite) 1607a8a4aa52Sscottleechua return false; 1608a8a4aa52Sscottleechua 1609a8a4aa52Sscottleechua $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 161061fec75eSscottleechua $row = $sqlite->queryRecord($query, [$aggregateId]); 1611a8a4aa52Sscottleechua 1612a8a4aa52Sscottleechua if(!isset($row['url'])) 1613a8a4aa52Sscottleechua { 1614a8a4aa52Sscottleechua $url = 'dokuwiki-aggregated-' . bin2hex(random_bytes(16)) . '.ics'; 1615a8a4aa52Sscottleechua $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 161661fec75eSscottleechua $sqlite->query($query, [$url, $aggregateId]); 1617a8a4aa52Sscottleechua 1618a8a4aa52Sscottleechua $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES(?, ?)"; 161961fec75eSscottleechua $sqlite->query($query, [$id, $aggregateId]); 1620a8a4aa52Sscottleechua } 1621a8a4aa52Sscottleechua else 1622a8a4aa52Sscottleechua { 1623a8a4aa52Sscottleechua $url = $row['url']; 1624a8a4aa52Sscottleechua } 1625a8a4aa52Sscottleechua 1626a8a4aa52Sscottleechua $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1627a8a4aa52Sscottleechua $this->cachedValues['privateurl'][$aggregateId] = $url; 1628a8a4aa52Sscottleechua return $url; 1629a8a4aa52Sscottleechua } 1630a8a4aa52Sscottleechua 1631a8a4aa52Sscottleechua /** 1632cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1633cb71a62aSAndreas Boehler * 1634cb71a62aSAndreas Boehler * @param string $url The private URL 1635cb71a62aSAndreas Boehler * 1636cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1637cb71a62aSAndreas Boehler */ 1638f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1639f69bb449SAndreas Boehler { 16405f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 16415f2c3e2dSAndreas Boehler if(!$sqlite) 16425f2c3e2dSAndreas Boehler return false; 164351f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 164461fec75eSscottleechua $row = $sqlite->queryRecord($query, [$url]); 1645f69bb449SAndreas Boehler if(!isset($row['calid'])) 1646f69bb449SAndreas Boehler return false; 1647f69bb449SAndreas Boehler return $row['calid']; 1648f69bb449SAndreas Boehler } 1649f69bb449SAndreas Boehler 1650cb71a62aSAndreas Boehler /** 1651cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1652cb71a62aSAndreas Boehler * 16537e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1654cb71a62aSAndreas Boehler * 1655cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1656cb71a62aSAndreas Boehler */ 1657f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1658f69bb449SAndreas Boehler { 1659f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1660f69bb449SAndreas Boehler if($calSettings === false) 1661f69bb449SAndreas Boehler return false; 1662f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1663f69bb449SAndreas Boehler if($events === false) 1664f69bb449SAndreas Boehler return false; 1665f69bb449SAndreas Boehler 16667e0b8590SAndreas Boehler // Load SabreDAV 16677e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 16687e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 16697e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1670f69bb449SAndreas Boehler foreach($events as $event) 1671f69bb449SAndreas Boehler { 16727e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 16737e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 16747e0b8590SAndreas Boehler $out .= $evt->serialize(); 1675f69bb449SAndreas Boehler } 16767e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1677f69bb449SAndreas Boehler return $out; 1678f69bb449SAndreas Boehler } 1679f69bb449SAndreas Boehler 16807c7c6b0bSAndreas Boehler /** 1681a8a4aa52Sscottleechua * Return an aggregated calendar as ICS feed, combining multiple calendars. 1682a8a4aa52Sscottleechua * 1683a8a4aa52Sscottleechua * @param string $icsFile The ICS file name for the aggregated calendar 1684a8a4aa52Sscottleechua * 1685a8a4aa52Sscottleechua * @return mixed The combined calendar events as string or false 1686a8a4aa52Sscottleechua */ 1687a8a4aa52Sscottleechua public function getAggregatedCalendarAsICSFeed($icsFile) 1688a8a4aa52Sscottleechua { 1689a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1690a8a4aa52Sscottleechua if(!$sqlite) 1691a8a4aa52Sscottleechua return false; 1692a8a4aa52Sscottleechua 1693a8a4aa52Sscottleechua // Find the aggregated calendar ID from the URL 1694a8a4aa52Sscottleechua $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 16956a6f477eSscottleechua $row = $sqlite->queryRecord($query, [$icsFile]); 1696a8a4aa52Sscottleechua 1697a8a4aa52Sscottleechua if(!isset($row['calid'])) 1698a8a4aa52Sscottleechua return false; 1699a8a4aa52Sscottleechua 1700a8a4aa52Sscottleechua $aggregateId = $row['calid']; 1701a8a4aa52Sscottleechua 1702a8a4aa52Sscottleechua // Get the page ID for this aggregated calendar 1703a8a4aa52Sscottleechua $query = "SELECT page FROM pagetocalendarmapping WHERE calid = ?"; 17046a6f477eSscottleechua $row = $sqlite->queryRecord($query, [$aggregateId]); 1705a8a4aa52Sscottleechua 1706a8a4aa52Sscottleechua if(!isset($row['page'])) 1707a8a4aa52Sscottleechua return false; 1708a8a4aa52Sscottleechua 1709a8a4aa52Sscottleechua $pageId = $row['page']; 1710a8a4aa52Sscottleechua 1711a8a4aa52Sscottleechua // Get the calendar pages for this aggregated calendar 1712*ebcdb60aSscottleechua $calendarPages = $this->getCalendarPagesByMeta($pageId, true); // Skip permission check for private URLs 1713a8a4aa52Sscottleechua if($calendarPages === false || count($calendarPages) <= 1) 1714*ebcdb60aSscottleechua { 1715*ebcdb60aSscottleechua if($conf['allowdebug']) 1716*ebcdb60aSscottleechua \dokuwiki\Logger::error('DAVCAL', 'No calendar pages found or count <= 1 for page: '.$pageId.' (count: '.(is_array($calendarPages) ? count($calendarPages) : 'false').')', __FILE__, __LINE__); 1717a8a4aa52Sscottleechua return false; 1718*ebcdb60aSscottleechua } 1719a8a4aa52Sscottleechua 1720a8a4aa52Sscottleechua // Load SabreDAV 1721a8a4aa52Sscottleechua require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1722a8a4aa52Sscottleechua 1723a8a4aa52Sscottleechua // Start building the combined ICS 1724a8a4aa52Sscottleechua $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 1725a8a4aa52Sscottleechua $out .= "Aggregated Calendar\r\n"; 1726a8a4aa52Sscottleechua 1727a8a4aa52Sscottleechua // Combine events from all calendars 1728a8a4aa52Sscottleechua foreach($calendarPages as $calPage => $color) 1729a8a4aa52Sscottleechua { 1730a8a4aa52Sscottleechua $calid = $this->getCalendarIdForPage($calPage); 1731a8a4aa52Sscottleechua if($calid === false) 1732a8a4aa52Sscottleechua continue; 1733a8a4aa52Sscottleechua 1734a8a4aa52Sscottleechua $events = $this->getAllCalendarEvents($calid); 1735a8a4aa52Sscottleechua if($events === false) 1736a8a4aa52Sscottleechua continue; 1737a8a4aa52Sscottleechua 1738a8a4aa52Sscottleechua foreach($events as $event) 1739a8a4aa52Sscottleechua { 1740a8a4aa52Sscottleechua $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1741a8a4aa52Sscottleechua $evt = $vcal->VEVENT; 1742a8a4aa52Sscottleechua $out .= $evt->serialize(); 1743a8a4aa52Sscottleechua } 1744a8a4aa52Sscottleechua } 1745a8a4aa52Sscottleechua 1746a8a4aa52Sscottleechua $out .= "END:VCALENDAR\r\n"; 1747a8a4aa52Sscottleechua return $out; 1748a8a4aa52Sscottleechua } 1749a8a4aa52Sscottleechua 1750a8a4aa52Sscottleechua /** 17517c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 17527c7c6b0bSAndreas Boehler * 17537c7c6b0bSAndreas Boehler * @param string $key The key to query 175421d04f73SAndreas Boehler * @return mixed The option set, null if not found 17557c7c6b0bSAndreas Boehler */ 17567c7c6b0bSAndreas Boehler public function getConfig($key) 17577c7c6b0bSAndreas Boehler { 17587c7c6b0bSAndreas Boehler return $this->getConf($key); 17597c7c6b0bSAndreas Boehler } 17607c7c6b0bSAndreas Boehler 1761d5703f5aSAndreas Boehler /** 1762d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1763d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1764d5703f5aSAndreas Boehler * 1765d5703f5aSAndreas Boehler * Returns an array with the following keys: 1766d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1767d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1768d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1769d5703f5aSAndreas Boehler * * firstOccurence 1770d5703f5aSAndreas Boehler * * lastOccurence 1771d5703f5aSAndreas Boehler * * uid - value of the UID property 1772d5703f5aSAndreas Boehler * 1773d5703f5aSAndreas Boehler * @param string $calendarData 1774d5703f5aSAndreas Boehler * @return array 1775d5703f5aSAndreas Boehler */ 1776d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1777d5703f5aSAndreas Boehler { 1778d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1779d5703f5aSAndreas Boehler 1780d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1781d5703f5aSAndreas Boehler $componentType = null; 1782d5703f5aSAndreas Boehler $component = null; 1783d5703f5aSAndreas Boehler $firstOccurence = null; 1784d5703f5aSAndreas Boehler $lastOccurence = null; 1785d5703f5aSAndreas Boehler $uid = null; 1786d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1787d5703f5aSAndreas Boehler { 1788d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1789d5703f5aSAndreas Boehler { 1790d5703f5aSAndreas Boehler $componentType = $component->name; 1791d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1792d5703f5aSAndreas Boehler break; 1793d5703f5aSAndreas Boehler } 1794d5703f5aSAndreas Boehler } 1795d5703f5aSAndreas Boehler if (!$componentType) 1796d5703f5aSAndreas Boehler { 1797d5703f5aSAndreas Boehler return false; 1798d5703f5aSAndreas Boehler } 1799d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1800d5703f5aSAndreas Boehler { 1801d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1802d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1803d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1804d5703f5aSAndreas Boehler { 1805d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1806d5703f5aSAndreas Boehler { 1807d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1808d5703f5aSAndreas Boehler } 1809d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1810d5703f5aSAndreas Boehler { 1811d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1812d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1813d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1814d5703f5aSAndreas Boehler } 1815d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1816d5703f5aSAndreas Boehler { 1817d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1818d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1819d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1820d5703f5aSAndreas Boehler } 1821d5703f5aSAndreas Boehler else 1822d5703f5aSAndreas Boehler { 1823d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1824d5703f5aSAndreas Boehler } 1825d5703f5aSAndreas Boehler } 1826d5703f5aSAndreas Boehler else 1827d5703f5aSAndreas Boehler { 1828d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1829d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1830d5703f5aSAndreas Boehler if ($it->isInfinite()) 1831d5703f5aSAndreas Boehler { 1832d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1833d5703f5aSAndreas Boehler } 1834d5703f5aSAndreas Boehler else 1835d5703f5aSAndreas Boehler { 1836d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1837d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1838d5703f5aSAndreas Boehler { 1839d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1840d5703f5aSAndreas Boehler $it->next(); 1841d5703f5aSAndreas Boehler } 1842d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1843d5703f5aSAndreas Boehler } 1844d5703f5aSAndreas Boehler } 1845d5703f5aSAndreas Boehler } 1846d5703f5aSAndreas Boehler 1847d5703f5aSAndreas Boehler return array( 1848d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1849d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1850d5703f5aSAndreas Boehler 'componentType' => $componentType, 1851d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1852d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1853d5703f5aSAndreas Boehler 'uid' => $uid, 1854d5703f5aSAndreas Boehler ); 1855d5703f5aSAndreas Boehler 1856d5703f5aSAndreas Boehler } 1857d5703f5aSAndreas Boehler 1858d5703f5aSAndreas Boehler /** 1859d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1860d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1861d5703f5aSAndreas Boehler * 1862d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1863d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1864d5703f5aSAndreas Boehler * 1865d5703f5aSAndreas Boehler * @return mixed The result 1866d5703f5aSAndreas Boehler */ 1867d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1868d5703f5aSAndreas Boehler { 1869c42afaebSscottleechua \dokuwiki\Logger::debug('DAVCAL', 'Calendar query executed', __FILE__, __LINE__); 1870d5703f5aSAndreas Boehler $componentType = null; 1871d5703f5aSAndreas Boehler $requirePostFilter = true; 1872d5703f5aSAndreas Boehler $timeRange = null; 18735f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 18745f2c3e2dSAndreas Boehler if(!$sqlite) 18755f2c3e2dSAndreas Boehler return false; 1876d5703f5aSAndreas Boehler 1877d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1878d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1879d5703f5aSAndreas Boehler { 1880d5703f5aSAndreas Boehler $requirePostFilter = false; 1881d5703f5aSAndreas Boehler } 1882d5703f5aSAndreas Boehler 1883d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1884d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1885d5703f5aSAndreas Boehler { 1886d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1887d5703f5aSAndreas Boehler 1888d5703f5aSAndreas Boehler // Checking if we need post-filters 1889d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1890d5703f5aSAndreas Boehler { 1891d5703f5aSAndreas Boehler $requirePostFilter = false; 1892d5703f5aSAndreas Boehler } 1893d5703f5aSAndreas Boehler // There was a time-range filter 1894d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1895d5703f5aSAndreas Boehler { 1896d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1897d5703f5aSAndreas Boehler 1898d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1899d5703f5aSAndreas Boehler // 100% accurate mysql query. 1900d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1901d5703f5aSAndreas Boehler { 1902d5703f5aSAndreas Boehler $requirePostFilter = false; 1903d5703f5aSAndreas Boehler } 1904d5703f5aSAndreas Boehler } 1905d5703f5aSAndreas Boehler 1906d5703f5aSAndreas Boehler } 1907d5703f5aSAndreas Boehler 1908d5703f5aSAndreas Boehler if ($requirePostFilter) 1909d5703f5aSAndreas Boehler { 1910d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1911d5703f5aSAndreas Boehler } 1912d5703f5aSAndreas Boehler else 1913d5703f5aSAndreas Boehler { 1914d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1915d5703f5aSAndreas Boehler } 1916d5703f5aSAndreas Boehler 1917d5703f5aSAndreas Boehler $values = array( 1918d5703f5aSAndreas Boehler $calendarId 1919d5703f5aSAndreas Boehler ); 1920d5703f5aSAndreas Boehler 1921d5703f5aSAndreas Boehler if ($componentType) 1922d5703f5aSAndreas Boehler { 1923d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1924d5703f5aSAndreas Boehler $values[] = $componentType; 1925d5703f5aSAndreas Boehler } 1926d5703f5aSAndreas Boehler 1927d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1928d5703f5aSAndreas Boehler { 1929d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1930d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1931d5703f5aSAndreas Boehler } 1932d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1933d5703f5aSAndreas Boehler { 1934d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1935d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1936d5703f5aSAndreas Boehler } 1937d5703f5aSAndreas Boehler 19386a6f477eSscottleechua $arr = $sqlite->queryAll($query, $values); 1939d5703f5aSAndreas Boehler 1940d5703f5aSAndreas Boehler $result = array(); 1941d5703f5aSAndreas Boehler foreach($arr as $row) 1942d5703f5aSAndreas Boehler { 1943d5703f5aSAndreas Boehler if ($requirePostFilter) 1944d5703f5aSAndreas Boehler { 1945d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1946d5703f5aSAndreas Boehler { 1947d5703f5aSAndreas Boehler continue; 1948d5703f5aSAndreas Boehler } 1949d5703f5aSAndreas Boehler } 1950d5703f5aSAndreas Boehler $result[] = $row['uri']; 1951d5703f5aSAndreas Boehler 1952d5703f5aSAndreas Boehler } 1953d5703f5aSAndreas Boehler 1954d5703f5aSAndreas Boehler return $result; 1955d5703f5aSAndreas Boehler } 1956d5703f5aSAndreas Boehler 1957d5703f5aSAndreas Boehler /** 1958d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1959d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1960d5703f5aSAndreas Boehler * 1961d5703f5aSAndreas Boehler * @param array $object 1962d5703f5aSAndreas Boehler * @param array $filters 1963d5703f5aSAndreas Boehler * @return bool 1964d5703f5aSAndreas Boehler */ 1965d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 1966d5703f5aSAndreas Boehler { 1967d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1968d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1969d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 1970d5703f5aSAndreas Boehler // well. 1971d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 1972d5703f5aSAndreas Boehler { 1973d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1974d5703f5aSAndreas Boehler } 1975d5703f5aSAndreas Boehler 1976d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1977d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1978d5703f5aSAndreas Boehler 197939787131SAndreas Boehler $res = $validator->validate($vObject, $filters); 198039787131SAndreas Boehler return $res; 1981d5703f5aSAndreas Boehler 1982d5703f5aSAndreas Boehler } 1983d5703f5aSAndreas Boehler 1984d5703f5aSAndreas Boehler /** 1985d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1986d5703f5aSAndreas Boehler * 1987d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1988d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 1989d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 1990d5703f5aSAndreas Boehler * @param int $limit The limit of changes 1991d5703f5aSAndreas Boehler * 1992d5703f5aSAndreas Boehler * @return array The result 1993d5703f5aSAndreas Boehler */ 1994d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1995d5703f5aSAndreas Boehler { 1996d5703f5aSAndreas Boehler // Current synctoken 1997d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1998d5703f5aSAndreas Boehler 1999d5703f5aSAndreas Boehler if ($currentToken === false) return null; 2000d5703f5aSAndreas Boehler 2001d5703f5aSAndreas Boehler $result = array( 2002d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 2003d5703f5aSAndreas Boehler 'added' => array(), 2004d5703f5aSAndreas Boehler 'modified' => array(), 2005d5703f5aSAndreas Boehler 'deleted' => array(), 2006d5703f5aSAndreas Boehler ); 20075f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 20085f2c3e2dSAndreas Boehler if(!$sqlite) 20095f2c3e2dSAndreas Boehler return false; 2010d5703f5aSAndreas Boehler 2011d5703f5aSAndreas Boehler if ($syncToken) 2012d5703f5aSAndreas Boehler { 2013d5703f5aSAndreas Boehler 2014d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 2015d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 2016d5703f5aSAndreas Boehler 2017d5703f5aSAndreas Boehler // Fetching all changes 20186a6f477eSscottleechua $arr = $sqlite->queryAll($query, array($syncToken, $currentToken, $calid)); 2019d5703f5aSAndreas Boehler $changes = array(); 2020d5703f5aSAndreas Boehler 2021d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 2022d5703f5aSAndreas Boehler // last change on a node is relevant. 2023d5703f5aSAndreas Boehler foreach($arr as $row) 2024d5703f5aSAndreas Boehler { 2025d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 2026d5703f5aSAndreas Boehler } 2027d5703f5aSAndreas Boehler 2028d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 2029d5703f5aSAndreas Boehler { 2030d5703f5aSAndreas Boehler switch ($operation) 2031d5703f5aSAndreas Boehler { 2032d5703f5aSAndreas Boehler case 1 : 2033d5703f5aSAndreas Boehler $result['added'][] = $uri; 2034d5703f5aSAndreas Boehler break; 2035d5703f5aSAndreas Boehler case 2 : 2036d5703f5aSAndreas Boehler $result['modified'][] = $uri; 2037d5703f5aSAndreas Boehler break; 2038d5703f5aSAndreas Boehler case 3 : 2039d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 2040d5703f5aSAndreas Boehler break; 2041d5703f5aSAndreas Boehler } 2042d5703f5aSAndreas Boehler 2043d5703f5aSAndreas Boehler } 2044d5703f5aSAndreas Boehler } 2045d5703f5aSAndreas Boehler else 2046d5703f5aSAndreas Boehler { 2047d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 2048d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 20496a6f477eSscottleechua $arr = $sqlite->queryAll($query, array($calid)); 2050d5703f5aSAndreas Boehler 2051d5703f5aSAndreas Boehler $result['added'] = $arr; 2052d5703f5aSAndreas Boehler } 2053d5703f5aSAndreas Boehler return $result; 2054d5703f5aSAndreas Boehler } 2055d5703f5aSAndreas Boehler 2056a1a3b679SAndreas Boehler} 2057