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() { 20c42afaebSscottleechua \dokuwiki\Logger::debug('DAVCAL', 'Helper initialized', __FILE__, __LINE__); 215f2c3e2dSAndreas Boehler } 225f2c3e2dSAndreas Boehler 235f2c3e2dSAndreas Boehler /** Establish and initialize the database if not already done 245f2c3e2dSAndreas Boehler * @return sqlite interface or false 255f2c3e2dSAndreas Boehler */ 265f2c3e2dSAndreas Boehler private function getDB() 275f2c3e2dSAndreas Boehler { 285f2c3e2dSAndreas Boehler if($this->sqlite === null) 295f2c3e2dSAndreas Boehler { 307e2d2436Sscottleechua $this->sqlite = new \dokuwiki\plugin\sqlite\SQLiteDB('davcal', DOKU_PLUGIN.'davcal/db/'); 31a1a3b679SAndreas Boehler if(!$this->sqlite) 32a1a3b679SAndreas Boehler { 33c42afaebSscottleechua \dokuwiki\Logger::error('DAVCAL', 'This plugin requires the sqlite plugin. Please install it.', __FILE__, __LINE__); 345f2c3e2dSAndreas Boehler msg('This plugin requires the sqlite plugin. Please install it.', -1); 355f2c3e2dSAndreas Boehler return false; 36a1a3b679SAndreas Boehler } 37a1a3b679SAndreas Boehler } 385f2c3e2dSAndreas Boehler return $this->sqlite; 395f2c3e2dSAndreas Boehler } 40a1a3b679SAndreas Boehler 41cb71a62aSAndreas Boehler /** 42185e2535SAndreas Boehler * Retrieve meta data for a given page 43185e2535SAndreas Boehler * 44185e2535SAndreas Boehler * @param string $id optional The page ID 45185e2535SAndreas Boehler * @return array The metadata 46185e2535SAndreas Boehler */ 47185e2535SAndreas Boehler private function getMeta($id = null) { 48185e2535SAndreas Boehler global $ID; 49185e2535SAndreas Boehler global $INFO; 50185e2535SAndreas Boehler 51185e2535SAndreas Boehler if ($id === null) $id = $ID; 52185e2535SAndreas Boehler 53185e2535SAndreas Boehler if($ID === $id && $INFO['meta']) { 54185e2535SAndreas Boehler $meta = $INFO['meta']; 55185e2535SAndreas Boehler } else { 56185e2535SAndreas Boehler $meta = p_get_metadata($id); 57185e2535SAndreas Boehler } 58185e2535SAndreas Boehler 59185e2535SAndreas Boehler return $meta; 60185e2535SAndreas Boehler } 61185e2535SAndreas Boehler 62185e2535SAndreas Boehler /** 63185e2535SAndreas Boehler * Retrieve the meta data for a given page 64185e2535SAndreas Boehler * 65185e2535SAndreas Boehler * @param string $id optional The page ID 66185e2535SAndreas Boehler * @return array with meta data 67185e2535SAndreas Boehler */ 68185e2535SAndreas Boehler public function getCalendarMetaForPage($id = null) 69185e2535SAndreas Boehler { 70185e2535SAndreas Boehler if(is_null($id)) 71185e2535SAndreas Boehler { 72185e2535SAndreas Boehler global $ID; 73185e2535SAndreas Boehler $id = $ID; 74185e2535SAndreas Boehler } 75185e2535SAndreas Boehler 76185e2535SAndreas Boehler $meta = $this->getMeta($id); 77185e2535SAndreas Boehler if(isset($meta['plugin_davcal'])) 78185e2535SAndreas Boehler return $meta['plugin_davcal']; 79185e2535SAndreas Boehler else 80185e2535SAndreas Boehler return array(); 81185e2535SAndreas Boehler } 82185e2535SAndreas Boehler 83185e2535SAndreas Boehler /** 84d71c9934SAndreas Boehler * Check the permission of a user for a given calendar ID 85d71c9934SAndreas Boehler * 86d71c9934SAndreas Boehler * @param string $id The calendar ID to check 87d71c9934SAndreas Boehler * @return int AUTH_* constants 88d71c9934SAndreas Boehler */ 89d71c9934SAndreas Boehler public function checkCalendarPermission($id) 90d71c9934SAndreas Boehler { 91d4992453SAndreas Boehler if(strpos($id, 'webdav://') === 0) 92d71c9934SAndreas Boehler { 93d71c9934SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 94d71c9934SAndreas Boehler if(is_null($wdc)) 95d71c9934SAndreas Boehler return AUTH_NONE; 96d4992453SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 97d71c9934SAndreas Boehler $settings = $wdc->getConnection($connectionId); 98d71c9934SAndreas Boehler if($settings === false) 99d71c9934SAndreas Boehler return AUTH_NONE; 10058d0e54eSAndreas Boehler // Check if webdavclient has permissions attached to a page 10158d0e54eSAndreas Boehler if(!empty($settings['permission'])) 10258d0e54eSAndreas Boehler { 10358d0e54eSAndreas Boehler $perm = auth_quickaclcheck($settings['permission']); 10458d0e54eSAndreas Boehler // In case the page has more than read permission, but the 10558d0e54eSAndreas Boehler // calendar is read-only, we need to modify the permission here. 10658d0e54eSAndreas Boehler if($perm > AUTH_READ && $settings['write'] == 0) 10758d0e54eSAndreas Boehler $perm = AUTH_READ; 10858d0e54eSAndreas Boehler return $perm; 10958d0e54eSAndreas Boehler } 110d71c9934SAndreas Boehler if($settings['write'] === '1') 111d71c9934SAndreas Boehler return AUTH_CREATE; 112d71c9934SAndreas Boehler return AUTH_READ; 113d71c9934SAndreas Boehler } 114d71c9934SAndreas Boehler else 115d71c9934SAndreas Boehler { 116d71c9934SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 117d71c9934SAndreas Boehler // We return AUTH_READ if the calendar does not exist. This makes 118d71c9934SAndreas Boehler // davcal happy when there are just included calendars 119d71c9934SAndreas Boehler if($calid === false) 120d71c9934SAndreas Boehler return AUTH_READ; 121d71c9934SAndreas Boehler return auth_quickaclcheck($id); 122d71c9934SAndreas Boehler } 123d71c9934SAndreas Boehler } 124d71c9934SAndreas Boehler 125d71c9934SAndreas Boehler /** 12680e1ddf7SAndreas Boehler * Filter calendar pages and return only those where the current 12780e1ddf7SAndreas Boehler * user has at least read permission. 12880e1ddf7SAndreas Boehler * 12980e1ddf7SAndreas Boehler * @param array $calendarPages Array with calendar pages to check 13080e1ddf7SAndreas Boehler * @return array with filtered calendar pages 13180e1ddf7SAndreas Boehler */ 13280e1ddf7SAndreas Boehler public function filterCalendarPagesByUserPermission($calendarPages) 13380e1ddf7SAndreas Boehler { 13480e1ddf7SAndreas Boehler $retList = array(); 13580e1ddf7SAndreas Boehler foreach($calendarPages as $page => $data) 13680e1ddf7SAndreas Boehler { 13758d0e54eSAndreas Boehler if($this->checkCalendarPermission($page) >= AUTH_READ) 13880e1ddf7SAndreas Boehler { 13980e1ddf7SAndreas Boehler $retList[$page] = $data; 14080e1ddf7SAndreas Boehler } 14180e1ddf7SAndreas Boehler } 14280e1ddf7SAndreas Boehler return $retList; 14380e1ddf7SAndreas Boehler } 14480e1ddf7SAndreas Boehler 14580e1ddf7SAndreas Boehler /** 146185e2535SAndreas Boehler * Get all calendar pages used by a given page 147185e2535SAndreas Boehler * based on the stored metadata 148185e2535SAndreas Boehler * 149185e2535SAndreas Boehler * @param string $id optional The page id 150185e2535SAndreas Boehler * @return mixed The pages as array or false 151185e2535SAndreas Boehler */ 152185e2535SAndreas Boehler public function getCalendarPagesByMeta($id = null) 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 { 164ed764890SAndreas Boehler // Filter the list of pages by permission 16580e1ddf7SAndreas Boehler $pages = $this->filterCalendarPagesByUserPermission($meta['id']); 16680e1ddf7SAndreas Boehler if(empty($pages)) 167ed764890SAndreas Boehler return false; 16880e1ddf7SAndreas Boehler return $pages; 169ed764890SAndreas Boehler } 170185e2535SAndreas Boehler return false; 171185e2535SAndreas Boehler } 172185e2535SAndreas Boehler 173185e2535SAndreas Boehler /** 174185e2535SAndreas Boehler * Get a list of calendar names/pages/ids/colors 175185e2535SAndreas Boehler * for an array of page ids 176185e2535SAndreas Boehler * 177185e2535SAndreas Boehler * @param array $calendarPages The calendar pages to retrieve 178185e2535SAndreas Boehler * @return array The list 179185e2535SAndreas Boehler */ 180185e2535SAndreas Boehler public function getCalendarMapForIDs($calendarPages) 181185e2535SAndreas Boehler { 182185e2535SAndreas Boehler $data = array(); 1834a2bf5eeSAndreas Boehler foreach($calendarPages as $page => $color) 184185e2535SAndreas Boehler { 1850b805092SAndreas Boehler if(strpos($page, 'webdav://') === 0) 1860b805092SAndreas Boehler { 1870b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1880b805092SAndreas Boehler if(is_null($wdc)) 1890b805092SAndreas Boehler continue; 1900b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $page); 1910b805092SAndreas Boehler $settings = $wdc->getConnection($connectionId); 1922393a702SAndreas Boehler if($settings === false) 1932393a702SAndreas Boehler continue; 1940b805092SAndreas Boehler $name = $settings['displayname']; 195d71c9934SAndreas Boehler $write = ($settings['write'] === '1'); 1960b805092SAndreas Boehler $calid = $connectionId; 197cd2f100dSAndreas Boehler $color = '#3a87ad'; 1980b805092SAndreas Boehler } 1990b805092SAndreas Boehler else 2000b805092SAndreas Boehler { 201185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($page); 202185e2535SAndreas Boehler if($calid !== false) 203185e2535SAndreas Boehler { 204185e2535SAndreas Boehler $settings = $this->getCalendarSettings($calid); 205185e2535SAndreas Boehler $name = $settings['displayname']; 206cd2f100dSAndreas Boehler $color = $settings['calendarcolor']; 207ed764890SAndreas Boehler $write = (auth_quickaclcheck($page) > AUTH_READ); 2080b805092SAndreas Boehler } 2090b805092SAndreas Boehler else 2100b805092SAndreas Boehler { 2110b805092SAndreas Boehler continue; 2120b805092SAndreas Boehler } 2130b805092SAndreas Boehler } 214185e2535SAndreas Boehler $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid, 215ed764890SAndreas Boehler 'color' => $color, 'write' => $write); 216185e2535SAndreas Boehler } 217185e2535SAndreas Boehler return $data; 218185e2535SAndreas Boehler } 219185e2535SAndreas Boehler 220185e2535SAndreas Boehler /** 221185e2535SAndreas Boehler * Get the saved calendar color for a given page. 222185e2535SAndreas Boehler * 223185e2535SAndreas Boehler * @param string $id optional The page ID 224185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 225185e2535SAndreas Boehler */ 226185e2535SAndreas Boehler public function getCalendarColorForPage($id = null) 227185e2535SAndreas Boehler { 228185e2535SAndreas Boehler if(is_null($id)) 229185e2535SAndreas Boehler { 230185e2535SAndreas Boehler global $ID; 231185e2535SAndreas Boehler $id = $ID; 232185e2535SAndreas Boehler } 233185e2535SAndreas Boehler 234185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 235185e2535SAndreas Boehler if($calid === false) 236185e2535SAndreas Boehler return false; 237185e2535SAndreas Boehler 238185e2535SAndreas Boehler return $this->getCalendarColorForCalendar($calid); 239185e2535SAndreas Boehler } 240185e2535SAndreas Boehler 241185e2535SAndreas Boehler /** 242185e2535SAndreas Boehler * Get the saved calendar color for a given calendar ID. 243185e2535SAndreas Boehler * 244185e2535SAndreas Boehler * @param string $id optional The calendar ID 245185e2535SAndreas Boehler * @return mixed The color on success, otherwise false 246185e2535SAndreas Boehler */ 247185e2535SAndreas Boehler public function getCalendarColorForCalendar($calid) 248185e2535SAndreas Boehler { 249185e2535SAndreas Boehler if(isset($this->cachedValues['calendarcolor'][$calid])) 250185e2535SAndreas Boehler return $this->cachedValues['calendarcolor'][$calid]; 251185e2535SAndreas Boehler 252185e2535SAndreas Boehler $row = $this->getCalendarSettings($calid); 253185e2535SAndreas Boehler 254185e2535SAndreas Boehler if(!isset($row['calendarcolor'])) 255185e2535SAndreas Boehler return false; 256185e2535SAndreas Boehler 257185e2535SAndreas Boehler $color = $row['calendarcolor']; 258185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 259185e2535SAndreas Boehler return $color; 260185e2535SAndreas Boehler } 261185e2535SAndreas Boehler 262185e2535SAndreas Boehler /** 263e86c8dd3SAndreas Boehler * Get the user's principal URL for iOS sync 264e86c8dd3SAndreas Boehler * @param string $user the user name 265e86c8dd3SAndreas Boehler * @return the URL to the principal sync 266e86c8dd3SAndreas Boehler */ 267e86c8dd3SAndreas Boehler public function getPrincipalUrlForUser($user) 268e86c8dd3SAndreas Boehler { 269e86c8dd3SAndreas Boehler if(is_null($user)) 270e86c8dd3SAndreas Boehler return false; 271e86c8dd3SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user; 272e86c8dd3SAndreas Boehler return $url; 273e86c8dd3SAndreas Boehler } 274e86c8dd3SAndreas Boehler 275e86c8dd3SAndreas Boehler /** 276185e2535SAndreas Boehler * Set the calendar color for a given page. 277185e2535SAndreas Boehler * 278185e2535SAndreas Boehler * @param string $color The color definition 279185e2535SAndreas Boehler * @param string $id optional The page ID 280185e2535SAndreas Boehler * @return boolean True on success, otherwise false 281185e2535SAndreas Boehler */ 282185e2535SAndreas Boehler public function setCalendarColorForPage($color, $id = null) 283185e2535SAndreas Boehler { 284185e2535SAndreas Boehler if(is_null($id)) 285185e2535SAndreas Boehler { 286185e2535SAndreas Boehler global $ID; 287185e2535SAndreas Boehler $id = $ID; 288185e2535SAndreas Boehler } 289185e2535SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 290185e2535SAndreas Boehler if($calid === false) 291185e2535SAndreas Boehler return false; 292185e2535SAndreas Boehler 2935f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 2945f2c3e2dSAndreas Boehler if(!$sqlite) 2955f2c3e2dSAndreas Boehler return false; 29651f4febbSAndreas Boehler $query = "UPDATE calendars SET calendarcolor = ? ". 29751f4febbSAndreas Boehler " WHERE id = ?"; 2985f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $color, $calid); 299185e2535SAndreas Boehler if($res !== false) 300185e2535SAndreas Boehler { 301185e2535SAndreas Boehler $this->cachedValues['calendarcolor'][$calid] = $color; 302185e2535SAndreas Boehler return true; 303185e2535SAndreas Boehler } 304185e2535SAndreas Boehler return false; 305185e2535SAndreas Boehler } 306185e2535SAndreas Boehler 307185e2535SAndreas Boehler /** 308cb71a62aSAndreas Boehler * Set the calendar name and description for a given page with a given 309cb71a62aSAndreas Boehler * page id. 310cb71a62aSAndreas Boehler * If the calendar doesn't exist, the calendar is created! 311cb71a62aSAndreas Boehler * 312cb71a62aSAndreas Boehler * @param string $name The name of the new calendar 313cb71a62aSAndreas Boehler * @param string $description The description of the new calendar 314cb71a62aSAndreas Boehler * @param string $id (optional) The ID of the page 315cb71a62aSAndreas Boehler * @param string $userid The userid of the creating user 316cb71a62aSAndreas Boehler * 317cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false. 318cb71a62aSAndreas Boehler */ 319a1a3b679SAndreas Boehler public function setCalendarNameForPage($name, $description, $id = null, $userid = null) 320a1a3b679SAndreas Boehler { 321a1a3b679SAndreas Boehler if(is_null($id)) 322a1a3b679SAndreas Boehler { 323a1a3b679SAndreas Boehler global $ID; 324a1a3b679SAndreas Boehler $id = $ID; 325a1a3b679SAndreas Boehler } 326a1a3b679SAndreas Boehler if(is_null($userid)) 32734a47953SAndreas Boehler { 32834a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 32934a47953SAndreas Boehler { 330a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 33134a47953SAndreas Boehler } 33234a47953SAndreas Boehler else 33334a47953SAndreas Boehler { 33434a47953SAndreas Boehler $userid = uniqid('davcal-'); 33534a47953SAndreas Boehler } 33634a47953SAndreas Boehler } 337a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 338a1a3b679SAndreas Boehler if($calid === false) 339a1a3b679SAndreas Boehler return $this->createCalendarForPage($name, $description, $id, $userid); 340a1a3b679SAndreas Boehler 3415f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3425f2c3e2dSAndreas Boehler if(!$sqlite) 3435f2c3e2dSAndreas Boehler return false; 34451f4febbSAndreas Boehler $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?"; 3455f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $name, $description, $calid); 346b269830cSAndreas Boehler if($res !== false) 347b269830cSAndreas Boehler return true; 348b269830cSAndreas Boehler return false; 349a1a3b679SAndreas Boehler } 350a1a3b679SAndreas Boehler 351cb71a62aSAndreas Boehler /** 352d5703f5aSAndreas Boehler * Update a calendar's displayname 353d5703f5aSAndreas Boehler * 354d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 355d5703f5aSAndreas Boehler * @param string $name The new calendar name 356d5703f5aSAndreas Boehler * 357d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 358d5703f5aSAndreas Boehler */ 359d5703f5aSAndreas Boehler public function updateCalendarName($calid, $name) 360d5703f5aSAndreas Boehler { 3615f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3625f2c3e2dSAndreas Boehler if(!$sqlite) 3635f2c3e2dSAndreas Boehler return false; 364d5703f5aSAndreas Boehler $query = "UPDATE calendars SET displayname = ? WHERE id = ?"; 3655f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $name); 366d5703f5aSAndreas Boehler if($res !== false) 367d5703f5aSAndreas Boehler { 368d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 369d5703f5aSAndreas Boehler return true; 370d5703f5aSAndreas Boehler } 371d5703f5aSAndreas Boehler return false; 372d5703f5aSAndreas Boehler } 373d5703f5aSAndreas Boehler 374d5703f5aSAndreas Boehler /** 375d5703f5aSAndreas Boehler * Update the calendar description 376d5703f5aSAndreas Boehler * 377d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 378d5703f5aSAndreas Boehler * @param string $description The new calendar's description 379d5703f5aSAndreas Boehler * 380d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 381d5703f5aSAndreas Boehler */ 382d5703f5aSAndreas Boehler public function updateCalendarDescription($calid, $description) 383d5703f5aSAndreas Boehler { 3845f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 3855f2c3e2dSAndreas Boehler if(!$sqlite) 3865f2c3e2dSAndreas Boehler return false; 387d5703f5aSAndreas Boehler $query = "UPDATE calendars SET description = ? WHERE id = ?"; 3885f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $description); 389d5703f5aSAndreas Boehler if($res !== false) 390d5703f5aSAndreas Boehler { 391d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 392d5703f5aSAndreas Boehler return true; 393d5703f5aSAndreas Boehler } 394d5703f5aSAndreas Boehler return false; 395d5703f5aSAndreas Boehler } 396d5703f5aSAndreas Boehler 397d5703f5aSAndreas Boehler /** 398d5703f5aSAndreas Boehler * Update a calendar's timezone information 399d5703f5aSAndreas Boehler * 400d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 401d5703f5aSAndreas Boehler * @param string $timezone The new timezone to set 402d5703f5aSAndreas Boehler * 403d5703f5aSAndreas Boehler * @return boolean True on success, otherwise false 404d5703f5aSAndreas Boehler */ 405d5703f5aSAndreas Boehler public function updateCalendarTimezone($calid, $timezone) 406d5703f5aSAndreas Boehler { 4075f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4085f2c3e2dSAndreas Boehler if(!$sqlite) 4095f2c3e2dSAndreas Boehler return false; 410d5703f5aSAndreas Boehler $query = "UPDATE calendars SET timezone = ? WHERE id = ?"; 4115f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $timezone); 412d5703f5aSAndreas Boehler if($res !== false) 413d5703f5aSAndreas Boehler { 414d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, '', 'modified'); 415d5703f5aSAndreas Boehler return true; 416d5703f5aSAndreas Boehler } 417d5703f5aSAndreas Boehler return false; 418d5703f5aSAndreas Boehler } 419d5703f5aSAndreas Boehler 420d5703f5aSAndreas Boehler /** 421cb71a62aSAndreas Boehler * Save the personal settings to the SQLite database 'calendarsettings'. 422cb71a62aSAndreas Boehler * 423cb71a62aSAndreas Boehler * @param array $settings The settings array to store 424cb71a62aSAndreas Boehler * @param string $userid (optional) The userid to store 425cb71a62aSAndreas Boehler * 426cb71a62aSAndreas Boehler * @param boolean True on success, otherwise false 427cb71a62aSAndreas Boehler */ 428a495d34cSAndreas Boehler public function savePersonalSettings($settings, $userid = null) 429a495d34cSAndreas Boehler { 430a495d34cSAndreas Boehler if(is_null($userid)) 43134a47953SAndreas Boehler { 43234a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 43334a47953SAndreas Boehler { 434a495d34cSAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 43534a47953SAndreas Boehler } 43634a47953SAndreas Boehler else 43734a47953SAndreas Boehler { 43834a47953SAndreas Boehler return false; 43934a47953SAndreas Boehler } 44034a47953SAndreas Boehler } 4415f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4425f2c3e2dSAndreas Boehler if(!$sqlite) 4435f2c3e2dSAndreas Boehler return false; 4445f2c3e2dSAndreas Boehler $sqlite->query("BEGIN TRANSACTION"); 445a495d34cSAndreas Boehler 44651f4febbSAndreas Boehler $query = "DELETE FROM calendarsettings WHERE userid = ?"; 4475f2c3e2dSAndreas Boehler $sqlite->query($query, $userid); 448bd883736SAndreas Boehler 449a495d34cSAndreas Boehler foreach($settings as $key => $value) 450a495d34cSAndreas Boehler { 45151f4febbSAndreas Boehler $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)"; 4525f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $userid, $key, $value); 453a495d34cSAndreas Boehler if($res === false) 454a495d34cSAndreas Boehler return false; 455a495d34cSAndreas Boehler } 4565f2c3e2dSAndreas Boehler $sqlite->query("COMMIT TRANSACTION"); 457185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 458a495d34cSAndreas Boehler return true; 459a495d34cSAndreas Boehler } 460a495d34cSAndreas Boehler 461cb71a62aSAndreas Boehler /** 462cb71a62aSAndreas Boehler * Retrieve the settings array for a given user id. 463cb71a62aSAndreas Boehler * Some sane defaults are returned, currently: 464cb71a62aSAndreas Boehler * 465cb71a62aSAndreas Boehler * timezone => local 466cb71a62aSAndreas Boehler * weeknumbers => 0 467cb71a62aSAndreas Boehler * workweek => 0 468cb71a62aSAndreas Boehler * 469cb71a62aSAndreas Boehler * @param string $userid (optional) The user id to retrieve 470cb71a62aSAndreas Boehler * 471cb71a62aSAndreas Boehler * @return array The settings array 472cb71a62aSAndreas Boehler */ 473a495d34cSAndreas Boehler public function getPersonalSettings($userid = null) 474a495d34cSAndreas Boehler { 475bd883736SAndreas Boehler $settings = array( 476fb813b30SAndreas Boehler 'timezone' => $this->getConf('timezone'), 477fb813b30SAndreas Boehler 'weeknumbers' => $this->getConf('weeknumbers'), 478fb813b30SAndreas Boehler 'workweek' => $this->getConf('workweek'), 4791d5bdcd0SAndreas Boehler 'monday' => $this->getConf('monday'), 4801d5bdcd0SAndreas Boehler 'timeformat' => $this->getConf('timeformat') 481bd883736SAndreas Boehler ); 48234a47953SAndreas Boehler if(is_null($userid)) 48334a47953SAndreas Boehler { 48434a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 48534a47953SAndreas Boehler { 48634a47953SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 48734a47953SAndreas Boehler } 48834a47953SAndreas Boehler else 48934a47953SAndreas Boehler { 49034a47953SAndreas Boehler return $settings; 49134a47953SAndreas Boehler } 49234a47953SAndreas Boehler } 49334a47953SAndreas Boehler 4945f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 4955f2c3e2dSAndreas Boehler if(!$sqlite) 4965f2c3e2dSAndreas Boehler return false; 49734a47953SAndreas Boehler if(isset($this->cachedValues['settings'][$userid])) 49834a47953SAndreas Boehler return $this->cachedValues['settings'][$userid]; 49951f4febbSAndreas Boehler $query = "SELECT key, value FROM calendarsettings WHERE userid = ?"; 50061fec75eSscottleechua $arr = $sqlite->queryAll($query, [$userid]); 501a495d34cSAndreas Boehler foreach($arr as $row) 502a495d34cSAndreas Boehler { 503a495d34cSAndreas Boehler $settings[$row['key']] = $row['value']; 504a495d34cSAndreas Boehler } 505185e2535SAndreas Boehler $this->cachedValues['settings'][$userid] = $settings; 506a495d34cSAndreas Boehler return $settings; 507a495d34cSAndreas Boehler } 508a495d34cSAndreas Boehler 509cb71a62aSAndreas Boehler /** 510cb71a62aSAndreas Boehler * Retrieve the calendar ID based on a page ID from the SQLite table 511cb71a62aSAndreas Boehler * 'pagetocalendarmapping'. 512cb71a62aSAndreas Boehler * 513cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to retrieve the corresponding calendar 514cb71a62aSAndreas Boehler * 515cb71a62aSAndreas Boehler * @return mixed the ID on success, otherwise false 516cb71a62aSAndreas Boehler */ 517a1a3b679SAndreas Boehler public function getCalendarIdForPage($id = null) 518a1a3b679SAndreas Boehler { 519a1a3b679SAndreas Boehler if(is_null($id)) 520a1a3b679SAndreas Boehler { 521a1a3b679SAndreas Boehler global $ID; 522a1a3b679SAndreas Boehler $id = $ID; 523a1a3b679SAndreas Boehler } 524a1a3b679SAndreas Boehler 525185e2535SAndreas Boehler if(isset($this->cachedValues['calid'][$id])) 526185e2535SAndreas Boehler return $this->cachedValues['calid'][$id]; 527185e2535SAndreas Boehler 5285f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5295f2c3e2dSAndreas Boehler if(!$sqlite) 5305f2c3e2dSAndreas Boehler return false; 53151f4febbSAndreas Boehler $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?"; 53261fec75eSscottleechua $row = $sqlite->queryRecord($query, [$id]); 533a1a3b679SAndreas Boehler if(isset($row['calid'])) 534185e2535SAndreas Boehler { 535185e2535SAndreas Boehler $calid = $row['calid']; 536185e2535SAndreas Boehler $this->cachedValues['calid'] = $calid; 537185e2535SAndreas Boehler return $calid; 538185e2535SAndreas Boehler } 539a1a3b679SAndreas Boehler return false; 540a1a3b679SAndreas Boehler } 541a1a3b679SAndreas Boehler 542cb71a62aSAndreas Boehler /** 543cb71a62aSAndreas Boehler * Retrieve the complete calendar id to page mapping. 544cb71a62aSAndreas Boehler * This is necessary to be able to retrieve a list of 545cb71a62aSAndreas Boehler * calendars for a given user and check the access rights. 546cb71a62aSAndreas Boehler * 547cb71a62aSAndreas Boehler * @return array The mapping array 548cb71a62aSAndreas Boehler */ 549a1a3b679SAndreas Boehler public function getCalendarIdToPageMapping() 550a1a3b679SAndreas Boehler { 5515f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 5525f2c3e2dSAndreas Boehler if(!$sqlite) 5535f2c3e2dSAndreas Boehler return false; 554a1a3b679SAndreas Boehler $query = "SELECT calid, page FROM pagetocalendarmapping"; 55561fec75eSscottleechua $arr = $sqlite->queryAll($query, []); 556a1a3b679SAndreas Boehler return $arr; 557a1a3b679SAndreas Boehler } 558a1a3b679SAndreas Boehler 559cb71a62aSAndreas Boehler /** 560cb71a62aSAndreas Boehler * Retrieve all calendar IDs a given user has access to. 561cb71a62aSAndreas Boehler * The user is specified by the principalUri, so the 562cb71a62aSAndreas Boehler * user name is actually split from the URI component. 563cb71a62aSAndreas Boehler * 564cb71a62aSAndreas Boehler * Access rights are checked against DokuWiki's ACL 565cb71a62aSAndreas Boehler * and applied accordingly. 566cb71a62aSAndreas Boehler * 567cb71a62aSAndreas Boehler * @param string $principalUri The principal URI to work on 568cb71a62aSAndreas Boehler * 569cb71a62aSAndreas Boehler * @return array An associative array of calendar IDs 570cb71a62aSAndreas Boehler */ 571a1a3b679SAndreas Boehler public function getCalendarIdsForUser($principalUri) 572a1a3b679SAndreas Boehler { 57334a47953SAndreas Boehler global $auth; 574a1a3b679SAndreas Boehler $user = explode('/', $principalUri); 575a1a3b679SAndreas Boehler $user = end($user); 576a1a3b679SAndreas Boehler $mapping = $this->getCalendarIdToPageMapping(); 577a1a3b679SAndreas Boehler $calids = array(); 57834a47953SAndreas Boehler $ud = $auth->getUserData($user); 57934a47953SAndreas Boehler $groups = $ud['grps']; 580a1a3b679SAndreas Boehler foreach($mapping as $row) 581a1a3b679SAndreas Boehler { 582a1a3b679SAndreas Boehler $id = $row['calid']; 58313b16484SAndreas Boehler $enabled = $this->getCalendarStatus($id); 58413b16484SAndreas Boehler if($enabled == false) 58513b16484SAndreas Boehler continue; 586a1a3b679SAndreas Boehler $page = $row['page']; 58734a47953SAndreas Boehler $acl = auth_aclcheck($page, $user, $groups); 588a1a3b679SAndreas Boehler if($acl >= AUTH_READ) 589a1a3b679SAndreas Boehler { 590a1a3b679SAndreas Boehler $write = $acl > AUTH_READ; 591a1a3b679SAndreas Boehler $calids[$id] = array('readonly' => !$write); 592a1a3b679SAndreas Boehler } 593a1a3b679SAndreas Boehler } 594a1a3b679SAndreas Boehler return $calids; 595a1a3b679SAndreas Boehler } 596a1a3b679SAndreas Boehler 597cb71a62aSAndreas Boehler /** 598cb71a62aSAndreas Boehler * Create a new calendar for a given page ID and set name and description 599cb71a62aSAndreas Boehler * accordingly. Also update the pagetocalendarmapping table on success. 600cb71a62aSAndreas Boehler * 601cb71a62aSAndreas Boehler * @param string $name The calendar's name 602cb71a62aSAndreas Boehler * @param string $description The calendar's description 603cb71a62aSAndreas Boehler * @param string $id (optional) The page ID to work on 604cb71a62aSAndreas Boehler * @param string $userid (optional) The user ID that created the calendar 605cb71a62aSAndreas Boehler * 606cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 607cb71a62aSAndreas Boehler */ 608a1a3b679SAndreas Boehler public function createCalendarForPage($name, $description, $id = null, $userid = null) 609a1a3b679SAndreas Boehler { 610a1a3b679SAndreas Boehler if(is_null($id)) 611a1a3b679SAndreas Boehler { 612a1a3b679SAndreas Boehler global $ID; 613a1a3b679SAndreas Boehler $id = $ID; 614a1a3b679SAndreas Boehler } 615a1a3b679SAndreas Boehler if(is_null($userid)) 61634a47953SAndreas Boehler { 61734a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 61834a47953SAndreas Boehler { 619a1a3b679SAndreas Boehler $userid = $_SERVER['REMOTE_USER']; 62034a47953SAndreas Boehler } 62134a47953SAndreas Boehler else 62234a47953SAndreas Boehler { 62334a47953SAndreas Boehler $userid = uniqid('davcal-'); 62434a47953SAndreas Boehler } 62534a47953SAndreas Boehler } 626a1a3b679SAndreas Boehler $values = array('principals/'.$userid, 627a1a3b679SAndreas Boehler $name, 628a1a3b679SAndreas Boehler str_replace(array('/', ' ', ':'), '_', $id), 629a1a3b679SAndreas Boehler $description, 630a1a3b679SAndreas Boehler 'VEVENT,VTODO', 63155a741c0SAndreas Boehler 0, 63255a741c0SAndreas Boehler 1); 6335f2c3e2dSAndreas Boehler 6345f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6355f2c3e2dSAndreas Boehler if(!$sqlite) 6365f2c3e2dSAndreas Boehler return false; 63751f4febbSAndreas Boehler $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ". 63851f4febbSAndreas Boehler "VALUES (?, ?, ?, ?, ?, ?, ?)"; 63961fec75eSscottleechua $sqlite->query($query, $values); 640cb71a62aSAndreas Boehler 64151f4febbSAndreas Boehler $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ". 64251f4febbSAndreas Boehler "uri = ? AND description = ?"; 64361fec75eSscottleechua $row = $sqlite->queryRecord($query, array($values[0], $values[1], $values[2], $values[3])); 644cb71a62aSAndreas Boehler 645a1a3b679SAndreas Boehler if(isset($row['id'])) 646a1a3b679SAndreas Boehler { 64751f4febbSAndreas Boehler $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)"; 64861fec75eSscottleechua $sqlite->query($query, array($id, $row['id'])); 64961fec75eSscottleechua return true; 650a1a3b679SAndreas Boehler } 651a1a3b679SAndreas Boehler 652a1a3b679SAndreas Boehler return false; 653a1a3b679SAndreas Boehler } 654a1a3b679SAndreas Boehler 655cb71a62aSAndreas Boehler /** 656d5703f5aSAndreas Boehler * Add a new calendar entry to the given calendar. Calendar data is 657d5703f5aSAndreas Boehler * specified as ICS file, thus it needs to be parsed first. 658d5703f5aSAndreas Boehler * 659d5703f5aSAndreas Boehler * This is mainly needed for the sync support. 660d5703f5aSAndreas Boehler * 661d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 662d5703f5aSAndreas Boehler * @param string $uri The new object URI 663d5703f5aSAndreas Boehler * @param string $ics The ICS file 664d5703f5aSAndreas Boehler * 665d5703f5aSAndreas Boehler * @return mixed The etag. 666d5703f5aSAndreas Boehler */ 667d5703f5aSAndreas Boehler public function addCalendarEntryToCalendarByICS($calid, $uri, $ics) 668d5703f5aSAndreas Boehler { 669d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 670d5703f5aSAndreas Boehler 6715f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 6725f2c3e2dSAndreas Boehler if(!$sqlite) 6735f2c3e2dSAndreas Boehler return false; 674d5703f5aSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)"; 6755f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 676d5703f5aSAndreas Boehler $calid, 677d5703f5aSAndreas Boehler $uri, 678d5703f5aSAndreas Boehler $ics, 679d5703f5aSAndreas Boehler time(), 680d5703f5aSAndreas Boehler $extraData['etag'], 681d5703f5aSAndreas Boehler $extraData['size'], 682d5703f5aSAndreas Boehler $extraData['componentType'], 683d5703f5aSAndreas Boehler $extraData['firstOccurence'], 684d5703f5aSAndreas Boehler $extraData['lastOccurence'], 685d5703f5aSAndreas Boehler $extraData['uid']); 686d5703f5aSAndreas Boehler // If successfully, update the sync token database 687d5703f5aSAndreas Boehler if($res !== false) 688d5703f5aSAndreas Boehler { 689d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 690d5703f5aSAndreas Boehler } 691d5703f5aSAndreas Boehler return $extraData['etag']; 692d5703f5aSAndreas Boehler } 693d5703f5aSAndreas Boehler 694d5703f5aSAndreas Boehler /** 695d5703f5aSAndreas Boehler * Edit a calendar entry by providing a new ICS file. This is mainly 696d5703f5aSAndreas Boehler * needed for the sync support. 697d5703f5aSAndreas Boehler * 698d5703f5aSAndreas Boehler * @param int $calid The calendar's IS 699d5703f5aSAndreas Boehler * @param string $uri The object's URI to modify 700d5703f5aSAndreas Boehler * @param string $ics The new object's ICS file 701d5703f5aSAndreas Boehler */ 702d5703f5aSAndreas Boehler public function editCalendarEntryToCalendarByICS($calid, $uri, $ics) 703d5703f5aSAndreas Boehler { 704d5703f5aSAndreas Boehler $extraData = $this->getDenormalizedData($ics); 705d5703f5aSAndreas Boehler 7065f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 7075f2c3e2dSAndreas Boehler if(!$sqlite) 7085f2c3e2dSAndreas Boehler return false; 709d5703f5aSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?"; 7105f2c3e2dSAndreas Boehler $res = $sqlite->query($query, 711d5703f5aSAndreas Boehler $ics, 712d5703f5aSAndreas Boehler time(), 713d5703f5aSAndreas Boehler $extraData['etag'], 714d5703f5aSAndreas Boehler $extraData['size'], 715d5703f5aSAndreas Boehler $extraData['componentType'], 716d5703f5aSAndreas Boehler $extraData['firstOccurence'], 717d5703f5aSAndreas Boehler $extraData['lastOccurence'], 718d5703f5aSAndreas Boehler $extraData['uid'], 719d5703f5aSAndreas Boehler $calid, 720d5703f5aSAndreas Boehler $uri 721d5703f5aSAndreas Boehler ); 722d5703f5aSAndreas Boehler if($res !== false) 723d5703f5aSAndreas Boehler { 724d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 725d5703f5aSAndreas Boehler } 726d5703f5aSAndreas Boehler return $extraData['etag']; 727d5703f5aSAndreas Boehler } 728d5703f5aSAndreas Boehler 729d5703f5aSAndreas Boehler /** 730cb71a62aSAndreas Boehler * Add a new iCal entry for a given page, i.e. a given calendar. 731cb71a62aSAndreas Boehler * 732cb71a62aSAndreas Boehler * The parameter array needs to contain 733cb71a62aSAndreas Boehler * detectedtz => The timezone as detected by the browser 73482a48dfbSAndreas Boehler * currenttz => The timezone in use by the calendar 735cb71a62aSAndreas Boehler * eventfrom => The event's start date 736cb71a62aSAndreas Boehler * eventfromtime => The event's start time 737cb71a62aSAndreas Boehler * eventto => The event's end date 738cb71a62aSAndreas Boehler * eventtotime => The event's end time 739cb71a62aSAndreas Boehler * eventname => The event's name 740cb71a62aSAndreas Boehler * eventdescription => The event's description 741cb71a62aSAndreas Boehler * 742cb71a62aSAndreas Boehler * @param string $id The page ID to work on 743cb71a62aSAndreas Boehler * @param string $user The user who created the calendar 744cb71a62aSAndreas Boehler * @param string $params A parameter array with values to create 745cb71a62aSAndreas Boehler * 746cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 747cb71a62aSAndreas Boehler */ 748a1a3b679SAndreas Boehler public function addCalendarEntryToCalendarForPage($id, $user, $params) 749a1a3b679SAndreas Boehler { 75082a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 75182a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 75282a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 753a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 754bd883736SAndreas Boehler else 755bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 756cb71a62aSAndreas Boehler 757cb71a62aSAndreas Boehler // Retrieve dates from settings 758b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 759b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 760b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 761b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 762cb71a62aSAndreas Boehler 763cb71a62aSAndreas Boehler // Load SabreDAV 7649bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 765a1a3b679SAndreas Boehler $vcalendar = new \Sabre\VObject\Component\VCalendar(); 766cb71a62aSAndreas Boehler 767cb71a62aSAndreas Boehler // Add VCalendar, UID and Event Name 768a1a3b679SAndreas Boehler $event = $vcalendar->add('VEVENT'); 769b269830cSAndreas Boehler $uuid = \Sabre\VObject\UUIDUtil::getUUID(); 770b269830cSAndreas Boehler $event->add('UID', $uuid); 771a1a3b679SAndreas Boehler $event->summary = $params['eventname']; 772cb71a62aSAndreas Boehler 773cb71a62aSAndreas Boehler // Add a description if requested 7740eebc909SAndreas Boehler $description = $params['eventdescription']; 7750eebc909SAndreas Boehler if($description !== '') 7760eebc909SAndreas Boehler $event->add('DESCRIPTION', $description); 777cb71a62aSAndreas Boehler 7782b7be5bdSAndreas Boehler // Add a location if requested 7792b7be5bdSAndreas Boehler $location = $params['eventlocation']; 7802b7be5bdSAndreas Boehler if($location !== '') 7812b7be5bdSAndreas Boehler $event->add('LOCATION', $location); 7822b7be5bdSAndreas Boehler 7834ecb526cSAndreas Boehler // Add attachments 7844ecb526cSAndreas Boehler $attachments = $params['attachments']; 78582a48dfbSAndreas Boehler if(!is_null($attachments)) 7864ecb526cSAndreas Boehler foreach($attachments as $attachment) 7874ecb526cSAndreas Boehler $event->add('ATTACH', $attachment); 7884ecb526cSAndreas Boehler 789cb71a62aSAndreas Boehler // Create a timestamp for last modified, created and dtstamp values in UTC 790b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 791b269830cSAndreas Boehler $event->add('DTSTAMP', $dtStamp); 792b269830cSAndreas Boehler $event->add('CREATED', $dtStamp); 793b269830cSAndreas Boehler $event->add('LAST-MODIFIED', $dtStamp); 794cb71a62aSAndreas Boehler 795cb71a62aSAndreas Boehler // Adjust the start date, based on the given timezone information 796b269830cSAndreas Boehler $dtStart = new \DateTime(); 797a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 798b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 799cb71a62aSAndreas Boehler 800cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 801b269830cSAndreas Boehler if($params['allday'] != '1') 802b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 803cb71a62aSAndreas Boehler 804cb71a62aSAndreas Boehler // Adjust the end date, based on the given timezone information 805b269830cSAndreas Boehler $dtEnd = new \DateTime(); 806a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 807b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 808cb71a62aSAndreas Boehler 809cb71a62aSAndreas Boehler // Only add the time values if it's not an allday event 810b269830cSAndreas Boehler if($params['allday'] != '1') 811b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 812cb71a62aSAndreas Boehler 813b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 814b269830cSAndreas Boehler if($params['allday'] == '1') 815b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 816cb71a62aSAndreas Boehler 817cb71a62aSAndreas Boehler // Really add Start and End events 818b269830cSAndreas Boehler $dtStartEv = $event->add('DTSTART', $dtStart); 819b269830cSAndreas Boehler $dtEndEv = $event->add('DTEND', $dtEnd); 820cb71a62aSAndreas Boehler 821cb71a62aSAndreas Boehler // Adjust the DATE format for allday events 822b269830cSAndreas Boehler if($params['allday'] == '1') 823b269830cSAndreas Boehler { 824b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 825b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 826b269830cSAndreas Boehler } 827cb71a62aSAndreas Boehler 828809cb0faSAndreas Boehler $eventStr = $vcalendar->serialize(); 829809cb0faSAndreas Boehler 830809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 831809cb0faSAndreas Boehler { 832809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 833809cb0faSAndreas Boehler if(is_null($wdc)) 834809cb0faSAndreas Boehler return false; 835809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 836809cb0faSAndreas Boehler return $wdc->addCalendarEntry($connectionId, $eventStr); 837809cb0faSAndreas Boehler } 838809cb0faSAndreas Boehler else 839809cb0faSAndreas Boehler { 840cb71a62aSAndreas Boehler // Actually add the values to the database 841a1a3b679SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 84202eea9b8SAndreas Boehler $uri = $uri = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 8431bb22c2bSAndreas Boehler $now = new \DateTime(); 844a1a3b679SAndreas Boehler 8455f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8465f2c3e2dSAndreas Boehler if(!$sqlite) 8475f2c3e2dSAndreas Boehler return false; 84851f4febbSAndreas Boehler $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; 8495f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT', 85051f4febbSAndreas Boehler $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(), 85151f4febbSAndreas Boehler strlen($eventStr), md5($eventStr), $uuid); 852cb71a62aSAndreas Boehler 853cb71a62aSAndreas Boehler // If successfully, update the sync token database 85455a741c0SAndreas Boehler if($res !== false) 85555a741c0SAndreas Boehler { 85655a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'added'); 857a1a3b679SAndreas Boehler return true; 858a1a3b679SAndreas Boehler } 859809cb0faSAndreas Boehler } 86055a741c0SAndreas Boehler return false; 86155a741c0SAndreas Boehler } 862a1a3b679SAndreas Boehler 863cb71a62aSAndreas Boehler /** 864cb71a62aSAndreas Boehler * Retrieve the calendar settings of a given calendar id 865cb71a62aSAndreas Boehler * 866cb71a62aSAndreas Boehler * @param string $calid The calendar ID 867cb71a62aSAndreas Boehler * 868cb71a62aSAndreas Boehler * @return array The calendar settings array 869cb71a62aSAndreas Boehler */ 870b269830cSAndreas Boehler public function getCalendarSettings($calid) 871b269830cSAndreas Boehler { 8725f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8735f2c3e2dSAndreas Boehler if(!$sqlite) 8745f2c3e2dSAndreas Boehler return false; 875d99db92eSscottleechua $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled, timezone FROM calendars WHERE id= ? "; 87661fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 877b269830cSAndreas Boehler return $row; 878b269830cSAndreas Boehler } 879b269830cSAndreas Boehler 880cb71a62aSAndreas Boehler /** 88113b16484SAndreas Boehler * Retrieve the calendar status of a given calendar id 88213b16484SAndreas Boehler * 88313b16484SAndreas Boehler * @param string $calid The calendar ID 88413b16484SAndreas Boehler * @return boolean True if calendar is enabled, otherwise false 88513b16484SAndreas Boehler */ 88613b16484SAndreas Boehler public function getCalendarStatus($calid) 88713b16484SAndreas Boehler { 8885f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 8895f2c3e2dSAndreas Boehler if(!$sqlite) 8905f2c3e2dSAndreas Boehler return false; 89113b16484SAndreas Boehler $query = "SELECT disabled FROM calendars WHERE id = ?"; 89261fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 89361fec75eSscottleechua if($row && $row['disabled'] == 1) 89413b16484SAndreas Boehler return false; 89513b16484SAndreas Boehler else 89613b16484SAndreas Boehler return true; 89713b16484SAndreas Boehler } 89813b16484SAndreas Boehler 89913b16484SAndreas Boehler /** 90013b16484SAndreas Boehler * Disable a calendar for a given page 90113b16484SAndreas Boehler * 90213b16484SAndreas Boehler * @param string $id The page ID 90313b16484SAndreas Boehler * 90413b16484SAndreas Boehler * @return boolean true on success, otherwise false 90513b16484SAndreas Boehler */ 90613b16484SAndreas Boehler public function disableCalendarForPage($id) 90713b16484SAndreas Boehler { 90813b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 90913b16484SAndreas Boehler if($calid === false) 91013b16484SAndreas Boehler return false; 9115f2c3e2dSAndreas Boehler 9125f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9135f2c3e2dSAndreas Boehler if(!$sqlite) 9145f2c3e2dSAndreas Boehler return false; 91513b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 1 WHERE id = ?"; 9165f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 91713b16484SAndreas Boehler if($res !== false) 91813b16484SAndreas Boehler return true; 91913b16484SAndreas Boehler return false; 92013b16484SAndreas Boehler } 92113b16484SAndreas Boehler 92213b16484SAndreas Boehler /** 92313b16484SAndreas Boehler * Enable a calendar for a given page 92413b16484SAndreas Boehler * 92513b16484SAndreas Boehler * @param string $id The page ID 92613b16484SAndreas Boehler * 92713b16484SAndreas Boehler * @return boolean true on success, otherwise false 92813b16484SAndreas Boehler */ 92913b16484SAndreas Boehler public function enableCalendarForPage($id) 93013b16484SAndreas Boehler { 93113b16484SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 93213b16484SAndreas Boehler if($calid === false) 93313b16484SAndreas Boehler return false; 9345f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9355f2c3e2dSAndreas Boehler if(!$sqlite) 9365f2c3e2dSAndreas Boehler return false; 93713b16484SAndreas Boehler $query = "UPDATE calendars SET disabled = 0 WHERE id = ?"; 9385f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid); 93913b16484SAndreas Boehler if($res !== false) 94013b16484SAndreas Boehler return true; 94113b16484SAndreas Boehler return false; 94213b16484SAndreas Boehler } 94313b16484SAndreas Boehler 94413b16484SAndreas Boehler /** 945cb71a62aSAndreas Boehler * Retrieve all events that are within a given date range, 946cb71a62aSAndreas Boehler * based on the timezone setting. 947cb71a62aSAndreas Boehler * 948cb71a62aSAndreas Boehler * There is also support for retrieving recurring events, 949cb71a62aSAndreas Boehler * using Sabre's VObject Iterator. Recurring events are represented 950cb71a62aSAndreas Boehler * as individual calendar entries with the same UID. 951cb71a62aSAndreas Boehler * 952cb71a62aSAndreas Boehler * @param string $id The page ID to work with 953cb71a62aSAndreas Boehler * @param string $user The user ID to work with 954cb71a62aSAndreas Boehler * @param string $startDate The start date as a string 955cb71a62aSAndreas Boehler * @param string $endDate The end date as a string 9564a2bf5eeSAndreas Boehler * @param string $color (optional) The calendar's color 957cb71a62aSAndreas Boehler * 958cb71a62aSAndreas Boehler * @return array An array containing the calendar entries. 959cb71a62aSAndreas Boehler */ 9604a2bf5eeSAndreas Boehler public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null) 961a1a3b679SAndreas Boehler { 96282a48dfbSAndreas Boehler if($timezone !== '' && $timezone !== 'local') 96382a48dfbSAndreas Boehler $timezone = new \DateTimeZone($timezone); 964bd883736SAndreas Boehler else 965bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 966a1a3b679SAndreas Boehler $data = array(); 967b8265976SAndreas Boehler $calname = 'unknown'; 968cb71a62aSAndreas Boehler 9695f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 9705f2c3e2dSAndreas Boehler if(!$sqlite) 9715f2c3e2dSAndreas Boehler return false; 9725f2c3e2dSAndreas Boehler 97361fec75eSscottleechua $calid = $this->getCalendarIdForPage($id); 974a469597cSAndreas Boehler $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?"; 9757e2d2436Sscottleechua $params = array($calid); 976a469597cSAndreas Boehler $startTs = null; 977a469597cSAndreas Boehler $endTs = null; 978a469597cSAndreas Boehler if($startDate !== null) 979a469597cSAndreas Boehler { 980a1a3b679SAndreas Boehler $startTs = new \DateTime($startDate); 9817e2d2436Sscottleechua $query .= " AND lastoccurence > ?"; 9827e2d2436Sscottleechua $params[] = $startTs->getTimestamp(); 983a469597cSAndreas Boehler } 984a469597cSAndreas Boehler if($endDate !== null) 985a469597cSAndreas Boehler { 986a1a3b679SAndreas Boehler $endTs = new \DateTime($endDate); 9877e2d2436Sscottleechua $query .= " AND firstoccurence < ?"; 9887e2d2436Sscottleechua $params[] = $endTs->getTimestamp(); 989a469597cSAndreas Boehler } 990cb71a62aSAndreas Boehler 9910b805092SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 9920b805092SAndreas Boehler 9930b805092SAndreas Boehler if(strpos($id, 'webdav://') === 0) 9940b805092SAndreas Boehler { 9950b805092SAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 9960b805092SAndreas Boehler if(is_null($wdc)) 9970b805092SAndreas Boehler return $data; 9980b805092SAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 9990b805092SAndreas Boehler $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate); 1000b8265976SAndreas Boehler $conn = $wdc->getConnection($connectionId); 1001b8265976SAndreas Boehler $calname = $conn['displayname']; 10020b805092SAndreas Boehler } 10030b805092SAndreas Boehler else 10040b805092SAndreas Boehler { 10050b805092SAndreas Boehler if(is_null($color)) 10060b805092SAndreas Boehler $color = $this->getCalendarColorForCalendar($calid); 10070b805092SAndreas Boehler 100859b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 100959b68239SAndreas Boehler if($enabled === false) 101059b68239SAndreas Boehler return $data; 101159b68239SAndreas Boehler 1012b8265976SAndreas Boehler $settings = $this->getCalendarSettings($calid); 1013b8265976SAndreas Boehler $calname = $settings['displayname']; 1014b8265976SAndreas Boehler 101561fec75eSscottleechua $arr = $sqlite->queryAll($query, $params); 10160b805092SAndreas Boehler } 1017cb71a62aSAndreas Boehler 1018a1a3b679SAndreas Boehler foreach($arr as $row) 1019a1a3b679SAndreas Boehler { 1020a1a3b679SAndreas Boehler if(isset($row['calendardata'])) 1021a1a3b679SAndreas Boehler { 1022b269830cSAndreas Boehler $entry = array(); 1023a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 1024ebc4eb57SAndreas Boehler $recurrence = $vcal->VEVENT->RRULE; 1025ebc4eb57SAndreas Boehler if($recurrence != null) 1026ebc4eb57SAndreas Boehler { 1027ebc4eb57SAndreas Boehler $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT)); 1028ebc4eb57SAndreas Boehler $rEvents->rewind(); 1029e9b7d302SAndreas Boehler while($rEvents->valid()) 1030ebc4eb57SAndreas Boehler { 1031ebc4eb57SAndreas Boehler $event = $rEvents->getEventObject(); 1032a469597cSAndreas Boehler if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp())) 1033e9b7d302SAndreas Boehler break; 1034a469597cSAndreas Boehler if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())) 1035ebc4eb57SAndreas Boehler { 1036ebc4eb57SAndreas Boehler $rEvents->next(); 1037ebc4eb57SAndreas Boehler continue; 1038ebc4eb57SAndreas Boehler } 1039b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1040b8265976SAndreas Boehler $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true)); 1041ebc4eb57SAndreas Boehler $rEvents->next(); 1042ebc4eb57SAndreas Boehler } 1043ebc4eb57SAndreas Boehler } 1044ebc4eb57SAndreas Boehler else 1045b8265976SAndreas Boehler $data[] = array_merge(array('calendarname' => $calname), 1046b8265976SAndreas Boehler $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color)); 1047ebc4eb57SAndreas Boehler } 1048ebc4eb57SAndreas Boehler } 1049ebc4eb57SAndreas Boehler return $data; 1050ebc4eb57SAndreas Boehler } 1051ebc4eb57SAndreas Boehler 1052cb71a62aSAndreas Boehler /** 1053cb71a62aSAndreas Boehler * Helper function that parses the iCal data of a VEVENT to a calendar entry. 1054cb71a62aSAndreas Boehler * 1055cb71a62aSAndreas Boehler * @param \Sabre\VObject\VEvent $event The event to parse 1056cb71a62aSAndreas Boehler * @param \DateTimeZone $timezone The timezone object 1057cb71a62aSAndreas Boehler * @param string $uid The entry's UID 10583c86dda8SAndreas Boehler * @param boolean $recurring (optional) Set to true to define a recurring event 1059cb71a62aSAndreas Boehler * 1060cb71a62aSAndreas Boehler * @return array The parse calendar entry 1061cb71a62aSAndreas Boehler */ 1062185e2535SAndreas Boehler private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false) 1063ebc4eb57SAndreas Boehler { 1064ebc4eb57SAndreas Boehler $entry = array(); 1065ebc4eb57SAndreas Boehler $start = $event->DTSTART; 1066cb71a62aSAndreas Boehler // Parse only if the start date/time is present 1067b269830cSAndreas Boehler if($start !== null) 1068b269830cSAndreas Boehler { 1069b269830cSAndreas Boehler $dtStart = $start->getDateTime(); 1070b269830cSAndreas Boehler $dtStart->setTimezone($timezone); 1071bf0ad2b4SAndreas Boehler 1072bf0ad2b4SAndreas Boehler // moment.js doesn't like times be given even if 1073bf0ad2b4SAndreas Boehler // allDay is set to true 1074bf0ad2b4SAndreas Boehler // This should fix T23 1075b269830cSAndreas Boehler if($start['VALUE'] == 'DATE') 1076bf0ad2b4SAndreas Boehler { 1077b269830cSAndreas Boehler $entry['allDay'] = true; 1078bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format("Y-m-d"); 1079bf0ad2b4SAndreas Boehler } 1080b269830cSAndreas Boehler else 1081bf0ad2b4SAndreas Boehler { 1082b269830cSAndreas Boehler $entry['allDay'] = false; 1083bf0ad2b4SAndreas Boehler $entry['start'] = $dtStart->format(\DateTime::ATOM); 1084bf0ad2b4SAndreas Boehler } 1085b269830cSAndreas Boehler } 1086ebc4eb57SAndreas Boehler $end = $event->DTEND; 1087bf0ad2b4SAndreas Boehler // Parse only if the end date/time is present 1088b269830cSAndreas Boehler if($end !== null) 1089b269830cSAndreas Boehler { 1090b269830cSAndreas Boehler $dtEnd = $end->getDateTime(); 1091b269830cSAndreas Boehler $dtEnd->setTimezone($timezone); 1092bf0ad2b4SAndreas Boehler if($end['VALUE'] == 'DATE') 1093bf0ad2b4SAndreas Boehler $entry['end'] = $dtEnd->format("Y-m-d"); 1094bf0ad2b4SAndreas Boehler else 1095b269830cSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1096b269830cSAndreas Boehler } 1097954f1ffaSAndreas Boehler $duration = $event->DURATION; 1098954f1ffaSAndreas Boehler // Parse duration only if start is set, but end is missing 1099954f1ffaSAndreas Boehler if($start !== null && $end == null && $duration !== null) 1100954f1ffaSAndreas Boehler { 1101954f1ffaSAndreas Boehler $interval = $duration->getDateInterval(); 1102954f1ffaSAndreas Boehler $dtStart = $start->getDateTime(); 1103954f1ffaSAndreas Boehler $dtStart->setTimezone($timezone); 1104954f1ffaSAndreas Boehler $dtEnd = $dtStart->add($interval); 1105954f1ffaSAndreas Boehler $dtEnd->setTimezone($timezone); 1106954f1ffaSAndreas Boehler $entry['end'] = $dtEnd->format(\DateTime::ATOM); 1107954f1ffaSAndreas Boehler } 1108ebc4eb57SAndreas Boehler $description = $event->DESCRIPTION; 11090eebc909SAndreas Boehler if($description !== null) 11100eebc909SAndreas Boehler $entry['description'] = (string)$description; 11110eebc909SAndreas Boehler else 11120eebc909SAndreas Boehler $entry['description'] = ''; 11134ecb526cSAndreas Boehler $attachments = $event->ATTACH; 11144ecb526cSAndreas Boehler if($attachments !== null) 11154ecb526cSAndreas Boehler { 11164ecb526cSAndreas Boehler $entry['attachments'] = array(); 11174ecb526cSAndreas Boehler foreach($attachments as $attachment) 11184ecb526cSAndreas Boehler $entry['attachments'][] = (string)$attachment; 11194ecb526cSAndreas Boehler } 1120ebc4eb57SAndreas Boehler $entry['title'] = (string)$event->summary; 11212b7be5bdSAndreas Boehler $entry['location'] = (string)$event->location; 1122ebc4eb57SAndreas Boehler $entry['id'] = $uid; 1123185e2535SAndreas Boehler $entry['page'] = $page; 1124185e2535SAndreas Boehler $entry['color'] = $color; 11253c86dda8SAndreas Boehler $entry['recurring'] = $recurring; 1126185e2535SAndreas Boehler 1127ebc4eb57SAndreas Boehler return $entry; 1128a1a3b679SAndreas Boehler } 1129a1a3b679SAndreas Boehler 1130cb71a62aSAndreas Boehler /** 1131cb71a62aSAndreas Boehler * Retrieve an event by its UID 1132cb71a62aSAndreas Boehler * 1133cb71a62aSAndreas Boehler * @param string $uid The event's UID 1134cb71a62aSAndreas Boehler * 1135cb71a62aSAndreas Boehler * @return mixed The table row with the given event 1136cb71a62aSAndreas Boehler */ 1137a1a3b679SAndreas Boehler public function getEventWithUid($uid) 1138a1a3b679SAndreas Boehler { 11395f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11405f2c3e2dSAndreas Boehler if(!$sqlite) 11415f2c3e2dSAndreas Boehler return false; 114251f4febbSAndreas Boehler $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?"; 114361fec75eSscottleechua $row = $sqlite->queryRecord($query, [$uid]); 1144a1a3b679SAndreas Boehler return $row; 1145a1a3b679SAndreas Boehler } 1146a1a3b679SAndreas Boehler 1147cb71a62aSAndreas Boehler /** 1148d5703f5aSAndreas Boehler * Retrieve information of a calendar's object, not including the actual 114959b68239SAndreas Boehler * calendar data! This is mainly needed for the sync support. 1150d5703f5aSAndreas Boehler * 1151d5703f5aSAndreas Boehler * @param int $calid The calendar ID 1152d5703f5aSAndreas Boehler * 1153d5703f5aSAndreas Boehler * @return mixed The result 1154d5703f5aSAndreas Boehler */ 1155d5703f5aSAndreas Boehler public function getCalendarObjects($calid) 1156d5703f5aSAndreas Boehler { 11575f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11585f2c3e2dSAndreas Boehler if(!$sqlite) 11595f2c3e2dSAndreas Boehler return false; 1160d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?"; 116161fec75eSscottleechua $arr = $sqlite->queryAll($query, [$calid]); 1162d5703f5aSAndreas Boehler return $arr; 1163d5703f5aSAndreas Boehler } 1164d5703f5aSAndreas Boehler 1165d5703f5aSAndreas Boehler /** 1166d5703f5aSAndreas Boehler * Retrieve a single calendar object by calendar ID and URI 1167d5703f5aSAndreas Boehler * 1168d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1169d5703f5aSAndreas Boehler * @param string $uri The object's URI 1170d5703f5aSAndreas Boehler * 1171d5703f5aSAndreas Boehler * @return mixed The result 1172d5703f5aSAndreas Boehler */ 1173d5703f5aSAndreas Boehler public function getCalendarObjectByUri($calid, $uri) 1174d5703f5aSAndreas Boehler { 11755f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11765f2c3e2dSAndreas Boehler if(!$sqlite) 11775f2c3e2dSAndreas Boehler return false; 1178d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 117961fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid, $uri]); 1180d5703f5aSAndreas Boehler return $row; 1181d5703f5aSAndreas Boehler } 1182d5703f5aSAndreas Boehler 1183d5703f5aSAndreas Boehler /** 1184d5703f5aSAndreas Boehler * Retrieve several calendar objects by specifying an array of URIs. 1185d5703f5aSAndreas Boehler * This is mainly neede for sync. 1186d5703f5aSAndreas Boehler * 1187d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1188d5703f5aSAndreas Boehler * @param array $uris An array of URIs 1189d5703f5aSAndreas Boehler * 1190d5703f5aSAndreas Boehler * @return mixed The result 1191d5703f5aSAndreas Boehler */ 1192d5703f5aSAndreas Boehler public function getMultipleCalendarObjectsByUri($calid, $uris) 1193d5703f5aSAndreas Boehler { 11945f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 11955f2c3e2dSAndreas Boehler if(!$sqlite) 11965f2c3e2dSAndreas Boehler return false; 1197d5703f5aSAndreas Boehler $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN ("; 1198d5703f5aSAndreas Boehler $query .= implode(',', array_fill(0, count($uris), '?')); 1199d5703f5aSAndreas Boehler $query .= ')'; 1200d5703f5aSAndreas Boehler $vals = array_merge(array($calid), $uris); 120161fec75eSscottleechua $arr = $sqlite->queryAll($query, $vals); 1202d5703f5aSAndreas Boehler return $arr; 1203d5703f5aSAndreas Boehler } 1204d5703f5aSAndreas Boehler 1205d5703f5aSAndreas Boehler /** 1206cb71a62aSAndreas Boehler * Retrieve all calendar events for a given calendar ID 1207cb71a62aSAndreas Boehler * 1208cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1209cb71a62aSAndreas Boehler * 1210cb71a62aSAndreas Boehler * @return array An array containing all calendar data 1211cb71a62aSAndreas Boehler */ 1212f69bb449SAndreas Boehler public function getAllCalendarEvents($calid) 1213f69bb449SAndreas Boehler { 121459b68239SAndreas Boehler $enabled = $this->getCalendarStatus($calid); 121559b68239SAndreas Boehler if($enabled === false) 121659b68239SAndreas Boehler return false; 12175f2c3e2dSAndreas Boehler 12185f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 12195f2c3e2dSAndreas Boehler if(!$sqlite) 12205f2c3e2dSAndreas Boehler return false; 12217e0b8590SAndreas Boehler $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?"; 122261fec75eSscottleechua $arr = $sqlite->queryAll($query, [$calid]); 1223f69bb449SAndreas Boehler return $arr; 1224f69bb449SAndreas Boehler } 1225f69bb449SAndreas Boehler 1226cb71a62aSAndreas Boehler /** 1227cb71a62aSAndreas Boehler * Edit a calendar entry for a page, given by its parameters. 1228cb71a62aSAndreas Boehler * The params array has the same format as @see addCalendarEntryForPage 1229cb71a62aSAndreas Boehler * 1230cb71a62aSAndreas Boehler * @param string $id The page's ID to work on 1231cb71a62aSAndreas Boehler * @param string $user The user's ID to work on 1232cb71a62aSAndreas Boehler * @param array $params The parameter array for the edited calendar event 1233cb71a62aSAndreas Boehler * 1234cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1235cb71a62aSAndreas Boehler */ 1236a1a3b679SAndreas Boehler public function editCalendarEntryForPage($id, $user, $params) 1237a1a3b679SAndreas Boehler { 123882a48dfbSAndreas Boehler if($params['currenttz'] !== '' && $params['currenttz'] !== 'local') 123982a48dfbSAndreas Boehler $timezone = new \DateTimeZone($params['currenttz']); 124082a48dfbSAndreas Boehler elseif($params['currenttz'] === 'local') 1241a25c89eaSAndreas Boehler $timezone = new \DateTimeZone($params['detectedtz']); 1242bd883736SAndreas Boehler else 1243bd883736SAndreas Boehler $timezone = new \DateTimeZone('UTC'); 1244cb71a62aSAndreas Boehler 1245cb71a62aSAndreas Boehler // Parse dates 1246b269830cSAndreas Boehler $startDate = explode('-', $params['eventfrom']); 1247b269830cSAndreas Boehler $startTime = explode(':', $params['eventfromtime']); 1248b269830cSAndreas Boehler $endDate = explode('-', $params['eventto']); 1249b269830cSAndreas Boehler $endTime = explode(':', $params['eventtotime']); 1250cb71a62aSAndreas Boehler 1251cb71a62aSAndreas Boehler // Retrieve the existing event based on the UID 125255a741c0SAndreas Boehler $uid = $params['uid']; 1253809cb0faSAndreas Boehler 1254809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1255809cb0faSAndreas Boehler { 1256809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1257809cb0faSAndreas Boehler if(is_null($wdc)) 1258809cb0faSAndreas Boehler return false; 1259809cb0faSAndreas Boehler $event = $wdc->getCalendarEntryByUid($uid); 1260809cb0faSAndreas Boehler } 1261809cb0faSAndreas Boehler else 1262809cb0faSAndreas Boehler { 126355a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 1264809cb0faSAndreas Boehler } 1265cb71a62aSAndreas Boehler 1266cb71a62aSAndreas Boehler // Load SabreDAV 12679bef4ad8SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1268a1a3b679SAndreas Boehler if(!isset($event['calendardata'])) 1269a1a3b679SAndreas Boehler return false; 127055a741c0SAndreas Boehler $uri = $event['uri']; 127155a741c0SAndreas Boehler $calid = $event['calendarid']; 1272cb71a62aSAndreas Boehler 1273cb71a62aSAndreas Boehler // Parse the existing event 1274a1a3b679SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1275b269830cSAndreas Boehler $vevent = $vcal->VEVENT; 1276cb71a62aSAndreas Boehler 1277cb71a62aSAndreas Boehler // Set the new event values 1278b269830cSAndreas Boehler $vevent->summary = $params['eventname']; 1279b269830cSAndreas Boehler $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 12800eebc909SAndreas Boehler $description = $params['eventdescription']; 12812b7be5bdSAndreas Boehler $location = $params['eventlocation']; 1282cb71a62aSAndreas Boehler 1283cb71a62aSAndreas Boehler // Remove existing timestamps to overwrite them 12840eebc909SAndreas Boehler $vevent->remove('DESCRIPTION'); 1285b269830cSAndreas Boehler $vevent->remove('DTSTAMP'); 1286b269830cSAndreas Boehler $vevent->remove('LAST-MODIFIED'); 12874ecb526cSAndreas Boehler $vevent->remove('ATTACH'); 12882b7be5bdSAndreas Boehler $vevent->remove('LOCATION'); 1289cb71a62aSAndreas Boehler 12902b7be5bdSAndreas Boehler // Add new time stamps, description and location 1291b269830cSAndreas Boehler $vevent->add('DTSTAMP', $dtStamp); 1292b269830cSAndreas Boehler $vevent->add('LAST-MODIFIED', $dtStamp); 12930eebc909SAndreas Boehler if($description !== '') 12940eebc909SAndreas Boehler $vevent->add('DESCRIPTION', $description); 12952b7be5bdSAndreas Boehler if($location !== '') 12962b7be5bdSAndreas Boehler $vevent->add('LOCATION', $location); 1297cb71a62aSAndreas Boehler 12984ecb526cSAndreas Boehler // Add attachments 12994ecb526cSAndreas Boehler $attachments = $params['attachments']; 130082a48dfbSAndreas Boehler if(!is_null($attachments)) 13014ecb526cSAndreas Boehler foreach($attachments as $attachment) 13024ecb526cSAndreas Boehler $vevent->add('ATTACH', $attachment); 13034ecb526cSAndreas Boehler 1304cb71a62aSAndreas Boehler // Setup DTSTART 1305b269830cSAndreas Boehler $dtStart = new \DateTime(); 1306a25c89eaSAndreas Boehler $dtStart->setTimezone($timezone); 1307b269830cSAndreas Boehler $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 1308b269830cSAndreas Boehler if($params['allday'] != '1') 1309b269830cSAndreas Boehler $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 1310cb71a62aSAndreas Boehler 13114ecb526cSAndreas Boehler // Setup DTEND 1312b269830cSAndreas Boehler $dtEnd = new \DateTime(); 1313a25c89eaSAndreas Boehler $dtEnd->setTimezone($timezone); 1314b269830cSAndreas Boehler $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 1315b269830cSAndreas Boehler if($params['allday'] != '1') 1316b269830cSAndreas Boehler $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 1317cb71a62aSAndreas Boehler 1318b269830cSAndreas Boehler // According to the VCal spec, we need to add a whole day here 1319b269830cSAndreas Boehler if($params['allday'] == '1') 1320b269830cSAndreas Boehler $dtEnd->add(new \DateInterval('P1D')); 1321b269830cSAndreas Boehler $vevent->remove('DTSTART'); 1322b269830cSAndreas Boehler $vevent->remove('DTEND'); 1323b269830cSAndreas Boehler $dtStartEv = $vevent->add('DTSTART', $dtStart); 1324b269830cSAndreas Boehler $dtEndEv = $vevent->add('DTEND', $dtEnd); 1325cb71a62aSAndreas Boehler 1326cb71a62aSAndreas Boehler // Remove the time for allday events 1327b269830cSAndreas Boehler if($params['allday'] == '1') 1328b269830cSAndreas Boehler { 1329b269830cSAndreas Boehler $dtStartEv['VALUE'] = 'DATE'; 1330b269830cSAndreas Boehler $dtEndEv['VALUE'] = 'DATE'; 1331b269830cSAndreas Boehler } 1332a1a3b679SAndreas Boehler $eventStr = $vcal->serialize(); 1333809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1334809cb0faSAndreas Boehler { 1335809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1336809cb0faSAndreas Boehler return $wdc->editCalendarEntry($connectionId, $uid, $eventStr); 1337809cb0faSAndreas Boehler } 1338809cb0faSAndreas Boehler else 1339809cb0faSAndreas Boehler { 13405f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 13415f2c3e2dSAndreas Boehler if(!$sqlite) 13425f2c3e2dSAndreas Boehler return false; 1343809cb0faSAndreas Boehler $now = new DateTime(); 1344cb71a62aSAndreas Boehler // Actually write to the database 134551f4febbSAndreas Boehler $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ". 134651f4febbSAndreas Boehler "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?"; 13475f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(), 134851f4febbSAndreas Boehler $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid); 134955a741c0SAndreas Boehler if($res !== false) 135055a741c0SAndreas Boehler { 135155a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'modified'); 1352a1a3b679SAndreas Boehler return true; 1353a1a3b679SAndreas Boehler } 1354809cb0faSAndreas Boehler } 135555a741c0SAndreas Boehler return false; 135655a741c0SAndreas Boehler } 1357a1a3b679SAndreas Boehler 1358cb71a62aSAndreas Boehler /** 1359d5703f5aSAndreas Boehler * Delete an event from a calendar by calendar ID and URI 1360d5703f5aSAndreas Boehler * 1361d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1362d5703f5aSAndreas Boehler * @param string $uri The object's URI 1363d5703f5aSAndreas Boehler * 1364d5703f5aSAndreas Boehler * @return true 1365d5703f5aSAndreas Boehler */ 1366d5703f5aSAndreas Boehler public function deleteCalendarEntryForCalendarByUri($calid, $uri) 1367d5703f5aSAndreas Boehler { 13685f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 13695f2c3e2dSAndreas Boehler if(!$sqlite) 13705f2c3e2dSAndreas Boehler return false; 1371d5703f5aSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?"; 13725f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $calid, $uri); 1373d5703f5aSAndreas Boehler if($res !== false) 1374d5703f5aSAndreas Boehler { 1375d5703f5aSAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 1376d5703f5aSAndreas Boehler } 1377d5703f5aSAndreas Boehler return true; 1378d5703f5aSAndreas Boehler } 1379d5703f5aSAndreas Boehler 1380d5703f5aSAndreas Boehler /** 1381cb71a62aSAndreas Boehler * Delete a calendar entry for a given page. Actually, the event is removed 1382cb71a62aSAndreas Boehler * based on the entry's UID, so that page ID is no used. 1383cb71a62aSAndreas Boehler * 1384cb71a62aSAndreas Boehler * @param string $id The page's ID (unused) 1385cb71a62aSAndreas Boehler * @param array $params The parameter array to work with 1386cb71a62aSAndreas Boehler * 1387cb71a62aSAndreas Boehler * @return boolean True 1388cb71a62aSAndreas Boehler */ 1389a1a3b679SAndreas Boehler public function deleteCalendarEntryForPage($id, $params) 1390a1a3b679SAndreas Boehler { 1391a1a3b679SAndreas Boehler $uid = $params['uid']; 1392809cb0faSAndreas Boehler if(strpos($id, 'webdav://') === 0) 1393809cb0faSAndreas Boehler { 1394809cb0faSAndreas Boehler $wdc =& plugin_load('helper', 'webdavclient'); 1395809cb0faSAndreas Boehler if(is_null($wdc)) 1396809cb0faSAndreas Boehler return false; 1397809cb0faSAndreas Boehler $connectionId = str_replace('webdav://', '', $id); 1398809cb0faSAndreas Boehler $result = $wdc->deleteCalendarEntry($connectionId, $uid); 1399809cb0faSAndreas Boehler return $result; 1400809cb0faSAndreas Boehler } 14015f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14025f2c3e2dSAndreas Boehler if(!$sqlite) 14035f2c3e2dSAndreas Boehler return false; 140455a741c0SAndreas Boehler $event = $this->getEventWithUid($uid); 14052c14b82bSAndreas Boehler $calid = $event['calendarid']; 140655a741c0SAndreas Boehler $uri = $event['uri']; 140751f4febbSAndreas Boehler $query = "DELETE FROM calendarobjects WHERE uid = ?"; 14085f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uid); 140955a741c0SAndreas Boehler if($res !== false) 141055a741c0SAndreas Boehler { 141155a741c0SAndreas Boehler $this->updateSyncTokenLog($calid, $uri, 'deleted'); 141255a741c0SAndreas Boehler } 1413a1a3b679SAndreas Boehler return true; 1414a1a3b679SAndreas Boehler } 1415a1a3b679SAndreas Boehler 1416cb71a62aSAndreas Boehler /** 1417cb71a62aSAndreas Boehler * Retrieve the current sync token for a calendar 1418cb71a62aSAndreas Boehler * 1419cb71a62aSAndreas Boehler * @param string $calid The calendar id 1420cb71a62aSAndreas Boehler * 1421cb71a62aSAndreas Boehler * @return mixed The synctoken or false 1422cb71a62aSAndreas Boehler */ 142355a741c0SAndreas Boehler public function getSyncTokenForCalendar($calid) 142455a741c0SAndreas Boehler { 1425b269830cSAndreas Boehler $row = $this->getCalendarSettings($calid); 142655a741c0SAndreas Boehler if(isset($row['synctoken'])) 142755a741c0SAndreas Boehler return $row['synctoken']; 142855a741c0SAndreas Boehler return false; 142955a741c0SAndreas Boehler } 143055a741c0SAndreas Boehler 1431cb71a62aSAndreas Boehler /** 1432cb71a62aSAndreas Boehler * Helper function to convert the operation name to 1433cb71a62aSAndreas Boehler * an operation code as stored in the database 1434cb71a62aSAndreas Boehler * 1435cb71a62aSAndreas Boehler * @param string $operationName The operation name 1436cb71a62aSAndreas Boehler * 1437cb71a62aSAndreas Boehler * @return mixed The operation code or false 1438cb71a62aSAndreas Boehler */ 143955a741c0SAndreas Boehler public function operationNameToOperation($operationName) 144055a741c0SAndreas Boehler { 144155a741c0SAndreas Boehler switch($operationName) 144255a741c0SAndreas Boehler { 144355a741c0SAndreas Boehler case 'added': 144455a741c0SAndreas Boehler return 1; 144555a741c0SAndreas Boehler break; 144655a741c0SAndreas Boehler case 'modified': 144755a741c0SAndreas Boehler return 2; 144855a741c0SAndreas Boehler break; 144955a741c0SAndreas Boehler case 'deleted': 145055a741c0SAndreas Boehler return 3; 145155a741c0SAndreas Boehler break; 145255a741c0SAndreas Boehler } 145355a741c0SAndreas Boehler return false; 145455a741c0SAndreas Boehler } 145555a741c0SAndreas Boehler 1456cb71a62aSAndreas Boehler /** 1457cb71a62aSAndreas Boehler * Update the sync token log based on the calendar id and the 1458cb71a62aSAndreas Boehler * operation that was performed. 1459cb71a62aSAndreas Boehler * 1460cb71a62aSAndreas Boehler * @param string $calid The calendar ID that was modified 1461cb71a62aSAndreas Boehler * @param string $uri The calendar URI that was modified 1462cb71a62aSAndreas Boehler * @param string $operation The operation that was performed 1463cb71a62aSAndreas Boehler * 1464cb71a62aSAndreas Boehler * @return boolean True on success, otherwise false 1465cb71a62aSAndreas Boehler */ 146655a741c0SAndreas Boehler private function updateSyncTokenLog($calid, $uri, $operation) 146755a741c0SAndreas Boehler { 146855a741c0SAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 146955a741c0SAndreas Boehler $operationCode = $this->operationNameToOperation($operation); 147055a741c0SAndreas Boehler if(($operationCode === false) || ($currentToken === false)) 147155a741c0SAndreas Boehler return false; 147255a741c0SAndreas Boehler $values = array($uri, 147355a741c0SAndreas Boehler $currentToken, 147455a741c0SAndreas Boehler $calid, 147555a741c0SAndreas Boehler $operationCode 147655a741c0SAndreas Boehler ); 14775f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 14785f2c3e2dSAndreas Boehler if(!$sqlite) 14795f2c3e2dSAndreas Boehler return false; 148051f4febbSAndreas Boehler $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)"; 14815f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $uri, $currentToken, $calid, $operationCode); 148255a741c0SAndreas Boehler if($res === false) 148355a741c0SAndreas Boehler return false; 148455a741c0SAndreas Boehler $currentToken++; 148551f4febbSAndreas Boehler $query = "UPDATE calendars SET synctoken = ? WHERE id = ?"; 14865f2c3e2dSAndreas Boehler $res = $sqlite->query($query, $currentToken, $calid); 148755a741c0SAndreas Boehler return ($res !== false); 148855a741c0SAndreas Boehler } 148955a741c0SAndreas Boehler 1490cb71a62aSAndreas Boehler /** 1491cb71a62aSAndreas Boehler * Return the sync URL for a given Page, i.e. a calendar 1492cb71a62aSAndreas Boehler * 1493cb71a62aSAndreas Boehler * @param string $id The page's ID 1494cb71a62aSAndreas Boehler * @param string $user (optional) The user's ID 1495cb71a62aSAndreas Boehler * 1496cb71a62aSAndreas Boehler * @return mixed The sync url or false 1497cb71a62aSAndreas Boehler */ 1498b269830cSAndreas Boehler public function getSyncUrlForPage($id, $user = null) 1499b269830cSAndreas Boehler { 1500415f5c8fSscottleechua if(is_null($user)) 150134a47953SAndreas Boehler { 150234a47953SAndreas Boehler if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) 150334a47953SAndreas Boehler { 1504415f5c8fSscottleechua $user = $_SERVER['REMOTE_USER']; 150534a47953SAndreas Boehler } 150634a47953SAndreas Boehler else 150734a47953SAndreas Boehler { 150834a47953SAndreas Boehler return false; 150934a47953SAndreas Boehler } 151034a47953SAndreas Boehler } 1511b269830cSAndreas Boehler 1512b269830cSAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1513b269830cSAndreas Boehler if($calid === false) 1514b269830cSAndreas Boehler return false; 1515b269830cSAndreas Boehler 1516b269830cSAndreas Boehler $calsettings = $this->getCalendarSettings($calid); 1517b269830cSAndreas Boehler if(!isset($calsettings['uri'])) 1518b269830cSAndreas Boehler return false; 1519b269830cSAndreas Boehler 1520b269830cSAndreas Boehler $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 1521b269830cSAndreas Boehler return $syncurl; 1522b269830cSAndreas Boehler } 1523b269830cSAndreas Boehler 1524cb71a62aSAndreas Boehler /** 1525cb71a62aSAndreas Boehler * Return the private calendar's URL for a given page 1526cb71a62aSAndreas Boehler * 1527cb71a62aSAndreas Boehler * @param string $id the page ID 1528cb71a62aSAndreas Boehler * 1529cb71a62aSAndreas Boehler * @return mixed The private URL or false 1530cb71a62aSAndreas Boehler */ 1531f69bb449SAndreas Boehler public function getPrivateURLForPage($id) 1532f69bb449SAndreas Boehler { 1533a8a4aa52Sscottleechua // Check if this is an aggregated calendar (has multiple calendar pages) 1534a8a4aa52Sscottleechua $calendarPages = $this->getCalendarPagesByMeta($id); 1535a8a4aa52Sscottleechua 1536a8a4aa52Sscottleechua if($calendarPages !== false && count($calendarPages) > 1) 1537a8a4aa52Sscottleechua { 1538a8a4aa52Sscottleechua // This is an aggregated calendar - create a special private URL 1539a8a4aa52Sscottleechua return $this->getPrivateURLForAggregatedCalendar($id, $calendarPages); 1540a8a4aa52Sscottleechua } 1541a8a4aa52Sscottleechua 1542a8a4aa52Sscottleechua // Single calendar - use the original logic 1543f69bb449SAndreas Boehler $calid = $this->getCalendarIdForPage($id); 1544f69bb449SAndreas Boehler if($calid === false) 1545f69bb449SAndreas Boehler return false; 1546f69bb449SAndreas Boehler 1547f69bb449SAndreas Boehler return $this->getPrivateURLForCalendar($calid); 1548f69bb449SAndreas Boehler } 1549f69bb449SAndreas Boehler 1550cb71a62aSAndreas Boehler /** 1551cb71a62aSAndreas Boehler * Return the private calendar's URL for a given calendar ID 1552cb71a62aSAndreas Boehler * 1553cb71a62aSAndreas Boehler * @param string $calid The calendar's ID 1554cb71a62aSAndreas Boehler * 1555cb71a62aSAndreas Boehler * @return mixed The private URL or false 1556cb71a62aSAndreas Boehler */ 1557f69bb449SAndreas Boehler public function getPrivateURLForCalendar($calid) 1558f69bb449SAndreas Boehler { 1559185e2535SAndreas Boehler if(isset($this->cachedValues['privateurl'][$calid])) 1560185e2535SAndreas Boehler return $this->cachedValues['privateurl'][$calid]; 15615f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 15625f2c3e2dSAndreas Boehler if(!$sqlite) 15635f2c3e2dSAndreas Boehler return false; 156451f4febbSAndreas Boehler $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 156561fec75eSscottleechua $row = $sqlite->queryRecord($query, [$calid]); 1566f69bb449SAndreas Boehler if(!isset($row['url'])) 1567f69bb449SAndreas Boehler { 156802eea9b8SAndreas Boehler $url = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics'; 156951f4febbSAndreas Boehler $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 157061fec75eSscottleechua $sqlite->query($query, [$url, $calid]); 1571f69bb449SAndreas Boehler } 1572f69bb449SAndreas Boehler else 1573f69bb449SAndreas Boehler { 1574f69bb449SAndreas Boehler $url = $row['url']; 1575f69bb449SAndreas Boehler } 1576185e2535SAndreas Boehler 1577185e2535SAndreas Boehler $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1578185e2535SAndreas Boehler $this->cachedValues['privateurl'][$calid] = $url; 1579185e2535SAndreas Boehler return $url; 1580f69bb449SAndreas Boehler } 1581f69bb449SAndreas Boehler 1582cb71a62aSAndreas Boehler /** 1583a8a4aa52Sscottleechua * Return the private calendar's URL for an aggregated calendar 1584a8a4aa52Sscottleechua * 1585a8a4aa52Sscottleechua * @param string $id the page ID 1586a8a4aa52Sscottleechua * @param array $calendarPages the calendar pages in the aggregation 1587a8a4aa52Sscottleechua * 1588a8a4aa52Sscottleechua * @return mixed The private URL or false 1589a8a4aa52Sscottleechua */ 1590a8a4aa52Sscottleechua public function getPrivateURLForAggregatedCalendar($id, $calendarPages) 1591a8a4aa52Sscottleechua { 1592a8a4aa52Sscottleechua $aggregateId = 'aggregated-' . md5($id . serialize($calendarPages)); 1593a8a4aa52Sscottleechua 1594a8a4aa52Sscottleechua if(isset($this->cachedValues['privateurl'][$aggregateId])) 1595a8a4aa52Sscottleechua return $this->cachedValues['privateurl'][$aggregateId]; 1596a8a4aa52Sscottleechua 1597a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1598a8a4aa52Sscottleechua if(!$sqlite) 1599a8a4aa52Sscottleechua return false; 1600a8a4aa52Sscottleechua 1601a8a4aa52Sscottleechua $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?"; 160261fec75eSscottleechua $row = $sqlite->queryRecord($query, [$aggregateId]); 1603a8a4aa52Sscottleechua 1604a8a4aa52Sscottleechua if(!isset($row['url'])) 1605a8a4aa52Sscottleechua { 1606a8a4aa52Sscottleechua $url = 'dokuwiki-aggregated-' . bin2hex(random_bytes(16)) . '.ics'; 1607a8a4aa52Sscottleechua $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)"; 160861fec75eSscottleechua $sqlite->query($query, [$url, $aggregateId]); 1609a8a4aa52Sscottleechua 1610a8a4aa52Sscottleechua $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES(?, ?)"; 161161fec75eSscottleechua $sqlite->query($query, [$id, $aggregateId]); 1612a8a4aa52Sscottleechua } 1613a8a4aa52Sscottleechua else 1614a8a4aa52Sscottleechua { 1615a8a4aa52Sscottleechua $url = $row['url']; 1616a8a4aa52Sscottleechua } 1617a8a4aa52Sscottleechua 1618a8a4aa52Sscottleechua $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url; 1619a8a4aa52Sscottleechua $this->cachedValues['privateurl'][$aggregateId] = $url; 1620a8a4aa52Sscottleechua return $url; 1621a8a4aa52Sscottleechua } 1622a8a4aa52Sscottleechua 1623a8a4aa52Sscottleechua /** 1624cb71a62aSAndreas Boehler * Retrieve the calendar ID for a given private calendar URL 1625cb71a62aSAndreas Boehler * 1626cb71a62aSAndreas Boehler * @param string $url The private URL 1627cb71a62aSAndreas Boehler * 1628cb71a62aSAndreas Boehler * @return mixed The calendar ID or false 1629cb71a62aSAndreas Boehler */ 1630f69bb449SAndreas Boehler public function getCalendarForPrivateURL($url) 1631f69bb449SAndreas Boehler { 16325f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 16335f2c3e2dSAndreas Boehler if(!$sqlite) 16345f2c3e2dSAndreas Boehler return false; 163551f4febbSAndreas Boehler $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 163661fec75eSscottleechua $row = $sqlite->queryRecord($query, [$url]); 1637f69bb449SAndreas Boehler if(!isset($row['calid'])) 1638f69bb449SAndreas Boehler return false; 1639f69bb449SAndreas Boehler return $row['calid']; 1640f69bb449SAndreas Boehler } 1641f69bb449SAndreas Boehler 1642cb71a62aSAndreas Boehler /** 1643cb71a62aSAndreas Boehler * Return a given calendar as ICS feed, i.e. all events in one ICS file. 1644cb71a62aSAndreas Boehler * 16457e0b8590SAndreas Boehler * @param string $calid The calendar ID to retrieve 1646cb71a62aSAndreas Boehler * 1647cb71a62aSAndreas Boehler * @return mixed The calendar events as string or false 1648cb71a62aSAndreas Boehler */ 1649f69bb449SAndreas Boehler public function getCalendarAsICSFeed($calid) 1650f69bb449SAndreas Boehler { 1651f69bb449SAndreas Boehler $calSettings = $this->getCalendarSettings($calid); 1652f69bb449SAndreas Boehler if($calSettings === false) 1653f69bb449SAndreas Boehler return false; 1654f69bb449SAndreas Boehler $events = $this->getAllCalendarEvents($calid); 1655f69bb449SAndreas Boehler if($events === false) 1656f69bb449SAndreas Boehler return false; 1657f69bb449SAndreas Boehler 16587e0b8590SAndreas Boehler // Load SabreDAV 16597e0b8590SAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 16607e0b8590SAndreas Boehler $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 16617e0b8590SAndreas Boehler $out .= $calSettings['displayname']."\r\n"; 1662f69bb449SAndreas Boehler foreach($events as $event) 1663f69bb449SAndreas Boehler { 16647e0b8590SAndreas Boehler $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 16657e0b8590SAndreas Boehler $evt = $vcal->VEVENT; 16667e0b8590SAndreas Boehler $out .= $evt->serialize(); 1667f69bb449SAndreas Boehler } 16687e0b8590SAndreas Boehler $out .= "END:VCALENDAR\r\n"; 1669f69bb449SAndreas Boehler return $out; 1670f69bb449SAndreas Boehler } 1671f69bb449SAndreas Boehler 16727c7c6b0bSAndreas Boehler /** 1673a8a4aa52Sscottleechua * Return an aggregated calendar as ICS feed, combining multiple calendars. 1674a8a4aa52Sscottleechua * 1675a8a4aa52Sscottleechua * @param string $icsFile The ICS file name for the aggregated calendar 1676a8a4aa52Sscottleechua * 1677a8a4aa52Sscottleechua * @return mixed The combined calendar events as string or false 1678a8a4aa52Sscottleechua */ 1679a8a4aa52Sscottleechua public function getAggregatedCalendarAsICSFeed($icsFile) 1680a8a4aa52Sscottleechua { 1681a8a4aa52Sscottleechua $sqlite = $this->getDB(); 1682a8a4aa52Sscottleechua if(!$sqlite) 1683a8a4aa52Sscottleechua return false; 1684a8a4aa52Sscottleechua 1685a8a4aa52Sscottleechua // Find the aggregated calendar ID from the URL 1686a8a4aa52Sscottleechua $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?"; 1687*6a6f477eSscottleechua $row = $sqlite->queryRecord($query, [$icsFile]); 1688a8a4aa52Sscottleechua 1689a8a4aa52Sscottleechua if(!isset($row['calid'])) 1690a8a4aa52Sscottleechua return false; 1691a8a4aa52Sscottleechua 1692a8a4aa52Sscottleechua $aggregateId = $row['calid']; 1693a8a4aa52Sscottleechua 1694a8a4aa52Sscottleechua // Get the page ID for this aggregated calendar 1695a8a4aa52Sscottleechua $query = "SELECT page FROM pagetocalendarmapping WHERE calid = ?"; 1696*6a6f477eSscottleechua $row = $sqlite->queryRecord($query, [$aggregateId]); 1697a8a4aa52Sscottleechua 1698a8a4aa52Sscottleechua if(!isset($row['page'])) 1699a8a4aa52Sscottleechua return false; 1700a8a4aa52Sscottleechua 1701a8a4aa52Sscottleechua $pageId = $row['page']; 1702a8a4aa52Sscottleechua 1703a8a4aa52Sscottleechua // Get the calendar pages for this aggregated calendar 1704a8a4aa52Sscottleechua $calendarPages = $this->getCalendarPagesByMeta($pageId); 1705a8a4aa52Sscottleechua if($calendarPages === false || count($calendarPages) <= 1) 1706a8a4aa52Sscottleechua return false; 1707a8a4aa52Sscottleechua 1708a8a4aa52Sscottleechua // Load SabreDAV 1709a8a4aa52Sscottleechua require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1710a8a4aa52Sscottleechua 1711a8a4aa52Sscottleechua // Start building the combined ICS 1712a8a4aa52Sscottleechua $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:"; 1713a8a4aa52Sscottleechua $out .= "Aggregated Calendar\r\n"; 1714a8a4aa52Sscottleechua 1715a8a4aa52Sscottleechua // Combine events from all calendars 1716a8a4aa52Sscottleechua foreach($calendarPages as $calPage => $color) 1717a8a4aa52Sscottleechua { 1718a8a4aa52Sscottleechua $calid = $this->getCalendarIdForPage($calPage); 1719a8a4aa52Sscottleechua if($calid === false) 1720a8a4aa52Sscottleechua continue; 1721a8a4aa52Sscottleechua 1722a8a4aa52Sscottleechua $events = $this->getAllCalendarEvents($calid); 1723a8a4aa52Sscottleechua if($events === false) 1724a8a4aa52Sscottleechua continue; 1725a8a4aa52Sscottleechua 1726a8a4aa52Sscottleechua foreach($events as $event) 1727a8a4aa52Sscottleechua { 1728a8a4aa52Sscottleechua $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 1729a8a4aa52Sscottleechua $evt = $vcal->VEVENT; 1730a8a4aa52Sscottleechua $out .= $evt->serialize(); 1731a8a4aa52Sscottleechua } 1732a8a4aa52Sscottleechua } 1733a8a4aa52Sscottleechua 1734a8a4aa52Sscottleechua $out .= "END:VCALENDAR\r\n"; 1735a8a4aa52Sscottleechua return $out; 1736a8a4aa52Sscottleechua } 1737a8a4aa52Sscottleechua 1738a8a4aa52Sscottleechua /** 17397c7c6b0bSAndreas Boehler * Retrieve a configuration option for the plugin 17407c7c6b0bSAndreas Boehler * 17417c7c6b0bSAndreas Boehler * @param string $key The key to query 174221d04f73SAndreas Boehler * @return mixed The option set, null if not found 17437c7c6b0bSAndreas Boehler */ 17447c7c6b0bSAndreas Boehler public function getConfig($key) 17457c7c6b0bSAndreas Boehler { 17467c7c6b0bSAndreas Boehler return $this->getConf($key); 17477c7c6b0bSAndreas Boehler } 17487c7c6b0bSAndreas Boehler 1749d5703f5aSAndreas Boehler /** 1750d5703f5aSAndreas Boehler * Parses some information from calendar objects, used for optimized 1751d5703f5aSAndreas Boehler * calendar-queries. Taken nearly unmodified from Sabre's PDO backend 1752d5703f5aSAndreas Boehler * 1753d5703f5aSAndreas Boehler * Returns an array with the following keys: 1754d5703f5aSAndreas Boehler * * etag - An md5 checksum of the object without the quotes. 1755d5703f5aSAndreas Boehler * * size - Size of the object in bytes 1756d5703f5aSAndreas Boehler * * componentType - VEVENT, VTODO or VJOURNAL 1757d5703f5aSAndreas Boehler * * firstOccurence 1758d5703f5aSAndreas Boehler * * lastOccurence 1759d5703f5aSAndreas Boehler * * uid - value of the UID property 1760d5703f5aSAndreas Boehler * 1761d5703f5aSAndreas Boehler * @param string $calendarData 1762d5703f5aSAndreas Boehler * @return array 1763d5703f5aSAndreas Boehler */ 1764d5703f5aSAndreas Boehler protected function getDenormalizedData($calendarData) 1765d5703f5aSAndreas Boehler { 1766d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1767d5703f5aSAndreas Boehler 1768d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($calendarData); 1769d5703f5aSAndreas Boehler $componentType = null; 1770d5703f5aSAndreas Boehler $component = null; 1771d5703f5aSAndreas Boehler $firstOccurence = null; 1772d5703f5aSAndreas Boehler $lastOccurence = null; 1773d5703f5aSAndreas Boehler $uid = null; 1774d5703f5aSAndreas Boehler foreach ($vObject->getComponents() as $component) 1775d5703f5aSAndreas Boehler { 1776d5703f5aSAndreas Boehler if ($component->name !== 'VTIMEZONE') 1777d5703f5aSAndreas Boehler { 1778d5703f5aSAndreas Boehler $componentType = $component->name; 1779d5703f5aSAndreas Boehler $uid = (string)$component->UID; 1780d5703f5aSAndreas Boehler break; 1781d5703f5aSAndreas Boehler } 1782d5703f5aSAndreas Boehler } 1783d5703f5aSAndreas Boehler if (!$componentType) 1784d5703f5aSAndreas Boehler { 1785d5703f5aSAndreas Boehler return false; 1786d5703f5aSAndreas Boehler } 1787d5703f5aSAndreas Boehler if ($componentType === 'VEVENT') 1788d5703f5aSAndreas Boehler { 1789d5703f5aSAndreas Boehler $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); 1790d5703f5aSAndreas Boehler // Finding the last occurence is a bit harder 1791d5703f5aSAndreas Boehler if (!isset($component->RRULE)) 1792d5703f5aSAndreas Boehler { 1793d5703f5aSAndreas Boehler if (isset($component->DTEND)) 1794d5703f5aSAndreas Boehler { 1795d5703f5aSAndreas Boehler $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp(); 1796d5703f5aSAndreas Boehler } 1797d5703f5aSAndreas Boehler elseif (isset($component->DURATION)) 1798d5703f5aSAndreas Boehler { 1799d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1800d5703f5aSAndreas Boehler $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue())); 1801d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1802d5703f5aSAndreas Boehler } 1803d5703f5aSAndreas Boehler elseif (!$component->DTSTART->hasTime()) 1804d5703f5aSAndreas Boehler { 1805d5703f5aSAndreas Boehler $endDate = clone $component->DTSTART->getDateTime(); 1806d5703f5aSAndreas Boehler $endDate->modify('+1 day'); 1807d5703f5aSAndreas Boehler $lastOccurence = $endDate->getTimeStamp(); 1808d5703f5aSAndreas Boehler } 1809d5703f5aSAndreas Boehler else 1810d5703f5aSAndreas Boehler { 1811d5703f5aSAndreas Boehler $lastOccurence = $firstOccurence; 1812d5703f5aSAndreas Boehler } 1813d5703f5aSAndreas Boehler } 1814d5703f5aSAndreas Boehler else 1815d5703f5aSAndreas Boehler { 1816d5703f5aSAndreas Boehler $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID); 1817d5703f5aSAndreas Boehler $maxDate = new \DateTime('2038-01-01'); 1818d5703f5aSAndreas Boehler if ($it->isInfinite()) 1819d5703f5aSAndreas Boehler { 1820d5703f5aSAndreas Boehler $lastOccurence = $maxDate->getTimeStamp(); 1821d5703f5aSAndreas Boehler } 1822d5703f5aSAndreas Boehler else 1823d5703f5aSAndreas Boehler { 1824d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1825d5703f5aSAndreas Boehler while ($it->valid() && $end < $maxDate) 1826d5703f5aSAndreas Boehler { 1827d5703f5aSAndreas Boehler $end = $it->getDtEnd(); 1828d5703f5aSAndreas Boehler $it->next(); 1829d5703f5aSAndreas Boehler } 1830d5703f5aSAndreas Boehler $lastOccurence = $end->getTimeStamp(); 1831d5703f5aSAndreas Boehler } 1832d5703f5aSAndreas Boehler } 1833d5703f5aSAndreas Boehler } 1834d5703f5aSAndreas Boehler 1835d5703f5aSAndreas Boehler return array( 1836d5703f5aSAndreas Boehler 'etag' => md5($calendarData), 1837d5703f5aSAndreas Boehler 'size' => strlen($calendarData), 1838d5703f5aSAndreas Boehler 'componentType' => $componentType, 1839d5703f5aSAndreas Boehler 'firstOccurence' => $firstOccurence, 1840d5703f5aSAndreas Boehler 'lastOccurence' => $lastOccurence, 1841d5703f5aSAndreas Boehler 'uid' => $uid, 1842d5703f5aSAndreas Boehler ); 1843d5703f5aSAndreas Boehler 1844d5703f5aSAndreas Boehler } 1845d5703f5aSAndreas Boehler 1846d5703f5aSAndreas Boehler /** 1847d5703f5aSAndreas Boehler * Query a calendar by ID and taking several filters into account. 1848d5703f5aSAndreas Boehler * This is heavily based on Sabre's PDO backend. 1849d5703f5aSAndreas Boehler * 1850d5703f5aSAndreas Boehler * @param int $calendarId The calendar's ID 1851d5703f5aSAndreas Boehler * @param array $filters The filter array to apply 1852d5703f5aSAndreas Boehler * 1853d5703f5aSAndreas Boehler * @return mixed The result 1854d5703f5aSAndreas Boehler */ 1855d5703f5aSAndreas Boehler public function calendarQuery($calendarId, $filters) 1856d5703f5aSAndreas Boehler { 1857c42afaebSscottleechua \dokuwiki\Logger::debug('DAVCAL', 'Calendar query executed', __FILE__, __LINE__); 1858d5703f5aSAndreas Boehler $componentType = null; 1859d5703f5aSAndreas Boehler $requirePostFilter = true; 1860d5703f5aSAndreas Boehler $timeRange = null; 18615f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 18625f2c3e2dSAndreas Boehler if(!$sqlite) 18635f2c3e2dSAndreas Boehler return false; 1864d5703f5aSAndreas Boehler 1865d5703f5aSAndreas Boehler // if no filters were specified, we don't need to filter after a query 1866d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters']) 1867d5703f5aSAndreas Boehler { 1868d5703f5aSAndreas Boehler $requirePostFilter = false; 1869d5703f5aSAndreas Boehler } 1870d5703f5aSAndreas Boehler 1871d5703f5aSAndreas Boehler // Figuring out if there's a component filter 1872d5703f5aSAndreas Boehler if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) 1873d5703f5aSAndreas Boehler { 1874d5703f5aSAndreas Boehler $componentType = $filters['comp-filters'][0]['name']; 1875d5703f5aSAndreas Boehler 1876d5703f5aSAndreas Boehler // Checking if we need post-filters 1877d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) 1878d5703f5aSAndreas Boehler { 1879d5703f5aSAndreas Boehler $requirePostFilter = false; 1880d5703f5aSAndreas Boehler } 1881d5703f5aSAndreas Boehler // There was a time-range filter 1882d5703f5aSAndreas Boehler if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) 1883d5703f5aSAndreas Boehler { 1884d5703f5aSAndreas Boehler $timeRange = $filters['comp-filters'][0]['time-range']; 1885d5703f5aSAndreas Boehler 1886d5703f5aSAndreas Boehler // If start time OR the end time is not specified, we can do a 1887d5703f5aSAndreas Boehler // 100% accurate mysql query. 1888d5703f5aSAndreas Boehler if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) 1889d5703f5aSAndreas Boehler { 1890d5703f5aSAndreas Boehler $requirePostFilter = false; 1891d5703f5aSAndreas Boehler } 1892d5703f5aSAndreas Boehler } 1893d5703f5aSAndreas Boehler 1894d5703f5aSAndreas Boehler } 1895d5703f5aSAndreas Boehler 1896d5703f5aSAndreas Boehler if ($requirePostFilter) 1897d5703f5aSAndreas Boehler { 1898d5703f5aSAndreas Boehler $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?"; 1899d5703f5aSAndreas Boehler } 1900d5703f5aSAndreas Boehler else 1901d5703f5aSAndreas Boehler { 1902d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 1903d5703f5aSAndreas Boehler } 1904d5703f5aSAndreas Boehler 1905d5703f5aSAndreas Boehler $values = array( 1906d5703f5aSAndreas Boehler $calendarId 1907d5703f5aSAndreas Boehler ); 1908d5703f5aSAndreas Boehler 1909d5703f5aSAndreas Boehler if ($componentType) 1910d5703f5aSAndreas Boehler { 1911d5703f5aSAndreas Boehler $query .= " AND componenttype = ?"; 1912d5703f5aSAndreas Boehler $values[] = $componentType; 1913d5703f5aSAndreas Boehler } 1914d5703f5aSAndreas Boehler 1915d5703f5aSAndreas Boehler if ($timeRange && $timeRange['start']) 1916d5703f5aSAndreas Boehler { 1917d5703f5aSAndreas Boehler $query .= " AND lastoccurence > ?"; 1918d5703f5aSAndreas Boehler $values[] = $timeRange['start']->getTimeStamp(); 1919d5703f5aSAndreas Boehler } 1920d5703f5aSAndreas Boehler if ($timeRange && $timeRange['end']) 1921d5703f5aSAndreas Boehler { 1922d5703f5aSAndreas Boehler $query .= " AND firstoccurence < ?"; 1923d5703f5aSAndreas Boehler $values[] = $timeRange['end']->getTimeStamp(); 1924d5703f5aSAndreas Boehler } 1925d5703f5aSAndreas Boehler 1926*6a6f477eSscottleechua $arr = $sqlite->queryAll($query, $values); 1927d5703f5aSAndreas Boehler 1928d5703f5aSAndreas Boehler $result = array(); 1929d5703f5aSAndreas Boehler foreach($arr as $row) 1930d5703f5aSAndreas Boehler { 1931d5703f5aSAndreas Boehler if ($requirePostFilter) 1932d5703f5aSAndreas Boehler { 1933d5703f5aSAndreas Boehler if (!$this->validateFilterForObject($row, $filters)) 1934d5703f5aSAndreas Boehler { 1935d5703f5aSAndreas Boehler continue; 1936d5703f5aSAndreas Boehler } 1937d5703f5aSAndreas Boehler } 1938d5703f5aSAndreas Boehler $result[] = $row['uri']; 1939d5703f5aSAndreas Boehler 1940d5703f5aSAndreas Boehler } 1941d5703f5aSAndreas Boehler 1942d5703f5aSAndreas Boehler return $result; 1943d5703f5aSAndreas Boehler } 1944d5703f5aSAndreas Boehler 1945d5703f5aSAndreas Boehler /** 1946d5703f5aSAndreas Boehler * This method validates if a filter (as passed to calendarQuery) matches 1947d5703f5aSAndreas Boehler * the given object. Taken from Sabre's PDO backend 1948d5703f5aSAndreas Boehler * 1949d5703f5aSAndreas Boehler * @param array $object 1950d5703f5aSAndreas Boehler * @param array $filters 1951d5703f5aSAndreas Boehler * @return bool 1952d5703f5aSAndreas Boehler */ 1953d5703f5aSAndreas Boehler protected function validateFilterForObject($object, $filters) 1954d5703f5aSAndreas Boehler { 1955d5703f5aSAndreas Boehler require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php'); 1956d5703f5aSAndreas Boehler // Unfortunately, setting the 'calendardata' here is optional. If 1957d5703f5aSAndreas Boehler // it was excluded, we actually need another call to get this as 1958d5703f5aSAndreas Boehler // well. 1959d5703f5aSAndreas Boehler if (!isset($object['calendardata'])) 1960d5703f5aSAndreas Boehler { 1961d5703f5aSAndreas Boehler $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']); 1962d5703f5aSAndreas Boehler } 1963d5703f5aSAndreas Boehler 1964d5703f5aSAndreas Boehler $vObject = \Sabre\VObject\Reader::read($object['calendardata']); 1965d5703f5aSAndreas Boehler $validator = new \Sabre\CalDAV\CalendarQueryValidator(); 1966d5703f5aSAndreas Boehler 196739787131SAndreas Boehler $res = $validator->validate($vObject, $filters); 196839787131SAndreas Boehler return $res; 1969d5703f5aSAndreas Boehler 1970d5703f5aSAndreas Boehler } 1971d5703f5aSAndreas Boehler 1972d5703f5aSAndreas Boehler /** 1973d5703f5aSAndreas Boehler * Retrieve changes for a given calendar based on the given syncToken. 1974d5703f5aSAndreas Boehler * 1975d5703f5aSAndreas Boehler * @param int $calid The calendar's ID 1976d5703f5aSAndreas Boehler * @param int $syncToken The supplied sync token 1977d5703f5aSAndreas Boehler * @param int $syncLevel The sync level 1978d5703f5aSAndreas Boehler * @param int $limit The limit of changes 1979d5703f5aSAndreas Boehler * 1980d5703f5aSAndreas Boehler * @return array The result 1981d5703f5aSAndreas Boehler */ 1982d5703f5aSAndreas Boehler public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null) 1983d5703f5aSAndreas Boehler { 1984d5703f5aSAndreas Boehler // Current synctoken 1985d5703f5aSAndreas Boehler $currentToken = $this->getSyncTokenForCalendar($calid); 1986d5703f5aSAndreas Boehler 1987d5703f5aSAndreas Boehler if ($currentToken === false) return null; 1988d5703f5aSAndreas Boehler 1989d5703f5aSAndreas Boehler $result = array( 1990d5703f5aSAndreas Boehler 'syncToken' => $currentToken, 1991d5703f5aSAndreas Boehler 'added' => array(), 1992d5703f5aSAndreas Boehler 'modified' => array(), 1993d5703f5aSAndreas Boehler 'deleted' => array(), 1994d5703f5aSAndreas Boehler ); 19955f2c3e2dSAndreas Boehler $sqlite = $this->getDB(); 19965f2c3e2dSAndreas Boehler if(!$sqlite) 19975f2c3e2dSAndreas Boehler return false; 1998d5703f5aSAndreas Boehler 1999d5703f5aSAndreas Boehler if ($syncToken) 2000d5703f5aSAndreas Boehler { 2001d5703f5aSAndreas Boehler 2002d5703f5aSAndreas Boehler $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; 2003d5703f5aSAndreas Boehler if ($limit > 0) $query .= " LIMIT " . (int)$limit; 2004d5703f5aSAndreas Boehler 2005d5703f5aSAndreas Boehler // Fetching all changes 2006*6a6f477eSscottleechua $arr = $sqlite->queryAll($query, array($syncToken, $currentToken, $calid)); 2007d5703f5aSAndreas Boehler $changes = array(); 2008d5703f5aSAndreas Boehler 2009d5703f5aSAndreas Boehler // This loop ensures that any duplicates are overwritten, only the 2010d5703f5aSAndreas Boehler // last change on a node is relevant. 2011d5703f5aSAndreas Boehler foreach($arr as $row) 2012d5703f5aSAndreas Boehler { 2013d5703f5aSAndreas Boehler $changes[$row['uri']] = $row['operation']; 2014d5703f5aSAndreas Boehler } 2015d5703f5aSAndreas Boehler 2016d5703f5aSAndreas Boehler foreach ($changes as $uri => $operation) 2017d5703f5aSAndreas Boehler { 2018d5703f5aSAndreas Boehler switch ($operation) 2019d5703f5aSAndreas Boehler { 2020d5703f5aSAndreas Boehler case 1 : 2021d5703f5aSAndreas Boehler $result['added'][] = $uri; 2022d5703f5aSAndreas Boehler break; 2023d5703f5aSAndreas Boehler case 2 : 2024d5703f5aSAndreas Boehler $result['modified'][] = $uri; 2025d5703f5aSAndreas Boehler break; 2026d5703f5aSAndreas Boehler case 3 : 2027d5703f5aSAndreas Boehler $result['deleted'][] = $uri; 2028d5703f5aSAndreas Boehler break; 2029d5703f5aSAndreas Boehler } 2030d5703f5aSAndreas Boehler 2031d5703f5aSAndreas Boehler } 2032d5703f5aSAndreas Boehler } 2033d5703f5aSAndreas Boehler else 2034d5703f5aSAndreas Boehler { 2035d5703f5aSAndreas Boehler // No synctoken supplied, this is the initial sync. 2036d5703f5aSAndreas Boehler $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?"; 2037*6a6f477eSscottleechua $arr = $sqlite->queryAll($query, array($calid)); 2038d5703f5aSAndreas Boehler 2039d5703f5aSAndreas Boehler $result['added'] = $arr; 2040d5703f5aSAndreas Boehler } 2041d5703f5aSAndreas Boehler return $result; 2042d5703f5aSAndreas Boehler } 2043d5703f5aSAndreas Boehler 2044a1a3b679SAndreas Boehler} 2045