xref: /plugin/davcal/helper.php (revision d49924538291b114574cc8adaa944477a72b17c7)
1a1a3b679SAndreas Boehler<?php
2a1a3b679SAndreas Boehler/**
3cb71a62aSAndreas Boehler  * Helper Class for the DAVCal plugin
4a1a3b679SAndreas Boehler  * This helper does the actual work.
5a1a3b679SAndreas Boehler  *
6a1a3b679SAndreas Boehler  */
7a1a3b679SAndreas Boehler
8a1a3b679SAndreas Boehler// must be run within Dokuwiki
9a1a3b679SAndreas Boehlerif(!defined('DOKU_INC')) die();
10a1a3b679SAndreas Boehler
11a1a3b679SAndreas Boehlerclass helper_plugin_davcal extends DokuWiki_Plugin {
12a1a3b679SAndreas Boehler
13a1a3b679SAndreas Boehler  protected $sqlite = null;
14185e2535SAndreas Boehler  protected $cachedValues = array();
15a1a3b679SAndreas Boehler
16a1a3b679SAndreas Boehler  /**
17cb71a62aSAndreas Boehler    * Constructor to load the configuration and the SQLite plugin
18a1a3b679SAndreas Boehler    */
19a1a3b679SAndreas Boehler  public function helper_plugin_davcal() {
2021d04f73SAndreas Boehler    dbglog('---- DAVCAL helper.php init');
215f2c3e2dSAndreas Boehler  }
225f2c3e2dSAndreas Boehler
235f2c3e2dSAndreas Boehler  /** Establish and initialize the database if not already done
245f2c3e2dSAndreas Boehler   * @return sqlite interface or false
255f2c3e2dSAndreas Boehler   */
265f2c3e2dSAndreas Boehler  private function getDB()
275f2c3e2dSAndreas Boehler  {
285f2c3e2dSAndreas Boehler      if($this->sqlite === null)
295f2c3e2dSAndreas Boehler      {
305f2c3e2dSAndreas Boehler        $this->sqlite = plugin_load('helper', 'sqlite');
31a1a3b679SAndreas Boehler        if(!$this->sqlite)
32a1a3b679SAndreas Boehler        {
3321d04f73SAndreas Boehler            dbglog('This plugin requires the sqlite plugin. Please install it.');
345f2c3e2dSAndreas Boehler            msg('This plugin requires the sqlite plugin. Please install it.', -1);
355f2c3e2dSAndreas Boehler            return false;
36a1a3b679SAndreas Boehler        }
37a1a3b679SAndreas Boehler        if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
38a1a3b679SAndreas Boehler        {
395f2c3e2dSAndreas Boehler            $this->sqlite = null;
4021d04f73SAndreas Boehler            dbglog('Error initialising the SQLite DB for DAVCal');
415f2c3e2dSAndreas Boehler            return false;
42a1a3b679SAndreas Boehler        }
43a1a3b679SAndreas Boehler      }
445f2c3e2dSAndreas Boehler      return $this->sqlite;
455f2c3e2dSAndreas Boehler  }
46a1a3b679SAndreas Boehler
47cb71a62aSAndreas Boehler  /**
48185e2535SAndreas Boehler   * Retrieve meta data for a given page
49185e2535SAndreas Boehler   *
50185e2535SAndreas Boehler   * @param string $id optional The page ID
51185e2535SAndreas Boehler   * @return array The metadata
52185e2535SAndreas Boehler   */
53185e2535SAndreas Boehler  private function getMeta($id = null) {
54185e2535SAndreas Boehler    global $ID;
55185e2535SAndreas Boehler    global $INFO;
56185e2535SAndreas Boehler
57185e2535SAndreas Boehler    if ($id === null) $id = $ID;
58185e2535SAndreas Boehler
59185e2535SAndreas Boehler    if($ID === $id && $INFO['meta']) {
60185e2535SAndreas Boehler        $meta = $INFO['meta'];
61185e2535SAndreas Boehler    } else {
62185e2535SAndreas Boehler        $meta = p_get_metadata($id);
63185e2535SAndreas Boehler    }
64185e2535SAndreas Boehler
65185e2535SAndreas Boehler    return $meta;
66185e2535SAndreas Boehler  }
67185e2535SAndreas Boehler
68185e2535SAndreas Boehler  /**
69185e2535SAndreas Boehler   * Retrieve the meta data for a given page
70185e2535SAndreas Boehler   *
71185e2535SAndreas Boehler   * @param string $id optional The page ID
72185e2535SAndreas Boehler   * @return array with meta data
73185e2535SAndreas Boehler   */
74185e2535SAndreas Boehler  public function getCalendarMetaForPage($id = null)
75185e2535SAndreas Boehler  {
76185e2535SAndreas Boehler      if(is_null($id))
77185e2535SAndreas Boehler      {
78185e2535SAndreas Boehler          global $ID;
79185e2535SAndreas Boehler          $id = $ID;
80185e2535SAndreas Boehler      }
81185e2535SAndreas Boehler
82185e2535SAndreas Boehler      $meta = $this->getMeta($id);
83185e2535SAndreas Boehler      if(isset($meta['plugin_davcal']))
84185e2535SAndreas Boehler        return $meta['plugin_davcal'];
85185e2535SAndreas Boehler      else
86185e2535SAndreas Boehler        return array();
87185e2535SAndreas Boehler  }
88185e2535SAndreas Boehler
89185e2535SAndreas Boehler  /**
90d71c9934SAndreas Boehler   * Check the permission of a user for a given calendar ID
91d71c9934SAndreas Boehler   *
92d71c9934SAndreas Boehler   * @param string $id The calendar ID to check
93d71c9934SAndreas Boehler   * @return int AUTH_* constants
94d71c9934SAndreas Boehler   */
95d71c9934SAndreas Boehler  public function checkCalendarPermission($id)
96d71c9934SAndreas Boehler  {
97*d4992453SAndreas Boehler      if(strpos($id, 'webdav://') === 0)
98d71c9934SAndreas Boehler      {
99d71c9934SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
100d71c9934SAndreas Boehler          if(is_null($wdc))
101d71c9934SAndreas Boehler            return AUTH_NONE;
102*d4992453SAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
103d71c9934SAndreas Boehler          $settings = $wdc->getConnection($connectionId);
104d71c9934SAndreas Boehler          if($settings === false)
105d71c9934SAndreas Boehler            return AUTH_NONE;
106d71c9934SAndreas Boehler          if($settings['write'] === '1')
107d71c9934SAndreas Boehler            return AUTH_CREATE;
108d71c9934SAndreas Boehler          return AUTH_READ;
109d71c9934SAndreas Boehler      }
110d71c9934SAndreas Boehler      else
111d71c9934SAndreas Boehler      {
112d71c9934SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
113d71c9934SAndreas Boehler          // We return AUTH_READ if the calendar does not exist. This makes
114d71c9934SAndreas Boehler          // davcal happy when there are just included calendars
115d71c9934SAndreas Boehler          if($calid === false)
116d71c9934SAndreas Boehler            return AUTH_READ;
117d71c9934SAndreas Boehler          return auth_quickaclcheck($id);
118d71c9934SAndreas Boehler      }
119d71c9934SAndreas Boehler  }
120d71c9934SAndreas Boehler
121d71c9934SAndreas Boehler  /**
12280e1ddf7SAndreas Boehler   * Filter calendar pages and return only those where the current
12380e1ddf7SAndreas Boehler   * user has at least read permission.
12480e1ddf7SAndreas Boehler   *
12580e1ddf7SAndreas Boehler   * @param array $calendarPages Array with calendar pages to check
12680e1ddf7SAndreas Boehler   * @return array with filtered calendar pages
12780e1ddf7SAndreas Boehler   */
12880e1ddf7SAndreas Boehler  public function filterCalendarPagesByUserPermission($calendarPages)
12980e1ddf7SAndreas Boehler  {
13080e1ddf7SAndreas Boehler      $retList = array();
13180e1ddf7SAndreas Boehler      foreach($calendarPages as $page => $data)
13280e1ddf7SAndreas Boehler      {
1330b805092SAndreas Boehler          // WebDAV Connections are always readable
1340b805092SAndreas Boehler          if(strpos($page, 'webdav://') === 0)
1350b805092SAndreas Boehler          {
1360b805092SAndreas Boehler              $retList[$page] = $data;
1370b805092SAndreas Boehler          }
1380b805092SAndreas Boehler          elseif(auth_quickaclcheck($page) >= AUTH_READ)
13980e1ddf7SAndreas Boehler          {
14080e1ddf7SAndreas Boehler              $retList[$page] = $data;
14180e1ddf7SAndreas Boehler          }
14280e1ddf7SAndreas Boehler      }
14380e1ddf7SAndreas Boehler      return $retList;
14480e1ddf7SAndreas Boehler  }
14580e1ddf7SAndreas Boehler
14680e1ddf7SAndreas Boehler  /**
147185e2535SAndreas Boehler   * Get all calendar pages used by a given page
148185e2535SAndreas Boehler   * based on the stored metadata
149185e2535SAndreas Boehler   *
150185e2535SAndreas Boehler   * @param string $id optional The page id
151185e2535SAndreas Boehler   * @return mixed The pages as array or false
152185e2535SAndreas Boehler   */
153185e2535SAndreas Boehler  public function getCalendarPagesByMeta($id = null)
154185e2535SAndreas Boehler  {
155185e2535SAndreas Boehler      if(is_null($id))
156185e2535SAndreas Boehler      {
157185e2535SAndreas Boehler          global $ID;
158185e2535SAndreas Boehler          $id = $ID;
159185e2535SAndreas Boehler      }
160185e2535SAndreas Boehler
161185e2535SAndreas Boehler      $meta = $this->getCalendarMetaForPage($id);
1620b805092SAndreas Boehler
163185e2535SAndreas Boehler      if(isset($meta['id']))
164ed764890SAndreas Boehler      {
165ed764890SAndreas Boehler          // Filter the list of pages by permission
16680e1ddf7SAndreas Boehler          $pages = $this->filterCalendarPagesByUserPermission($meta['id']);
16780e1ddf7SAndreas Boehler          if(empty($pages))
168ed764890SAndreas Boehler            return false;
16980e1ddf7SAndreas Boehler          return $pages;
170ed764890SAndreas Boehler      }
171185e2535SAndreas Boehler      return false;
172185e2535SAndreas Boehler  }
173185e2535SAndreas Boehler
174185e2535SAndreas Boehler  /**
175185e2535SAndreas Boehler   * Get a list of calendar names/pages/ids/colors
176185e2535SAndreas Boehler   * for an array of page ids
177185e2535SAndreas Boehler   *
178185e2535SAndreas Boehler   * @param array $calendarPages The calendar pages to retrieve
179185e2535SAndreas Boehler   * @return array The list
180185e2535SAndreas Boehler   */
181185e2535SAndreas Boehler  public function getCalendarMapForIDs($calendarPages)
182185e2535SAndreas Boehler  {
183185e2535SAndreas Boehler      $data = array();
1844a2bf5eeSAndreas Boehler      foreach($calendarPages as $page => $color)
185185e2535SAndreas Boehler      {
1860b805092SAndreas Boehler            if(strpos($page, 'webdav://') === 0)
1870b805092SAndreas Boehler            {
1880b805092SAndreas Boehler                $wdc =& plugin_load('helper', 'webdavclient');
1890b805092SAndreas Boehler                if(is_null($wdc))
1900b805092SAndreas Boehler                    continue;
1910b805092SAndreas Boehler                $connectionId = str_replace('webdav://', '', $page);
1920b805092SAndreas Boehler                $settings = $wdc->getConnection($connectionId);
1932393a702SAndreas Boehler                if($settings === false)
1942393a702SAndreas Boehler                    continue;
1950b805092SAndreas Boehler                $name = $settings['displayname'];
196d71c9934SAndreas Boehler                $write = ($settings['write'] === '1');
1970b805092SAndreas Boehler                $calid = $connectionId;
198cd2f100dSAndreas Boehler                $color = '#3a87ad';
1990b805092SAndreas Boehler            }
2000b805092SAndreas Boehler            else
2010b805092SAndreas Boehler            {
202185e2535SAndreas Boehler                $calid = $this->getCalendarIdForPage($page);
203185e2535SAndreas Boehler                if($calid !== false)
204185e2535SAndreas Boehler                {
205185e2535SAndreas Boehler                    $settings = $this->getCalendarSettings($calid);
206185e2535SAndreas Boehler                    $name = $settings['displayname'];
207cd2f100dSAndreas Boehler                    $color = $settings['calendarcolor'];
208ed764890SAndreas Boehler                    $write = (auth_quickaclcheck($page) > AUTH_READ);
2090b805092SAndreas Boehler                }
2100b805092SAndreas Boehler                else
2110b805092SAndreas Boehler                {
2120b805092SAndreas Boehler                    continue;
2130b805092SAndreas Boehler                }
2140b805092SAndreas Boehler            }
215185e2535SAndreas Boehler            $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
216ed764890SAndreas Boehler                            'color' => $color, 'write' => $write);
217185e2535SAndreas Boehler      }
218185e2535SAndreas Boehler      return $data;
219185e2535SAndreas Boehler  }
220185e2535SAndreas Boehler
221185e2535SAndreas Boehler  /**
222185e2535SAndreas Boehler   * Get the saved calendar color for a given page.
223185e2535SAndreas Boehler   *
224185e2535SAndreas Boehler   * @param string $id optional The page ID
225185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
226185e2535SAndreas Boehler   */
227185e2535SAndreas Boehler  public function getCalendarColorForPage($id = null)
228185e2535SAndreas Boehler  {
229185e2535SAndreas Boehler      if(is_null($id))
230185e2535SAndreas Boehler      {
231185e2535SAndreas Boehler          global $ID;
232185e2535SAndreas Boehler          $id = $ID;
233185e2535SAndreas Boehler      }
234185e2535SAndreas Boehler
235185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
236185e2535SAndreas Boehler      if($calid === false)
237185e2535SAndreas Boehler        return false;
238185e2535SAndreas Boehler
239185e2535SAndreas Boehler      return $this->getCalendarColorForCalendar($calid);
240185e2535SAndreas Boehler  }
241185e2535SAndreas Boehler
242185e2535SAndreas Boehler  /**
243185e2535SAndreas Boehler   * Get the saved calendar color for a given calendar ID.
244185e2535SAndreas Boehler   *
245185e2535SAndreas Boehler   * @param string $id optional The calendar ID
246185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
247185e2535SAndreas Boehler   */
248185e2535SAndreas Boehler  public function getCalendarColorForCalendar($calid)
249185e2535SAndreas Boehler  {
250185e2535SAndreas Boehler      if(isset($this->cachedValues['calendarcolor'][$calid]))
251185e2535SAndreas Boehler        return $this->cachedValues['calendarcolor'][$calid];
252185e2535SAndreas Boehler
253185e2535SAndreas Boehler      $row = $this->getCalendarSettings($calid);
254185e2535SAndreas Boehler
255185e2535SAndreas Boehler      if(!isset($row['calendarcolor']))
256185e2535SAndreas Boehler        return false;
257185e2535SAndreas Boehler
258185e2535SAndreas Boehler      $color = $row['calendarcolor'];
259185e2535SAndreas Boehler      $this->cachedValues['calendarcolor'][$calid] = $color;
260185e2535SAndreas Boehler      return $color;
261185e2535SAndreas Boehler  }
262185e2535SAndreas Boehler
263185e2535SAndreas Boehler  /**
264e86c8dd3SAndreas Boehler   * Get the user's principal URL for iOS sync
265e86c8dd3SAndreas Boehler   * @param string $user the user name
266e86c8dd3SAndreas Boehler   * @return the URL to the principal sync
267e86c8dd3SAndreas Boehler   */
268e86c8dd3SAndreas Boehler  public function getPrincipalUrlForUser($user)
269e86c8dd3SAndreas Boehler  {
270e86c8dd3SAndreas Boehler      if(is_null($user))
271e86c8dd3SAndreas Boehler        return false;
272e86c8dd3SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user;
273e86c8dd3SAndreas Boehler      return $url;
274e86c8dd3SAndreas Boehler  }
275e86c8dd3SAndreas Boehler
276e86c8dd3SAndreas Boehler  /**
277185e2535SAndreas Boehler   * Set the calendar color for a given page.
278185e2535SAndreas Boehler   *
279185e2535SAndreas Boehler   * @param string $color The color definition
280185e2535SAndreas Boehler   * @param string $id optional The page ID
281185e2535SAndreas Boehler   * @return boolean True on success, otherwise false
282185e2535SAndreas Boehler   */
283185e2535SAndreas Boehler  public function setCalendarColorForPage($color, $id = null)
284185e2535SAndreas Boehler  {
285185e2535SAndreas Boehler      if(is_null($id))
286185e2535SAndreas Boehler      {
287185e2535SAndreas Boehler          global $ID;
288185e2535SAndreas Boehler          $id = $ID;
289185e2535SAndreas Boehler      }
290185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
291185e2535SAndreas Boehler      if($calid === false)
292185e2535SAndreas Boehler        return false;
293185e2535SAndreas Boehler
2945f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
2955f2c3e2dSAndreas Boehler      if(!$sqlite)
2965f2c3e2dSAndreas Boehler        return false;
29751f4febbSAndreas Boehler      $query = "UPDATE calendars SET calendarcolor = ? ".
29851f4febbSAndreas Boehler               " WHERE id = ?";
2995f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $color, $calid);
300185e2535SAndreas Boehler      if($res !== false)
301185e2535SAndreas Boehler      {
302185e2535SAndreas Boehler        $this->cachedValues['calendarcolor'][$calid] = $color;
303185e2535SAndreas Boehler        return true;
304185e2535SAndreas Boehler      }
305185e2535SAndreas Boehler      return false;
306185e2535SAndreas Boehler  }
307185e2535SAndreas Boehler
308185e2535SAndreas Boehler  /**
309cb71a62aSAndreas Boehler   * Set the calendar name and description for a given page with a given
310cb71a62aSAndreas Boehler   * page id.
311cb71a62aSAndreas Boehler   * If the calendar doesn't exist, the calendar is created!
312cb71a62aSAndreas Boehler   *
313cb71a62aSAndreas Boehler   * @param string  $name The name of the new calendar
314cb71a62aSAndreas Boehler   * @param string  $description The description of the new calendar
315cb71a62aSAndreas Boehler   * @param string  $id (optional) The ID of the page
316cb71a62aSAndreas Boehler   * @param string  $userid The userid of the creating user
317cb71a62aSAndreas Boehler   *
318cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false.
319cb71a62aSAndreas Boehler   */
320a1a3b679SAndreas Boehler  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
321a1a3b679SAndreas Boehler  {
322a1a3b679SAndreas Boehler      if(is_null($id))
323a1a3b679SAndreas Boehler      {
324a1a3b679SAndreas Boehler          global $ID;
325a1a3b679SAndreas Boehler          $id = $ID;
326a1a3b679SAndreas Boehler      }
327a1a3b679SAndreas Boehler      if(is_null($userid))
32834a47953SAndreas Boehler      {
32934a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
33034a47953SAndreas Boehler        {
331a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
33234a47953SAndreas Boehler        }
33334a47953SAndreas Boehler        else
33434a47953SAndreas Boehler        {
33534a47953SAndreas Boehler          $userid = uniqid('davcal-');
33634a47953SAndreas Boehler        }
33734a47953SAndreas Boehler      }
338a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
339a1a3b679SAndreas Boehler      if($calid === false)
340a1a3b679SAndreas Boehler        return $this->createCalendarForPage($name, $description, $id, $userid);
341a1a3b679SAndreas Boehler
3425f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
3435f2c3e2dSAndreas Boehler      if(!$sqlite)
3445f2c3e2dSAndreas Boehler        return false;
34551f4febbSAndreas Boehler      $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?";
3465f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $name, $description, $calid);
347b269830cSAndreas Boehler      if($res !== false)
348b269830cSAndreas Boehler        return true;
349b269830cSAndreas Boehler      return false;
350a1a3b679SAndreas Boehler  }
351a1a3b679SAndreas Boehler
352cb71a62aSAndreas Boehler  /**
353d5703f5aSAndreas Boehler   * Update a calendar's displayname
354d5703f5aSAndreas Boehler   *
355d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
356d5703f5aSAndreas Boehler   * @param string $name The new calendar name
357d5703f5aSAndreas Boehler   *
358d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
359d5703f5aSAndreas Boehler   */
360d5703f5aSAndreas Boehler  public function updateCalendarName($calid, $name)
361d5703f5aSAndreas Boehler  {
3625f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
3635f2c3e2dSAndreas Boehler      if(!$sqlite)
3645f2c3e2dSAndreas Boehler        return false;
365d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET displayname = ? WHERE id = ?";
3665f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid, $name);
367d5703f5aSAndreas Boehler      if($res !== false)
368d5703f5aSAndreas Boehler      {
369d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
370d5703f5aSAndreas Boehler        return true;
371d5703f5aSAndreas Boehler      }
372d5703f5aSAndreas Boehler      return false;
373d5703f5aSAndreas Boehler  }
374d5703f5aSAndreas Boehler
375d5703f5aSAndreas Boehler  /**
376d5703f5aSAndreas Boehler   * Update the calendar description
377d5703f5aSAndreas Boehler   *
378d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
379d5703f5aSAndreas Boehler   * @param string $description The new calendar's description
380d5703f5aSAndreas Boehler   *
381d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
382d5703f5aSAndreas Boehler   */
383d5703f5aSAndreas Boehler  public function updateCalendarDescription($calid, $description)
384d5703f5aSAndreas Boehler  {
3855f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
3865f2c3e2dSAndreas Boehler      if(!$sqlite)
3875f2c3e2dSAndreas Boehler        return false;
388d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET description = ? WHERE id = ?";
3895f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid, $description);
390d5703f5aSAndreas Boehler      if($res !== false)
391d5703f5aSAndreas Boehler      {
392d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
393d5703f5aSAndreas Boehler        return true;
394d5703f5aSAndreas Boehler      }
395d5703f5aSAndreas Boehler      return false;
396d5703f5aSAndreas Boehler  }
397d5703f5aSAndreas Boehler
398d5703f5aSAndreas Boehler  /**
399d5703f5aSAndreas Boehler   * Update a calendar's timezone information
400d5703f5aSAndreas Boehler   *
401d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
402d5703f5aSAndreas Boehler   * @param string $timezone The new timezone to set
403d5703f5aSAndreas Boehler   *
404d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
405d5703f5aSAndreas Boehler   */
406d5703f5aSAndreas Boehler  public function updateCalendarTimezone($calid, $timezone)
407d5703f5aSAndreas Boehler  {
4085f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
4095f2c3e2dSAndreas Boehler      if(!$sqlite)
4105f2c3e2dSAndreas Boehler        return false;
411d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET timezone = ? WHERE id = ?";
4125f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid, $timezone);
413d5703f5aSAndreas Boehler      if($res !== false)
414d5703f5aSAndreas Boehler      {
415d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
416d5703f5aSAndreas Boehler        return true;
417d5703f5aSAndreas Boehler      }
418d5703f5aSAndreas Boehler      return false;
419d5703f5aSAndreas Boehler  }
420d5703f5aSAndreas Boehler
421d5703f5aSAndreas Boehler  /**
422cb71a62aSAndreas Boehler   * Save the personal settings to the SQLite database 'calendarsettings'.
423cb71a62aSAndreas Boehler   *
424cb71a62aSAndreas Boehler   * @param array  $settings The settings array to store
425cb71a62aSAndreas Boehler   * @param string $userid (optional) The userid to store
426cb71a62aSAndreas Boehler   *
427cb71a62aSAndreas Boehler   * @param boolean True on success, otherwise false
428cb71a62aSAndreas Boehler   */
429a495d34cSAndreas Boehler  public function savePersonalSettings($settings, $userid = null)
430a495d34cSAndreas Boehler  {
431a495d34cSAndreas Boehler      if(is_null($userid))
43234a47953SAndreas Boehler      {
43334a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
43434a47953SAndreas Boehler          {
435a495d34cSAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
43634a47953SAndreas Boehler          }
43734a47953SAndreas Boehler          else
43834a47953SAndreas Boehler          {
43934a47953SAndreas Boehler              return false;
44034a47953SAndreas Boehler          }
44134a47953SAndreas Boehler      }
4425f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
4435f2c3e2dSAndreas Boehler      if(!$sqlite)
4445f2c3e2dSAndreas Boehler        return false;
4455f2c3e2dSAndreas Boehler      $sqlite->query("BEGIN TRANSACTION");
446a495d34cSAndreas Boehler
44751f4febbSAndreas Boehler      $query = "DELETE FROM calendarsettings WHERE userid = ?";
4485f2c3e2dSAndreas Boehler      $sqlite->query($query, $userid);
449bd883736SAndreas Boehler
450a495d34cSAndreas Boehler      foreach($settings as $key => $value)
451a495d34cSAndreas Boehler      {
45251f4febbSAndreas Boehler          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)";
4535f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $userid, $key, $value);
454a495d34cSAndreas Boehler          if($res === false)
455a495d34cSAndreas Boehler              return false;
456a495d34cSAndreas Boehler      }
4575f2c3e2dSAndreas Boehler      $sqlite->query("COMMIT TRANSACTION");
458185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
459a495d34cSAndreas Boehler      return true;
460a495d34cSAndreas Boehler  }
461a495d34cSAndreas Boehler
462cb71a62aSAndreas Boehler  /**
463cb71a62aSAndreas Boehler   * Retrieve the settings array for a given user id.
464cb71a62aSAndreas Boehler   * Some sane defaults are returned, currently:
465cb71a62aSAndreas Boehler   *
466cb71a62aSAndreas Boehler   *    timezone    => local
467cb71a62aSAndreas Boehler   *    weeknumbers => 0
468cb71a62aSAndreas Boehler   *    workweek    => 0
469cb71a62aSAndreas Boehler   *
470cb71a62aSAndreas Boehler   * @param string $userid (optional) The user id to retrieve
471cb71a62aSAndreas Boehler   *
472cb71a62aSAndreas Boehler   * @return array The settings array
473cb71a62aSAndreas Boehler   */
474a495d34cSAndreas Boehler  public function getPersonalSettings($userid = null)
475a495d34cSAndreas Boehler  {
476bd883736SAndreas Boehler      // Some sane default settings
477bd883736SAndreas Boehler      $settings = array(
478fb813b30SAndreas Boehler        'timezone' => $this->getConf('timezone'),
479fb813b30SAndreas Boehler        'weeknumbers' => $this->getConf('weeknumbers'),
480fb813b30SAndreas Boehler        'workweek' => $this->getConf('workweek'),
4811d5bdcd0SAndreas Boehler        'monday' => $this->getConf('monday'),
4821d5bdcd0SAndreas Boehler        'timeformat' => $this->getConf('timeformat')
483bd883736SAndreas Boehler      );
48434a47953SAndreas Boehler      if(is_null($userid))
48534a47953SAndreas Boehler      {
48634a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
48734a47953SAndreas Boehler          {
48834a47953SAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
48934a47953SAndreas Boehler          }
49034a47953SAndreas Boehler          else
49134a47953SAndreas Boehler          {
49234a47953SAndreas Boehler            return $settings;
49334a47953SAndreas Boehler          }
49434a47953SAndreas Boehler      }
49534a47953SAndreas Boehler
4965f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
4975f2c3e2dSAndreas Boehler      if(!$sqlite)
4985f2c3e2dSAndreas Boehler        return false;
49934a47953SAndreas Boehler      if(isset($this->cachedValues['settings'][$userid]))
50034a47953SAndreas Boehler        return $this->cachedValues['settings'][$userid];
50151f4febbSAndreas Boehler      $query = "SELECT key, value FROM calendarsettings WHERE userid = ?";
5025f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $userid);
5035f2c3e2dSAndreas Boehler      $arr = $sqlite->res2arr($res);
504a495d34cSAndreas Boehler      foreach($arr as $row)
505a495d34cSAndreas Boehler      {
506a495d34cSAndreas Boehler          $settings[$row['key']] = $row['value'];
507a495d34cSAndreas Boehler      }
508185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
509a495d34cSAndreas Boehler      return $settings;
510a495d34cSAndreas Boehler  }
511a495d34cSAndreas Boehler
512cb71a62aSAndreas Boehler  /**
513cb71a62aSAndreas Boehler   * Retrieve the calendar ID based on a page ID from the SQLite table
514cb71a62aSAndreas Boehler   * 'pagetocalendarmapping'.
515cb71a62aSAndreas Boehler   *
516cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to retrieve the corresponding calendar
517cb71a62aSAndreas Boehler   *
518cb71a62aSAndreas Boehler   * @return mixed the ID on success, otherwise false
519cb71a62aSAndreas Boehler   */
520a1a3b679SAndreas Boehler  public function getCalendarIdForPage($id = null)
521a1a3b679SAndreas Boehler  {
522a1a3b679SAndreas Boehler      if(is_null($id))
523a1a3b679SAndreas Boehler      {
524a1a3b679SAndreas Boehler          global $ID;
525a1a3b679SAndreas Boehler          $id = $ID;
526a1a3b679SAndreas Boehler      }
527a1a3b679SAndreas Boehler
528185e2535SAndreas Boehler      if(isset($this->cachedValues['calid'][$id]))
529185e2535SAndreas Boehler        return $this->cachedValues['calid'][$id];
530185e2535SAndreas Boehler
5315f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
5325f2c3e2dSAndreas Boehler      if(!$sqlite)
5335f2c3e2dSAndreas Boehler        return false;
53451f4febbSAndreas Boehler      $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?";
5355f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $id);
5365f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
537a1a3b679SAndreas Boehler      if(isset($row['calid']))
538185e2535SAndreas Boehler      {
539185e2535SAndreas Boehler        $calid = $row['calid'];
540185e2535SAndreas Boehler        $this->cachedValues['calid'] = $calid;
541185e2535SAndreas Boehler        return $calid;
542185e2535SAndreas Boehler      }
543a1a3b679SAndreas Boehler      return false;
544a1a3b679SAndreas Boehler  }
545a1a3b679SAndreas Boehler
546cb71a62aSAndreas Boehler  /**
547cb71a62aSAndreas Boehler   * Retrieve the complete calendar id to page mapping.
548cb71a62aSAndreas Boehler   * This is necessary to be able to retrieve a list of
549cb71a62aSAndreas Boehler   * calendars for a given user and check the access rights.
550cb71a62aSAndreas Boehler   *
551cb71a62aSAndreas Boehler   * @return array The mapping array
552cb71a62aSAndreas Boehler   */
553a1a3b679SAndreas Boehler  public function getCalendarIdToPageMapping()
554a1a3b679SAndreas Boehler  {
5555f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
5565f2c3e2dSAndreas Boehler      if(!$sqlite)
5575f2c3e2dSAndreas Boehler        return false;
558a1a3b679SAndreas Boehler      $query = "SELECT calid, page FROM pagetocalendarmapping";
5595f2c3e2dSAndreas Boehler      $res = $sqlite->query($query);
5605f2c3e2dSAndreas Boehler      $arr = $sqlite->res2arr($res);
561a1a3b679SAndreas Boehler      return $arr;
562a1a3b679SAndreas Boehler  }
563a1a3b679SAndreas Boehler
564cb71a62aSAndreas Boehler  /**
565cb71a62aSAndreas Boehler   * Retrieve all calendar IDs a given user has access to.
566cb71a62aSAndreas Boehler   * The user is specified by the principalUri, so the
567cb71a62aSAndreas Boehler   * user name is actually split from the URI component.
568cb71a62aSAndreas Boehler   *
569cb71a62aSAndreas Boehler   * Access rights are checked against DokuWiki's ACL
570cb71a62aSAndreas Boehler   * and applied accordingly.
571cb71a62aSAndreas Boehler   *
572cb71a62aSAndreas Boehler   * @param string $principalUri The principal URI to work on
573cb71a62aSAndreas Boehler   *
574cb71a62aSAndreas Boehler   * @return array An associative array of calendar IDs
575cb71a62aSAndreas Boehler   */
576a1a3b679SAndreas Boehler  public function getCalendarIdsForUser($principalUri)
577a1a3b679SAndreas Boehler  {
57834a47953SAndreas Boehler      global $auth;
579a1a3b679SAndreas Boehler      $user = explode('/', $principalUri);
580a1a3b679SAndreas Boehler      $user = end($user);
581a1a3b679SAndreas Boehler      $mapping = $this->getCalendarIdToPageMapping();
582a1a3b679SAndreas Boehler      $calids = array();
58334a47953SAndreas Boehler      $ud = $auth->getUserData($user);
58434a47953SAndreas Boehler      $groups = $ud['grps'];
585a1a3b679SAndreas Boehler      foreach($mapping as $row)
586a1a3b679SAndreas Boehler      {
587a1a3b679SAndreas Boehler          $id = $row['calid'];
58813b16484SAndreas Boehler          $enabled = $this->getCalendarStatus($id);
58913b16484SAndreas Boehler          if($enabled == false)
59013b16484SAndreas Boehler            continue;
591a1a3b679SAndreas Boehler          $page = $row['page'];
59234a47953SAndreas Boehler          $acl = auth_aclcheck($page, $user, $groups);
593a1a3b679SAndreas Boehler          if($acl >= AUTH_READ)
594a1a3b679SAndreas Boehler          {
595a1a3b679SAndreas Boehler              $write = $acl > AUTH_READ;
596a1a3b679SAndreas Boehler              $calids[$id] = array('readonly' => !$write);
597a1a3b679SAndreas Boehler          }
598a1a3b679SAndreas Boehler      }
599a1a3b679SAndreas Boehler      return $calids;
600a1a3b679SAndreas Boehler  }
601a1a3b679SAndreas Boehler
602cb71a62aSAndreas Boehler  /**
603cb71a62aSAndreas Boehler   * Create a new calendar for a given page ID and set name and description
604cb71a62aSAndreas Boehler   * accordingly. Also update the pagetocalendarmapping table on success.
605cb71a62aSAndreas Boehler   *
606cb71a62aSAndreas Boehler   * @param string $name The calendar's name
607cb71a62aSAndreas Boehler   * @param string $description The calendar's description
608cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to work on
609cb71a62aSAndreas Boehler   * @param string $userid (optional) The user ID that created the calendar
610cb71a62aSAndreas Boehler   *
611cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
612cb71a62aSAndreas Boehler   */
613a1a3b679SAndreas Boehler  public function createCalendarForPage($name, $description, $id = null, $userid = null)
614a1a3b679SAndreas Boehler  {
615a1a3b679SAndreas Boehler      if(is_null($id))
616a1a3b679SAndreas Boehler      {
617a1a3b679SAndreas Boehler          global $ID;
618a1a3b679SAndreas Boehler          $id = $ID;
619a1a3b679SAndreas Boehler      }
620a1a3b679SAndreas Boehler      if(is_null($userid))
62134a47953SAndreas Boehler      {
62234a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
62334a47953SAndreas Boehler        {
624a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
62534a47953SAndreas Boehler        }
62634a47953SAndreas Boehler        else
62734a47953SAndreas Boehler        {
62834a47953SAndreas Boehler          $userid = uniqid('davcal-');
62934a47953SAndreas Boehler        }
63034a47953SAndreas Boehler      }
631a1a3b679SAndreas Boehler      $values = array('principals/'.$userid,
632a1a3b679SAndreas Boehler                      $name,
633a1a3b679SAndreas Boehler                      str_replace(array('/', ' ', ':'), '_', $id),
634a1a3b679SAndreas Boehler                      $description,
635a1a3b679SAndreas Boehler                      'VEVENT,VTODO',
63655a741c0SAndreas Boehler                      0,
63755a741c0SAndreas Boehler                      1);
6385f2c3e2dSAndreas Boehler
6395f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
6405f2c3e2dSAndreas Boehler      if(!$sqlite)
6415f2c3e2dSAndreas Boehler        return false;
64251f4febbSAndreas Boehler      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ".
64351f4febbSAndreas Boehler               "VALUES (?, ?, ?, ?, ?, ?, ?)";
6445f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]);
64555a741c0SAndreas Boehler      if($res === false)
64655a741c0SAndreas Boehler        return false;
647cb71a62aSAndreas Boehler
648cb71a62aSAndreas Boehler      // Get the new calendar ID
64951f4febbSAndreas Boehler      $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ".
65051f4febbSAndreas Boehler               "uri = ? AND description = ?";
6515f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3]);
6525f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
653cb71a62aSAndreas Boehler
654cb71a62aSAndreas Boehler      // Update the pagetocalendarmapping table with the new calendar ID
655a1a3b679SAndreas Boehler      if(isset($row['id']))
656a1a3b679SAndreas Boehler      {
65751f4febbSAndreas Boehler          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)";
6585f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $id, $row['id']);
65955a741c0SAndreas Boehler          return ($res !== false);
660a1a3b679SAndreas Boehler      }
661a1a3b679SAndreas Boehler
662a1a3b679SAndreas Boehler      return false;
663a1a3b679SAndreas Boehler  }
664a1a3b679SAndreas Boehler
665cb71a62aSAndreas Boehler  /**
666d5703f5aSAndreas Boehler   * Add a new calendar entry to the given calendar. Calendar data is
667d5703f5aSAndreas Boehler   * specified as ICS file, thus it needs to be parsed first.
668d5703f5aSAndreas Boehler   *
669d5703f5aSAndreas Boehler   * This is mainly needed for the sync support.
670d5703f5aSAndreas Boehler   *
671d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
672d5703f5aSAndreas Boehler   * @param string $uri The new object URI
673d5703f5aSAndreas Boehler   * @param string $ics The ICS file
674d5703f5aSAndreas Boehler   *
675d5703f5aSAndreas Boehler   * @return mixed The etag.
676d5703f5aSAndreas Boehler   */
677d5703f5aSAndreas Boehler  public function addCalendarEntryToCalendarByICS($calid, $uri, $ics)
678d5703f5aSAndreas Boehler  {
679d5703f5aSAndreas Boehler    $extraData = $this->getDenormalizedData($ics);
680d5703f5aSAndreas Boehler
6815f2c3e2dSAndreas Boehler    $sqlite = $this->getDB();
6825f2c3e2dSAndreas Boehler    if(!$sqlite)
6835f2c3e2dSAndreas Boehler      return false;
684d5703f5aSAndreas Boehler    $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)";
6855f2c3e2dSAndreas Boehler    $res = $sqlite->query($query,
686d5703f5aSAndreas Boehler            $calid,
687d5703f5aSAndreas Boehler            $uri,
688d5703f5aSAndreas Boehler            $ics,
689d5703f5aSAndreas Boehler            time(),
690d5703f5aSAndreas Boehler            $extraData['etag'],
691d5703f5aSAndreas Boehler            $extraData['size'],
692d5703f5aSAndreas Boehler            $extraData['componentType'],
693d5703f5aSAndreas Boehler            $extraData['firstOccurence'],
694d5703f5aSAndreas Boehler            $extraData['lastOccurence'],
695d5703f5aSAndreas Boehler            $extraData['uid']);
696d5703f5aSAndreas Boehler            // If successfully, update the sync token database
697d5703f5aSAndreas Boehler    if($res !== false)
698d5703f5aSAndreas Boehler    {
699d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, $uri, 'added');
700d5703f5aSAndreas Boehler    }
701d5703f5aSAndreas Boehler    return $extraData['etag'];
702d5703f5aSAndreas Boehler  }
703d5703f5aSAndreas Boehler
704d5703f5aSAndreas Boehler  /**
705d5703f5aSAndreas Boehler   * Edit a calendar entry by providing a new ICS file. This is mainly
706d5703f5aSAndreas Boehler   * needed for the sync support.
707d5703f5aSAndreas Boehler   *
708d5703f5aSAndreas Boehler   * @param int $calid The calendar's IS
709d5703f5aSAndreas Boehler   * @param string $uri The object's URI to modify
710d5703f5aSAndreas Boehler   * @param string $ics The new object's ICS file
711d5703f5aSAndreas Boehler   */
712d5703f5aSAndreas Boehler  public function editCalendarEntryToCalendarByICS($calid, $uri, $ics)
713d5703f5aSAndreas Boehler  {
714d5703f5aSAndreas Boehler      $extraData = $this->getDenormalizedData($ics);
715d5703f5aSAndreas Boehler
7165f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
7175f2c3e2dSAndreas Boehler      if(!$sqlite)
7185f2c3e2dSAndreas Boehler        return false;
719d5703f5aSAndreas Boehler      $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?";
7205f2c3e2dSAndreas Boehler      $res = $sqlite->query($query,
721d5703f5aSAndreas Boehler        $ics,
722d5703f5aSAndreas Boehler        time(),
723d5703f5aSAndreas Boehler        $extraData['etag'],
724d5703f5aSAndreas Boehler        $extraData['size'],
725d5703f5aSAndreas Boehler        $extraData['componentType'],
726d5703f5aSAndreas Boehler        $extraData['firstOccurence'],
727d5703f5aSAndreas Boehler        $extraData['lastOccurence'],
728d5703f5aSAndreas Boehler        $extraData['uid'],
729d5703f5aSAndreas Boehler        $calid,
730d5703f5aSAndreas Boehler        $uri
731d5703f5aSAndreas Boehler      );
732d5703f5aSAndreas Boehler      if($res !== false)
733d5703f5aSAndreas Boehler      {
734d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'modified');
735d5703f5aSAndreas Boehler      }
736d5703f5aSAndreas Boehler      return $extraData['etag'];
737d5703f5aSAndreas Boehler  }
738d5703f5aSAndreas Boehler
739d5703f5aSAndreas Boehler  /**
740cb71a62aSAndreas Boehler   * Add a new iCal entry for a given page, i.e. a given calendar.
741cb71a62aSAndreas Boehler   *
742cb71a62aSAndreas Boehler   * The parameter array needs to contain
743cb71a62aSAndreas Boehler   *   detectedtz       => The timezone as detected by the browser
74482a48dfbSAndreas Boehler   *   currenttz        => The timezone in use by the calendar
745cb71a62aSAndreas Boehler   *   eventfrom        => The event's start date
746cb71a62aSAndreas Boehler   *   eventfromtime    => The event's start time
747cb71a62aSAndreas Boehler   *   eventto          => The event's end date
748cb71a62aSAndreas Boehler   *   eventtotime      => The event's end time
749cb71a62aSAndreas Boehler   *   eventname        => The event's name
750cb71a62aSAndreas Boehler   *   eventdescription => The event's description
751cb71a62aSAndreas Boehler   *
752cb71a62aSAndreas Boehler   * @param string $id The page ID to work on
753cb71a62aSAndreas Boehler   * @param string $user The user who created the calendar
754cb71a62aSAndreas Boehler   * @param string $params A parameter array with values to create
755cb71a62aSAndreas Boehler   *
756cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
757cb71a62aSAndreas Boehler   */
758a1a3b679SAndreas Boehler  public function addCalendarEntryToCalendarForPage($id, $user, $params)
759a1a3b679SAndreas Boehler  {
76082a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
76182a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
76282a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
763a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
764bd883736SAndreas Boehler      else
765bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
766cb71a62aSAndreas Boehler
767cb71a62aSAndreas Boehler      // Retrieve dates from settings
768b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
769b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
770b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
771b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
772cb71a62aSAndreas Boehler
773cb71a62aSAndreas Boehler      // Load SabreDAV
7749bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
775a1a3b679SAndreas Boehler      $vcalendar = new \Sabre\VObject\Component\VCalendar();
776cb71a62aSAndreas Boehler
777cb71a62aSAndreas Boehler      // Add VCalendar, UID and Event Name
778a1a3b679SAndreas Boehler      $event = $vcalendar->add('VEVENT');
779b269830cSAndreas Boehler      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
780b269830cSAndreas Boehler      $event->add('UID', $uuid);
781a1a3b679SAndreas Boehler      $event->summary = $params['eventname'];
782cb71a62aSAndreas Boehler
783cb71a62aSAndreas Boehler      // Add a description if requested
7840eebc909SAndreas Boehler      $description = $params['eventdescription'];
7850eebc909SAndreas Boehler      if($description !== '')
7860eebc909SAndreas Boehler        $event->add('DESCRIPTION', $description);
787cb71a62aSAndreas Boehler
7882b7be5bdSAndreas Boehler      // Add a location if requested
7892b7be5bdSAndreas Boehler      $location = $params['eventlocation'];
7902b7be5bdSAndreas Boehler      if($location !== '')
7912b7be5bdSAndreas Boehler        $event->add('LOCATION', $location);
7922b7be5bdSAndreas Boehler
7934ecb526cSAndreas Boehler      // Add attachments
7944ecb526cSAndreas Boehler      $attachments = $params['attachments'];
79582a48dfbSAndreas Boehler      if(!is_null($attachments))
7964ecb526cSAndreas Boehler        foreach($attachments as $attachment)
7974ecb526cSAndreas Boehler          $event->add('ATTACH', $attachment);
7984ecb526cSAndreas Boehler
799cb71a62aSAndreas Boehler      // Create a timestamp for last modified, created and dtstamp values in UTC
800b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
801b269830cSAndreas Boehler      $event->add('DTSTAMP', $dtStamp);
802b269830cSAndreas Boehler      $event->add('CREATED', $dtStamp);
803b269830cSAndreas Boehler      $event->add('LAST-MODIFIED', $dtStamp);
804cb71a62aSAndreas Boehler
805cb71a62aSAndreas Boehler      // Adjust the start date, based on the given timezone information
806b269830cSAndreas Boehler      $dtStart = new \DateTime();
807a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
808b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
809cb71a62aSAndreas Boehler
810cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
811b269830cSAndreas Boehler      if($params['allday'] != '1')
812b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
813cb71a62aSAndreas Boehler
814cb71a62aSAndreas Boehler      // Adjust the end date, based on the given timezone information
815b269830cSAndreas Boehler      $dtEnd = new \DateTime();
816a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
817b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
818cb71a62aSAndreas Boehler
819cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
820b269830cSAndreas Boehler      if($params['allday'] != '1')
821b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
822cb71a62aSAndreas Boehler
823b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
824b269830cSAndreas Boehler      if($params['allday'] == '1')
825b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
826cb71a62aSAndreas Boehler
827cb71a62aSAndreas Boehler      // Really add Start and End events
828b269830cSAndreas Boehler      $dtStartEv = $event->add('DTSTART', $dtStart);
829b269830cSAndreas Boehler      $dtEndEv = $event->add('DTEND', $dtEnd);
830cb71a62aSAndreas Boehler
831cb71a62aSAndreas Boehler      // Adjust the DATE format for allday events
832b269830cSAndreas Boehler      if($params['allday'] == '1')
833b269830cSAndreas Boehler      {
834b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
835b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
836b269830cSAndreas Boehler      }
837cb71a62aSAndreas Boehler
838809cb0faSAndreas Boehler      $eventStr = $vcalendar->serialize();
839809cb0faSAndreas Boehler
840809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
841809cb0faSAndreas Boehler      {
842809cb0faSAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
843809cb0faSAndreas Boehler          if(is_null($wdc))
844809cb0faSAndreas Boehler            return false;
845809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
846809cb0faSAndreas Boehler          return $wdc->addCalendarEntry($connectionId, $eventStr);
847809cb0faSAndreas Boehler      }
848809cb0faSAndreas Boehler      else
849809cb0faSAndreas Boehler      {
850cb71a62aSAndreas Boehler          // Actually add the values to the database
851a1a3b679SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
852a1a3b679SAndreas Boehler          $uri = uniqid('dokuwiki-').'.ics';
8531bb22c2bSAndreas Boehler          $now = new \DateTime();
854a1a3b679SAndreas Boehler
8555f2c3e2dSAndreas Boehler          $sqlite = $this->getDB();
8565f2c3e2dSAndreas Boehler          if(!$sqlite)
8575f2c3e2dSAndreas Boehler            return false;
85851f4febbSAndreas Boehler          $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
8595f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT',
86051f4febbSAndreas Boehler                                      $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(),
86151f4febbSAndreas Boehler                                      strlen($eventStr), md5($eventStr), $uuid);
862cb71a62aSAndreas Boehler
863cb71a62aSAndreas Boehler          // If successfully, update the sync token database
86455a741c0SAndreas Boehler          if($res !== false)
86555a741c0SAndreas Boehler          {
86655a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'added');
867a1a3b679SAndreas Boehler              return true;
868a1a3b679SAndreas Boehler          }
869809cb0faSAndreas Boehler      }
87055a741c0SAndreas Boehler      return false;
87155a741c0SAndreas Boehler  }
872a1a3b679SAndreas Boehler
873cb71a62aSAndreas Boehler  /**
874cb71a62aSAndreas Boehler   * Retrieve the calendar settings of a given calendar id
875cb71a62aSAndreas Boehler   *
876cb71a62aSAndreas Boehler   * @param string $calid The calendar ID
877cb71a62aSAndreas Boehler   *
878cb71a62aSAndreas Boehler   * @return array The calendar settings array
879cb71a62aSAndreas Boehler   */
880b269830cSAndreas Boehler  public function getCalendarSettings($calid)
881b269830cSAndreas Boehler  {
8825f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
8835f2c3e2dSAndreas Boehler      if(!$sqlite)
8845f2c3e2dSAndreas Boehler        return false;
88513b16484SAndreas Boehler      $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled FROM calendars WHERE id= ? ";
8865f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
8875f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
888b269830cSAndreas Boehler      return $row;
889b269830cSAndreas Boehler  }
890b269830cSAndreas Boehler
891cb71a62aSAndreas Boehler  /**
89213b16484SAndreas Boehler   * Retrieve the calendar status of a given calendar id
89313b16484SAndreas Boehler   *
89413b16484SAndreas Boehler   * @param string $calid The calendar ID
89513b16484SAndreas Boehler   * @return boolean True if calendar is enabled, otherwise false
89613b16484SAndreas Boehler   */
89713b16484SAndreas Boehler  public function getCalendarStatus($calid)
89813b16484SAndreas Boehler  {
8995f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
9005f2c3e2dSAndreas Boehler      if(!$sqlite)
9015f2c3e2dSAndreas Boehler        return false;
90213b16484SAndreas Boehler      $query = "SELECT disabled FROM calendars WHERE id = ?";
9035f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
9045f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
90513b16484SAndreas Boehler      if($row['disabled'] == 1)
90613b16484SAndreas Boehler        return false;
90713b16484SAndreas Boehler      else
90813b16484SAndreas Boehler        return true;
90913b16484SAndreas Boehler  }
91013b16484SAndreas Boehler
91113b16484SAndreas Boehler  /**
91213b16484SAndreas Boehler   * Disable a calendar for a given page
91313b16484SAndreas Boehler   *
91413b16484SAndreas Boehler   * @param string $id The page ID
91513b16484SAndreas Boehler   *
91613b16484SAndreas Boehler   * @return boolean true on success, otherwise false
91713b16484SAndreas Boehler   */
91813b16484SAndreas Boehler  public function disableCalendarForPage($id)
91913b16484SAndreas Boehler  {
92013b16484SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
92113b16484SAndreas Boehler      if($calid === false)
92213b16484SAndreas Boehler        return false;
9235f2c3e2dSAndreas Boehler
9245f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
9255f2c3e2dSAndreas Boehler      if(!$sqlite)
9265f2c3e2dSAndreas Boehler        return false;
92713b16484SAndreas Boehler      $query = "UPDATE calendars SET disabled = 1 WHERE id = ?";
9285f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
92913b16484SAndreas Boehler      if($res !== false)
93013b16484SAndreas Boehler        return true;
93113b16484SAndreas Boehler      return false;
93213b16484SAndreas Boehler  }
93313b16484SAndreas Boehler
93413b16484SAndreas Boehler  /**
93513b16484SAndreas Boehler   * Enable a calendar for a given page
93613b16484SAndreas Boehler   *
93713b16484SAndreas Boehler   * @param string $id The page ID
93813b16484SAndreas Boehler   *
93913b16484SAndreas Boehler   * @return boolean true on success, otherwise false
94013b16484SAndreas Boehler   */
94113b16484SAndreas Boehler  public function enableCalendarForPage($id)
94213b16484SAndreas Boehler  {
94313b16484SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
94413b16484SAndreas Boehler      if($calid === false)
94513b16484SAndreas Boehler        return false;
9465f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
9475f2c3e2dSAndreas Boehler      if(!$sqlite)
9485f2c3e2dSAndreas Boehler        return false;
94913b16484SAndreas Boehler      $query = "UPDATE calendars SET disabled = 0 WHERE id = ?";
9505f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
95113b16484SAndreas Boehler      if($res !== false)
95213b16484SAndreas Boehler        return true;
95313b16484SAndreas Boehler      return false;
95413b16484SAndreas Boehler  }
95513b16484SAndreas Boehler
95613b16484SAndreas Boehler  /**
957cb71a62aSAndreas Boehler   * Retrieve all events that are within a given date range,
958cb71a62aSAndreas Boehler   * based on the timezone setting.
959cb71a62aSAndreas Boehler   *
960cb71a62aSAndreas Boehler   * There is also support for retrieving recurring events,
961cb71a62aSAndreas Boehler   * using Sabre's VObject Iterator. Recurring events are represented
962cb71a62aSAndreas Boehler   * as individual calendar entries with the same UID.
963cb71a62aSAndreas Boehler   *
964cb71a62aSAndreas Boehler   * @param string $id The page ID to work with
965cb71a62aSAndreas Boehler   * @param string $user The user ID to work with
966cb71a62aSAndreas Boehler   * @param string $startDate The start date as a string
967cb71a62aSAndreas Boehler   * @param string $endDate The end date as a string
9684a2bf5eeSAndreas Boehler   * @param string $color (optional) The calendar's color
969cb71a62aSAndreas Boehler   *
970cb71a62aSAndreas Boehler   * @return array An array containing the calendar entries.
971cb71a62aSAndreas Boehler   */
9724a2bf5eeSAndreas Boehler  public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null)
973a1a3b679SAndreas Boehler  {
97482a48dfbSAndreas Boehler      if($timezone !== '' && $timezone !== 'local')
97582a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($timezone);
976bd883736SAndreas Boehler      else
977bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
978a1a3b679SAndreas Boehler      $data = array();
979cb71a62aSAndreas Boehler
9805f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
9815f2c3e2dSAndreas Boehler      if(!$sqlite)
9825f2c3e2dSAndreas Boehler        return false;
9835f2c3e2dSAndreas Boehler
984a469597cSAndreas Boehler      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?";
985a469597cSAndreas Boehler      $startTs = null;
986a469597cSAndreas Boehler      $endTs = null;
987a469597cSAndreas Boehler      if($startDate !== null)
988a469597cSAndreas Boehler      {
989a1a3b679SAndreas Boehler        $startTs = new \DateTime($startDate);
9905f2c3e2dSAndreas Boehler        $query .= " AND lastoccurence > ".$sqlite->quote_string($startTs->getTimestamp());
991a469597cSAndreas Boehler      }
992a469597cSAndreas Boehler      if($endDate !== null)
993a469597cSAndreas Boehler      {
994a1a3b679SAndreas Boehler        $endTs = new \DateTime($endDate);
9955f2c3e2dSAndreas Boehler        $query .= " AND firstoccurence < ".$sqlite->quote_string($endTs->getTimestamp());
996a469597cSAndreas Boehler      }
997cb71a62aSAndreas Boehler
9980b805092SAndreas Boehler      // Load SabreDAV
9990b805092SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
10000b805092SAndreas Boehler
10010b805092SAndreas Boehler      if(strpos($id, 'webdav://') === 0)
10020b805092SAndreas Boehler      {
10030b805092SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
10040b805092SAndreas Boehler          if(is_null($wdc))
10050b805092SAndreas Boehler            return $data;
10060b805092SAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
10070b805092SAndreas Boehler          $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate);
10080b805092SAndreas Boehler      }
10090b805092SAndreas Boehler      else
10100b805092SAndreas Boehler      {
10110b805092SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
10120b805092SAndreas Boehler          if(is_null($color))
10130b805092SAndreas Boehler            $color = $this->getCalendarColorForCalendar($calid);
10140b805092SAndreas Boehler
101559b68239SAndreas Boehler          $enabled = $this->getCalendarStatus($calid);
101659b68239SAndreas Boehler          if($enabled === false)
101759b68239SAndreas Boehler            return $data;
101859b68239SAndreas Boehler
1019cb71a62aSAndreas Boehler          // Retrieve matching calendar objects
10205f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $calid);
10215f2c3e2dSAndreas Boehler          $arr = $sqlite->res2arr($res);
10220b805092SAndreas Boehler      }
1023cb71a62aSAndreas Boehler
1024cb71a62aSAndreas Boehler      // Parse individual calendar entries
1025a1a3b679SAndreas Boehler      foreach($arr as $row)
1026a1a3b679SAndreas Boehler      {
1027a1a3b679SAndreas Boehler          if(isset($row['calendardata']))
1028a1a3b679SAndreas Boehler          {
1029b269830cSAndreas Boehler              $entry = array();
1030a1a3b679SAndreas Boehler              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
1031ebc4eb57SAndreas Boehler              $recurrence = $vcal->VEVENT->RRULE;
1032cb71a62aSAndreas Boehler              // If it is a recurring event, pass it through Sabre's EventIterator
1033ebc4eb57SAndreas Boehler              if($recurrence != null)
1034ebc4eb57SAndreas Boehler              {
1035ebc4eb57SAndreas Boehler                  $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
1036ebc4eb57SAndreas Boehler                  $rEvents->rewind();
1037e9b7d302SAndreas Boehler                  while($rEvents->valid())
1038ebc4eb57SAndreas Boehler                  {
1039ebc4eb57SAndreas Boehler                      $event = $rEvents->getEventObject();
1040cb71a62aSAndreas Boehler                      // If we are after the given time range, exit
1041a469597cSAndreas Boehler                      if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()))
1042e9b7d302SAndreas Boehler                          break;
1043cb71a62aSAndreas Boehler
1044cb71a62aSAndreas Boehler                      // If we are before the given time range, continue
1045a469597cSAndreas Boehler                      if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()))
1046ebc4eb57SAndreas Boehler                      {
1047ebc4eb57SAndreas Boehler                          $rEvents->next();
1048ebc4eb57SAndreas Boehler                          continue;
1049ebc4eb57SAndreas Boehler                      }
1050cb71a62aSAndreas Boehler
1051cb71a62aSAndreas Boehler                      // If we are within the given time range, parse the event
1052185e2535SAndreas Boehler                      $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
1053ebc4eb57SAndreas Boehler                      $rEvents->next();
1054ebc4eb57SAndreas Boehler                  }
1055ebc4eb57SAndreas Boehler              }
1056ebc4eb57SAndreas Boehler              else
1057185e2535SAndreas Boehler                $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
1058ebc4eb57SAndreas Boehler          }
1059ebc4eb57SAndreas Boehler      }
1060ebc4eb57SAndreas Boehler      return $data;
1061ebc4eb57SAndreas Boehler  }
1062ebc4eb57SAndreas Boehler
1063cb71a62aSAndreas Boehler  /**
1064cb71a62aSAndreas Boehler   * Helper function that parses the iCal data of a VEVENT to a calendar entry.
1065cb71a62aSAndreas Boehler   *
1066cb71a62aSAndreas Boehler   * @param \Sabre\VObject\VEvent $event The event to parse
1067cb71a62aSAndreas Boehler   * @param \DateTimeZone $timezone The timezone object
1068cb71a62aSAndreas Boehler   * @param string $uid The entry's UID
10693c86dda8SAndreas Boehler   * @param boolean $recurring (optional) Set to true to define a recurring event
1070cb71a62aSAndreas Boehler   *
1071cb71a62aSAndreas Boehler   * @return array The parse calendar entry
1072cb71a62aSAndreas Boehler   */
1073185e2535SAndreas Boehler  private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
1074ebc4eb57SAndreas Boehler  {
1075ebc4eb57SAndreas Boehler      $entry = array();
1076ebc4eb57SAndreas Boehler      $start = $event->DTSTART;
1077cb71a62aSAndreas Boehler      // Parse only if the start date/time is present
1078b269830cSAndreas Boehler      if($start !== null)
1079b269830cSAndreas Boehler      {
1080b269830cSAndreas Boehler        $dtStart = $start->getDateTime();
1081b269830cSAndreas Boehler        $dtStart->setTimezone($timezone);
1082bf0ad2b4SAndreas Boehler
1083bf0ad2b4SAndreas Boehler        // moment.js doesn't like times be given even if
1084bf0ad2b4SAndreas Boehler        // allDay is set to true
1085bf0ad2b4SAndreas Boehler        // This should fix T23
1086b269830cSAndreas Boehler        if($start['VALUE'] == 'DATE')
1087bf0ad2b4SAndreas Boehler        {
1088b269830cSAndreas Boehler          $entry['allDay'] = true;
1089bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format("Y-m-d");
1090bf0ad2b4SAndreas Boehler        }
1091b269830cSAndreas Boehler        else
1092bf0ad2b4SAndreas Boehler        {
1093b269830cSAndreas Boehler          $entry['allDay'] = false;
1094bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format(\DateTime::ATOM);
1095bf0ad2b4SAndreas Boehler        }
1096b269830cSAndreas Boehler      }
1097ebc4eb57SAndreas Boehler      $end = $event->DTEND;
1098bf0ad2b4SAndreas Boehler      // Parse only if the end date/time is present
1099b269830cSAndreas Boehler      if($end !== null)
1100b269830cSAndreas Boehler      {
1101b269830cSAndreas Boehler        $dtEnd = $end->getDateTime();
1102b269830cSAndreas Boehler        $dtEnd->setTimezone($timezone);
1103bf0ad2b4SAndreas Boehler        if($end['VALUE'] == 'DATE')
1104bf0ad2b4SAndreas Boehler          $entry['end'] = $dtEnd->format("Y-m-d");
1105bf0ad2b4SAndreas Boehler        else
1106b269830cSAndreas Boehler          $entry['end'] = $dtEnd->format(\DateTime::ATOM);
1107b269830cSAndreas 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 = ?";
11435f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $uid);
11445f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
1145a1a3b679SAndreas Boehler      return $row;
1146a1a3b679SAndreas Boehler  }
1147a1a3b679SAndreas Boehler
1148cb71a62aSAndreas Boehler  /**
1149d5703f5aSAndreas Boehler   * Retrieve information of a calendar's object, not including the actual
115059b68239SAndreas Boehler   * calendar data! This is mainly needed for the sync support.
1151d5703f5aSAndreas Boehler   *
1152d5703f5aSAndreas Boehler   * @param int $calid The calendar ID
1153d5703f5aSAndreas Boehler   *
1154d5703f5aSAndreas Boehler   * @return mixed The result
1155d5703f5aSAndreas Boehler   */
1156d5703f5aSAndreas Boehler  public function getCalendarObjects($calid)
1157d5703f5aSAndreas Boehler  {
11585f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
11595f2c3e2dSAndreas Boehler      if(!$sqlite)
11605f2c3e2dSAndreas Boehler        return false;
1161d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?";
11625f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
11635f2c3e2dSAndreas Boehler      $arr = $sqlite->res2arr($res);
1164d5703f5aSAndreas Boehler      return $arr;
1165d5703f5aSAndreas Boehler  }
1166d5703f5aSAndreas Boehler
1167d5703f5aSAndreas Boehler  /**
1168d5703f5aSAndreas Boehler   * Retrieve a single calendar object by calendar ID and URI
1169d5703f5aSAndreas Boehler   *
1170d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1171d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1172d5703f5aSAndreas Boehler   *
1173d5703f5aSAndreas Boehler   * @return mixed The result
1174d5703f5aSAndreas Boehler   */
1175d5703f5aSAndreas Boehler  public function getCalendarObjectByUri($calid, $uri)
1176d5703f5aSAndreas Boehler  {
11775f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
11785f2c3e2dSAndreas Boehler      if(!$sqlite)
11795f2c3e2dSAndreas Boehler        return false;
1180d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?";
11815f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid, $uri);
11825f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
1183d5703f5aSAndreas Boehler      return $row;
1184d5703f5aSAndreas Boehler  }
1185d5703f5aSAndreas Boehler
1186d5703f5aSAndreas Boehler  /**
1187d5703f5aSAndreas Boehler   * Retrieve several calendar objects by specifying an array of URIs.
1188d5703f5aSAndreas Boehler   * This is mainly neede for sync.
1189d5703f5aSAndreas Boehler   *
1190d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1191d5703f5aSAndreas Boehler   * @param array $uris An array of URIs
1192d5703f5aSAndreas Boehler   *
1193d5703f5aSAndreas Boehler   * @return mixed The result
1194d5703f5aSAndreas Boehler   */
1195d5703f5aSAndreas Boehler  public function getMultipleCalendarObjectsByUri($calid, $uris)
1196d5703f5aSAndreas Boehler  {
11975f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
11985f2c3e2dSAndreas Boehler      if(!$sqlite)
11995f2c3e2dSAndreas Boehler        return false;
1200d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN (";
1201d5703f5aSAndreas Boehler      // Inserting a whole bunch of question marks
1202d5703f5aSAndreas Boehler      $query .= implode(',', array_fill(0, count($uris), '?'));
1203d5703f5aSAndreas Boehler      $query .= ')';
1204d5703f5aSAndreas Boehler      $vals = array_merge(array($calid), $uris);
1205d5703f5aSAndreas Boehler
12065f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $vals);
12075f2c3e2dSAndreas Boehler      $arr = $sqlite->res2arr($res);
1208d5703f5aSAndreas Boehler      return $arr;
1209d5703f5aSAndreas Boehler  }
1210d5703f5aSAndreas Boehler
1211d5703f5aSAndreas Boehler  /**
1212cb71a62aSAndreas Boehler   * Retrieve all calendar events for a given calendar ID
1213cb71a62aSAndreas Boehler   *
1214cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1215cb71a62aSAndreas Boehler   *
1216cb71a62aSAndreas Boehler   * @return array An array containing all calendar data
1217cb71a62aSAndreas Boehler   */
1218f69bb449SAndreas Boehler  public function getAllCalendarEvents($calid)
1219f69bb449SAndreas Boehler  {
122059b68239SAndreas Boehler      $enabled = $this->getCalendarStatus($calid);
122159b68239SAndreas Boehler      if($enabled === false)
122259b68239SAndreas Boehler        return false;
12235f2c3e2dSAndreas Boehler
12245f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
12255f2c3e2dSAndreas Boehler      if(!$sqlite)
12265f2c3e2dSAndreas Boehler        return false;
12277e0b8590SAndreas Boehler      $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?";
12285f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
12295f2c3e2dSAndreas Boehler      $arr = $sqlite->res2arr($res);
1230f69bb449SAndreas Boehler      return $arr;
1231f69bb449SAndreas Boehler  }
1232f69bb449SAndreas Boehler
1233cb71a62aSAndreas Boehler  /**
1234cb71a62aSAndreas Boehler   * Edit a calendar entry for a page, given by its parameters.
1235cb71a62aSAndreas Boehler   * The params array has the same format as @see addCalendarEntryForPage
1236cb71a62aSAndreas Boehler   *
1237cb71a62aSAndreas Boehler   * @param string $id The page's ID to work on
1238cb71a62aSAndreas Boehler   * @param string $user The user's ID to work on
1239cb71a62aSAndreas Boehler   * @param array $params The parameter array for the edited calendar event
1240cb71a62aSAndreas Boehler   *
1241cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1242cb71a62aSAndreas Boehler   */
1243a1a3b679SAndreas Boehler  public function editCalendarEntryForPage($id, $user, $params)
1244a1a3b679SAndreas Boehler  {
124582a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
124682a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
124782a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
1248a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
1249bd883736SAndreas Boehler      else
1250bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
1251cb71a62aSAndreas Boehler
1252cb71a62aSAndreas Boehler      // Parse dates
1253b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
1254b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
1255b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
1256b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
1257cb71a62aSAndreas Boehler
1258cb71a62aSAndreas Boehler      // Retrieve the existing event based on the UID
125955a741c0SAndreas Boehler      $uid = $params['uid'];
1260809cb0faSAndreas Boehler
1261809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1262809cb0faSAndreas Boehler      {
1263809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1264809cb0faSAndreas Boehler        if(is_null($wdc))
1265809cb0faSAndreas Boehler          return false;
1266809cb0faSAndreas Boehler        $event = $wdc->getCalendarEntryByUid($uid);
1267809cb0faSAndreas Boehler      }
1268809cb0faSAndreas Boehler      else
1269809cb0faSAndreas Boehler      {
127055a741c0SAndreas Boehler        $event = $this->getEventWithUid($uid);
1271809cb0faSAndreas Boehler      }
1272cb71a62aSAndreas Boehler
1273cb71a62aSAndreas Boehler      // Load SabreDAV
12749bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1275a1a3b679SAndreas Boehler      if(!isset($event['calendardata']))
1276a1a3b679SAndreas Boehler        return false;
127755a741c0SAndreas Boehler      $uri = $event['uri'];
127855a741c0SAndreas Boehler      $calid = $event['calendarid'];
1279cb71a62aSAndreas Boehler
1280cb71a62aSAndreas Boehler      // Parse the existing event
1281a1a3b679SAndreas Boehler      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
1282b269830cSAndreas Boehler      $vevent = $vcal->VEVENT;
1283cb71a62aSAndreas Boehler
1284cb71a62aSAndreas Boehler      // Set the new event values
1285b269830cSAndreas Boehler      $vevent->summary = $params['eventname'];
1286b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
12870eebc909SAndreas Boehler      $description = $params['eventdescription'];
12882b7be5bdSAndreas Boehler      $location = $params['eventlocation'];
1289cb71a62aSAndreas Boehler
1290cb71a62aSAndreas Boehler      // Remove existing timestamps to overwrite them
12910eebc909SAndreas Boehler      $vevent->remove('DESCRIPTION');
1292b269830cSAndreas Boehler      $vevent->remove('DTSTAMP');
1293b269830cSAndreas Boehler      $vevent->remove('LAST-MODIFIED');
12944ecb526cSAndreas Boehler      $vevent->remove('ATTACH');
12952b7be5bdSAndreas Boehler      $vevent->remove('LOCATION');
1296cb71a62aSAndreas Boehler
12972b7be5bdSAndreas Boehler      // Add new time stamps, description and location
1298b269830cSAndreas Boehler      $vevent->add('DTSTAMP', $dtStamp);
1299b269830cSAndreas Boehler      $vevent->add('LAST-MODIFIED', $dtStamp);
13000eebc909SAndreas Boehler      if($description !== '')
13010eebc909SAndreas Boehler        $vevent->add('DESCRIPTION', $description);
13022b7be5bdSAndreas Boehler      if($location !== '')
13032b7be5bdSAndreas Boehler        $vevent->add('LOCATION', $location);
1304cb71a62aSAndreas Boehler
13054ecb526cSAndreas Boehler      // Add attachments
13064ecb526cSAndreas Boehler      $attachments = $params['attachments'];
130782a48dfbSAndreas Boehler      if(!is_null($attachments))
13084ecb526cSAndreas Boehler        foreach($attachments as $attachment)
13094ecb526cSAndreas Boehler          $vevent->add('ATTACH', $attachment);
13104ecb526cSAndreas Boehler
1311cb71a62aSAndreas Boehler      // Setup DTSTART
1312b269830cSAndreas Boehler      $dtStart = new \DateTime();
1313a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
1314b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
1315b269830cSAndreas Boehler      if($params['allday'] != '1')
1316b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
1317cb71a62aSAndreas Boehler
13184ecb526cSAndreas Boehler      // Setup DTEND
1319b269830cSAndreas Boehler      $dtEnd = new \DateTime();
1320a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
1321b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
1322b269830cSAndreas Boehler      if($params['allday'] != '1')
1323b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
1324cb71a62aSAndreas Boehler
1325b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
1326b269830cSAndreas Boehler      if($params['allday'] == '1')
1327b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
1328b269830cSAndreas Boehler      $vevent->remove('DTSTART');
1329b269830cSAndreas Boehler      $vevent->remove('DTEND');
1330b269830cSAndreas Boehler      $dtStartEv = $vevent->add('DTSTART', $dtStart);
1331b269830cSAndreas Boehler      $dtEndEv = $vevent->add('DTEND', $dtEnd);
1332cb71a62aSAndreas Boehler
1333cb71a62aSAndreas Boehler      // Remove the time for allday events
1334b269830cSAndreas Boehler      if($params['allday'] == '1')
1335b269830cSAndreas Boehler      {
1336b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
1337b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
1338b269830cSAndreas Boehler      }
1339a1a3b679SAndreas Boehler      $eventStr = $vcal->serialize();
1340809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1341809cb0faSAndreas Boehler      {
1342809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
1343809cb0faSAndreas Boehler          return $wdc->editCalendarEntry($connectionId, $uid, $eventStr);
1344809cb0faSAndreas Boehler      }
1345809cb0faSAndreas Boehler      else
1346809cb0faSAndreas Boehler      {
13475f2c3e2dSAndreas Boehler          $sqlite = $this->getDB();
13485f2c3e2dSAndreas Boehler          if(!$sqlite)
13495f2c3e2dSAndreas Boehler            return false;
1350809cb0faSAndreas Boehler          $now = new DateTime();
1351cb71a62aSAndreas Boehler          // Actually write to the database
135251f4febbSAndreas Boehler          $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ".
135351f4febbSAndreas Boehler                   "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?";
13545f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(),
135551f4febbSAndreas Boehler                                      $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid);
135655a741c0SAndreas Boehler          if($res !== false)
135755a741c0SAndreas Boehler          {
135855a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'modified');
1359a1a3b679SAndreas Boehler              return true;
1360a1a3b679SAndreas Boehler          }
1361809cb0faSAndreas Boehler      }
136255a741c0SAndreas Boehler      return false;
136355a741c0SAndreas Boehler  }
1364a1a3b679SAndreas Boehler
1365cb71a62aSAndreas Boehler  /**
1366d5703f5aSAndreas Boehler   * Delete an event from a calendar by calendar ID and URI
1367d5703f5aSAndreas Boehler   *
1368d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1369d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1370d5703f5aSAndreas Boehler   *
1371d5703f5aSAndreas Boehler   * @return true
1372d5703f5aSAndreas Boehler   */
1373d5703f5aSAndreas Boehler  public function deleteCalendarEntryForCalendarByUri($calid, $uri)
1374d5703f5aSAndreas Boehler  {
13755f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
13765f2c3e2dSAndreas Boehler      if(!$sqlite)
13775f2c3e2dSAndreas Boehler        return false;
1378d5703f5aSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?";
13795f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid, $uri);
1380d5703f5aSAndreas Boehler      if($res !== false)
1381d5703f5aSAndreas Boehler      {
1382d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
1383d5703f5aSAndreas Boehler      }
1384d5703f5aSAndreas Boehler      return true;
1385d5703f5aSAndreas Boehler  }
1386d5703f5aSAndreas Boehler
1387d5703f5aSAndreas Boehler  /**
1388cb71a62aSAndreas Boehler   * Delete a calendar entry for a given page. Actually, the event is removed
1389cb71a62aSAndreas Boehler   * based on the entry's UID, so that page ID is no used.
1390cb71a62aSAndreas Boehler   *
1391cb71a62aSAndreas Boehler   * @param string $id The page's ID (unused)
1392cb71a62aSAndreas Boehler   * @param array $params The parameter array to work with
1393cb71a62aSAndreas Boehler   *
1394cb71a62aSAndreas Boehler   * @return boolean True
1395cb71a62aSAndreas Boehler   */
1396a1a3b679SAndreas Boehler  public function deleteCalendarEntryForPage($id, $params)
1397a1a3b679SAndreas Boehler  {
1398a1a3b679SAndreas Boehler      $uid = $params['uid'];
1399809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1400809cb0faSAndreas Boehler      {
1401809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1402809cb0faSAndreas Boehler        if(is_null($wdc))
1403809cb0faSAndreas Boehler          return false;
1404809cb0faSAndreas Boehler        $connectionId = str_replace('webdav://', '', $id);
1405809cb0faSAndreas Boehler        $result = $wdc->deleteCalendarEntry($connectionId, $uid);
1406809cb0faSAndreas Boehler        return $result;
1407809cb0faSAndreas Boehler      }
14085f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
14095f2c3e2dSAndreas Boehler      if(!$sqlite)
14105f2c3e2dSAndreas Boehler        return false;
141155a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
14122c14b82bSAndreas Boehler      $calid = $event['calendarid'];
141355a741c0SAndreas Boehler      $uri = $event['uri'];
141451f4febbSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE uid = ?";
14155f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $uid);
141655a741c0SAndreas Boehler      if($res !== false)
141755a741c0SAndreas Boehler      {
141855a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
141955a741c0SAndreas Boehler      }
1420a1a3b679SAndreas Boehler      return true;
1421a1a3b679SAndreas Boehler  }
1422a1a3b679SAndreas Boehler
1423cb71a62aSAndreas Boehler  /**
1424cb71a62aSAndreas Boehler   * Retrieve the current sync token for a calendar
1425cb71a62aSAndreas Boehler   *
1426cb71a62aSAndreas Boehler   * @param string $calid The calendar id
1427cb71a62aSAndreas Boehler   *
1428cb71a62aSAndreas Boehler   * @return mixed The synctoken or false
1429cb71a62aSAndreas Boehler   */
143055a741c0SAndreas Boehler  public function getSyncTokenForCalendar($calid)
143155a741c0SAndreas Boehler  {
1432b269830cSAndreas Boehler      $row = $this->getCalendarSettings($calid);
143355a741c0SAndreas Boehler      if(isset($row['synctoken']))
143455a741c0SAndreas Boehler          return $row['synctoken'];
143555a741c0SAndreas Boehler      return false;
143655a741c0SAndreas Boehler  }
143755a741c0SAndreas Boehler
1438cb71a62aSAndreas Boehler  /**
1439cb71a62aSAndreas Boehler   * Helper function to convert the operation name to
1440cb71a62aSAndreas Boehler   * an operation code as stored in the database
1441cb71a62aSAndreas Boehler   *
1442cb71a62aSAndreas Boehler   * @param string $operationName The operation name
1443cb71a62aSAndreas Boehler   *
1444cb71a62aSAndreas Boehler   * @return mixed The operation code or false
1445cb71a62aSAndreas Boehler   */
144655a741c0SAndreas Boehler  public function operationNameToOperation($operationName)
144755a741c0SAndreas Boehler  {
144855a741c0SAndreas Boehler      switch($operationName)
144955a741c0SAndreas Boehler      {
145055a741c0SAndreas Boehler          case 'added':
145155a741c0SAndreas Boehler              return 1;
145255a741c0SAndreas Boehler          break;
145355a741c0SAndreas Boehler          case 'modified':
145455a741c0SAndreas Boehler              return 2;
145555a741c0SAndreas Boehler          break;
145655a741c0SAndreas Boehler          case 'deleted':
145755a741c0SAndreas Boehler              return 3;
145855a741c0SAndreas Boehler          break;
145955a741c0SAndreas Boehler      }
146055a741c0SAndreas Boehler      return false;
146155a741c0SAndreas Boehler  }
146255a741c0SAndreas Boehler
1463cb71a62aSAndreas Boehler  /**
1464cb71a62aSAndreas Boehler   * Update the sync token log based on the calendar id and the
1465cb71a62aSAndreas Boehler   * operation that was performed.
1466cb71a62aSAndreas Boehler   *
1467cb71a62aSAndreas Boehler   * @param string $calid The calendar ID that was modified
1468cb71a62aSAndreas Boehler   * @param string $uri The calendar URI that was modified
1469cb71a62aSAndreas Boehler   * @param string $operation The operation that was performed
1470cb71a62aSAndreas Boehler   *
1471cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1472cb71a62aSAndreas Boehler   */
147355a741c0SAndreas Boehler  private function updateSyncTokenLog($calid, $uri, $operation)
147455a741c0SAndreas Boehler  {
147555a741c0SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
147655a741c0SAndreas Boehler      $operationCode = $this->operationNameToOperation($operation);
147755a741c0SAndreas Boehler      if(($operationCode === false) || ($currentToken === false))
147855a741c0SAndreas Boehler          return false;
147955a741c0SAndreas Boehler      $values = array($uri,
148055a741c0SAndreas Boehler                      $currentToken,
148155a741c0SAndreas Boehler                      $calid,
148255a741c0SAndreas Boehler                      $operationCode
148355a741c0SAndreas Boehler      );
14845f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
14855f2c3e2dSAndreas Boehler      if(!$sqlite)
14865f2c3e2dSAndreas Boehler        return false;
148751f4febbSAndreas Boehler      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)";
14885f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $uri, $currentToken, $calid, $operationCode);
148955a741c0SAndreas Boehler      if($res === false)
149055a741c0SAndreas Boehler        return false;
149155a741c0SAndreas Boehler      $currentToken++;
149251f4febbSAndreas Boehler      $query = "UPDATE calendars SET synctoken = ? WHERE id = ?";
14935f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $currentToken, $calid);
149455a741c0SAndreas Boehler      return ($res !== false);
149555a741c0SAndreas Boehler  }
149655a741c0SAndreas Boehler
1497cb71a62aSAndreas Boehler  /**
1498cb71a62aSAndreas Boehler   * Return the sync URL for a given Page, i.e. a calendar
1499cb71a62aSAndreas Boehler   *
1500cb71a62aSAndreas Boehler   * @param string $id The page's ID
1501cb71a62aSAndreas Boehler   * @param string $user (optional) The user's ID
1502cb71a62aSAndreas Boehler   *
1503cb71a62aSAndreas Boehler   * @return mixed The sync url or false
1504cb71a62aSAndreas Boehler   */
1505b269830cSAndreas Boehler  public function getSyncUrlForPage($id, $user = null)
1506b269830cSAndreas Boehler  {
150734a47953SAndreas Boehler      if(is_null($userid))
150834a47953SAndreas Boehler      {
150934a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
151034a47953SAndreas Boehler        {
151134a47953SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
151234a47953SAndreas Boehler        }
151334a47953SAndreas Boehler        else
151434a47953SAndreas Boehler        {
151534a47953SAndreas Boehler          return false;
151634a47953SAndreas Boehler        }
151734a47953SAndreas Boehler      }
1518b269830cSAndreas Boehler
1519b269830cSAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1520b269830cSAndreas Boehler      if($calid === false)
1521b269830cSAndreas Boehler        return false;
1522b269830cSAndreas Boehler
1523b269830cSAndreas Boehler      $calsettings = $this->getCalendarSettings($calid);
1524b269830cSAndreas Boehler      if(!isset($calsettings['uri']))
1525b269830cSAndreas Boehler        return false;
1526b269830cSAndreas Boehler
1527b269830cSAndreas Boehler      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
1528b269830cSAndreas Boehler      return $syncurl;
1529b269830cSAndreas Boehler  }
1530b269830cSAndreas Boehler
1531cb71a62aSAndreas Boehler  /**
1532cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given page
1533cb71a62aSAndreas Boehler   *
1534cb71a62aSAndreas Boehler   * @param string $id the page ID
1535cb71a62aSAndreas Boehler   *
1536cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1537cb71a62aSAndreas Boehler   */
1538f69bb449SAndreas Boehler  public function getPrivateURLForPage($id)
1539f69bb449SAndreas Boehler  {
1540f69bb449SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1541f69bb449SAndreas Boehler      if($calid === false)
1542f69bb449SAndreas Boehler        return false;
1543f69bb449SAndreas Boehler
1544f69bb449SAndreas Boehler      return $this->getPrivateURLForCalendar($calid);
1545f69bb449SAndreas Boehler  }
1546f69bb449SAndreas Boehler
1547cb71a62aSAndreas Boehler  /**
1548cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given calendar ID
1549cb71a62aSAndreas Boehler   *
1550cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1551cb71a62aSAndreas Boehler   *
1552cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1553cb71a62aSAndreas Boehler   */
1554f69bb449SAndreas Boehler  public function getPrivateURLForCalendar($calid)
1555f69bb449SAndreas Boehler  {
1556185e2535SAndreas Boehler      if(isset($this->cachedValues['privateurl'][$calid]))
1557185e2535SAndreas Boehler        return $this->cachedValues['privateurl'][$calid];
15585f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
15595f2c3e2dSAndreas Boehler      if(!$sqlite)
15605f2c3e2dSAndreas Boehler        return false;
156151f4febbSAndreas Boehler      $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?";
15625f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $calid);
15635f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
1564f69bb449SAndreas Boehler      if(!isset($row['url']))
1565f69bb449SAndreas Boehler      {
1566f69bb449SAndreas Boehler          $url = uniqid("dokuwiki-").".ics";
156751f4febbSAndreas Boehler          $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)";
15685f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $url, $calid);
1569f69bb449SAndreas Boehler          if($res === false)
1570f69bb449SAndreas Boehler            return false;
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  /**
1583cb71a62aSAndreas Boehler   * Retrieve the calendar ID for a given private calendar URL
1584cb71a62aSAndreas Boehler   *
1585cb71a62aSAndreas Boehler   * @param string $url The private URL
1586cb71a62aSAndreas Boehler   *
1587cb71a62aSAndreas Boehler   * @return mixed The calendar ID or false
1588cb71a62aSAndreas Boehler   */
1589f69bb449SAndreas Boehler  public function getCalendarForPrivateURL($url)
1590f69bb449SAndreas Boehler  {
15915f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
15925f2c3e2dSAndreas Boehler      if(!$sqlite)
15935f2c3e2dSAndreas Boehler        return false;
159451f4febbSAndreas Boehler      $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?";
15955f2c3e2dSAndreas Boehler      $res = $sqlite->query($query, $url);
15965f2c3e2dSAndreas Boehler      $row = $sqlite->res2row($res);
1597f69bb449SAndreas Boehler      if(!isset($row['calid']))
1598f69bb449SAndreas Boehler        return false;
1599f69bb449SAndreas Boehler      return $row['calid'];
1600f69bb449SAndreas Boehler  }
1601f69bb449SAndreas Boehler
1602cb71a62aSAndreas Boehler  /**
1603cb71a62aSAndreas Boehler   * Return a given calendar as ICS feed, i.e. all events in one ICS file.
1604cb71a62aSAndreas Boehler   *
16057e0b8590SAndreas Boehler   * @param string $calid The calendar ID to retrieve
1606cb71a62aSAndreas Boehler   *
1607cb71a62aSAndreas Boehler   * @return mixed The calendar events as string or false
1608cb71a62aSAndreas Boehler   */
1609f69bb449SAndreas Boehler  public function getCalendarAsICSFeed($calid)
1610f69bb449SAndreas Boehler  {
1611f69bb449SAndreas Boehler      $calSettings = $this->getCalendarSettings($calid);
1612f69bb449SAndreas Boehler      if($calSettings === false)
1613f69bb449SAndreas Boehler        return false;
1614f69bb449SAndreas Boehler      $events = $this->getAllCalendarEvents($calid);
1615f69bb449SAndreas Boehler      if($events === false)
1616f69bb449SAndreas Boehler        return false;
1617f69bb449SAndreas Boehler
16187e0b8590SAndreas Boehler      // Load SabreDAV
16197e0b8590SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
16207e0b8590SAndreas Boehler      $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:";
16217e0b8590SAndreas Boehler      $out .= $calSettings['displayname']."\r\n";
1622f69bb449SAndreas Boehler      foreach($events as $event)
1623f69bb449SAndreas Boehler      {
16247e0b8590SAndreas Boehler          $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
16257e0b8590SAndreas Boehler          $evt = $vcal->VEVENT;
16267e0b8590SAndreas Boehler          $out .= $evt->serialize();
1627f69bb449SAndreas Boehler      }
16287e0b8590SAndreas Boehler      $out .= "END:VCALENDAR\r\n";
1629f69bb449SAndreas Boehler      return $out;
1630f69bb449SAndreas Boehler  }
1631f69bb449SAndreas Boehler
16327c7c6b0bSAndreas Boehler  /**
16337c7c6b0bSAndreas Boehler   * Retrieve a configuration option for the plugin
16347c7c6b0bSAndreas Boehler   *
16357c7c6b0bSAndreas Boehler   * @param string $key The key to query
163621d04f73SAndreas Boehler   * @return mixed The option set, null if not found
16377c7c6b0bSAndreas Boehler   */
16387c7c6b0bSAndreas Boehler  public function getConfig($key)
16397c7c6b0bSAndreas Boehler  {
16407c7c6b0bSAndreas Boehler      return $this->getConf($key);
16417c7c6b0bSAndreas Boehler  }
16427c7c6b0bSAndreas Boehler
1643d5703f5aSAndreas Boehler  /**
1644d5703f5aSAndreas Boehler   * Parses some information from calendar objects, used for optimized
1645d5703f5aSAndreas Boehler   * calendar-queries. Taken nearly unmodified from Sabre's PDO backend
1646d5703f5aSAndreas Boehler   *
1647d5703f5aSAndreas Boehler   * Returns an array with the following keys:
1648d5703f5aSAndreas Boehler   *   * etag - An md5 checksum of the object without the quotes.
1649d5703f5aSAndreas Boehler   *   * size - Size of the object in bytes
1650d5703f5aSAndreas Boehler   *   * componentType - VEVENT, VTODO or VJOURNAL
1651d5703f5aSAndreas Boehler   *   * firstOccurence
1652d5703f5aSAndreas Boehler   *   * lastOccurence
1653d5703f5aSAndreas Boehler   *   * uid - value of the UID property
1654d5703f5aSAndreas Boehler   *
1655d5703f5aSAndreas Boehler   * @param string $calendarData
1656d5703f5aSAndreas Boehler   * @return array
1657d5703f5aSAndreas Boehler   */
1658d5703f5aSAndreas Boehler  protected function getDenormalizedData($calendarData)
1659d5703f5aSAndreas Boehler  {
1660d5703f5aSAndreas Boehler    require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1661d5703f5aSAndreas Boehler
1662d5703f5aSAndreas Boehler    $vObject = \Sabre\VObject\Reader::read($calendarData);
1663d5703f5aSAndreas Boehler    $componentType = null;
1664d5703f5aSAndreas Boehler    $component = null;
1665d5703f5aSAndreas Boehler    $firstOccurence = null;
1666d5703f5aSAndreas Boehler    $lastOccurence = null;
1667d5703f5aSAndreas Boehler    $uid = null;
1668d5703f5aSAndreas Boehler    foreach ($vObject->getComponents() as $component)
1669d5703f5aSAndreas Boehler    {
1670d5703f5aSAndreas Boehler        if ($component->name !== 'VTIMEZONE')
1671d5703f5aSAndreas Boehler        {
1672d5703f5aSAndreas Boehler            $componentType = $component->name;
1673d5703f5aSAndreas Boehler            $uid = (string)$component->UID;
1674d5703f5aSAndreas Boehler            break;
1675d5703f5aSAndreas Boehler        }
1676d5703f5aSAndreas Boehler    }
1677d5703f5aSAndreas Boehler    if (!$componentType)
1678d5703f5aSAndreas Boehler    {
1679d5703f5aSAndreas Boehler        return false;
1680d5703f5aSAndreas Boehler    }
1681d5703f5aSAndreas Boehler    if ($componentType === 'VEVENT')
1682d5703f5aSAndreas Boehler    {
1683d5703f5aSAndreas Boehler        $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
1684d5703f5aSAndreas Boehler        // Finding the last occurence is a bit harder
1685d5703f5aSAndreas Boehler        if (!isset($component->RRULE))
1686d5703f5aSAndreas Boehler        {
1687d5703f5aSAndreas Boehler            if (isset($component->DTEND))
1688d5703f5aSAndreas Boehler            {
1689d5703f5aSAndreas Boehler                $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
1690d5703f5aSAndreas Boehler            }
1691d5703f5aSAndreas Boehler            elseif (isset($component->DURATION))
1692d5703f5aSAndreas Boehler            {
1693d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1694d5703f5aSAndreas Boehler                $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
1695d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1696d5703f5aSAndreas Boehler            }
1697d5703f5aSAndreas Boehler            elseif (!$component->DTSTART->hasTime())
1698d5703f5aSAndreas Boehler            {
1699d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1700d5703f5aSAndreas Boehler                $endDate->modify('+1 day');
1701d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1702d5703f5aSAndreas Boehler            }
1703d5703f5aSAndreas Boehler            else
1704d5703f5aSAndreas Boehler            {
1705d5703f5aSAndreas Boehler                $lastOccurence = $firstOccurence;
1706d5703f5aSAndreas Boehler            }
1707d5703f5aSAndreas Boehler        }
1708d5703f5aSAndreas Boehler        else
1709d5703f5aSAndreas Boehler        {
1710d5703f5aSAndreas Boehler            $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
1711d5703f5aSAndreas Boehler            $maxDate = new \DateTime('2038-01-01');
1712d5703f5aSAndreas Boehler            if ($it->isInfinite())
1713d5703f5aSAndreas Boehler            {
1714d5703f5aSAndreas Boehler                $lastOccurence = $maxDate->getTimeStamp();
1715d5703f5aSAndreas Boehler            }
1716d5703f5aSAndreas Boehler            else
1717d5703f5aSAndreas Boehler            {
1718d5703f5aSAndreas Boehler                $end = $it->getDtEnd();
1719d5703f5aSAndreas Boehler                while ($it->valid() && $end < $maxDate)
1720d5703f5aSAndreas Boehler                {
1721d5703f5aSAndreas Boehler                    $end = $it->getDtEnd();
1722d5703f5aSAndreas Boehler                    $it->next();
1723d5703f5aSAndreas Boehler                }
1724d5703f5aSAndreas Boehler                $lastOccurence = $end->getTimeStamp();
1725d5703f5aSAndreas Boehler            }
1726d5703f5aSAndreas Boehler        }
1727d5703f5aSAndreas Boehler    }
1728d5703f5aSAndreas Boehler
1729d5703f5aSAndreas Boehler    return array(
1730d5703f5aSAndreas Boehler        'etag'           => md5($calendarData),
1731d5703f5aSAndreas Boehler        'size'           => strlen($calendarData),
1732d5703f5aSAndreas Boehler        'componentType'  => $componentType,
1733d5703f5aSAndreas Boehler        'firstOccurence' => $firstOccurence,
1734d5703f5aSAndreas Boehler        'lastOccurence'  => $lastOccurence,
1735d5703f5aSAndreas Boehler        'uid'            => $uid,
1736d5703f5aSAndreas Boehler    );
1737d5703f5aSAndreas Boehler
1738d5703f5aSAndreas Boehler  }
1739d5703f5aSAndreas Boehler
1740d5703f5aSAndreas Boehler  /**
1741d5703f5aSAndreas Boehler   * Query a calendar by ID and taking several filters into account.
1742d5703f5aSAndreas Boehler   * This is heavily based on Sabre's PDO backend.
1743d5703f5aSAndreas Boehler   *
1744d5703f5aSAndreas Boehler   * @param int $calendarId The calendar's ID
1745d5703f5aSAndreas Boehler   * @param array $filters The filter array to apply
1746d5703f5aSAndreas Boehler   *
1747d5703f5aSAndreas Boehler   * @return mixed The result
1748d5703f5aSAndreas Boehler   */
1749d5703f5aSAndreas Boehler  public function calendarQuery($calendarId, $filters)
1750d5703f5aSAndreas Boehler  {
175139787131SAndreas Boehler    dbglog('davcal::helper::calendarQuery');
1752d5703f5aSAndreas Boehler    $componentType = null;
1753d5703f5aSAndreas Boehler    $requirePostFilter = true;
1754d5703f5aSAndreas Boehler    $timeRange = null;
17555f2c3e2dSAndreas Boehler    $sqlite = $this->getDB();
17565f2c3e2dSAndreas Boehler    if(!$sqlite)
17575f2c3e2dSAndreas Boehler      return false;
1758d5703f5aSAndreas Boehler
1759d5703f5aSAndreas Boehler    // if no filters were specified, we don't need to filter after a query
1760d5703f5aSAndreas Boehler    if (!$filters['prop-filters'] && !$filters['comp-filters'])
1761d5703f5aSAndreas Boehler    {
1762d5703f5aSAndreas Boehler        $requirePostFilter = false;
1763d5703f5aSAndreas Boehler    }
1764d5703f5aSAndreas Boehler
1765d5703f5aSAndreas Boehler    // Figuring out if there's a component filter
1766d5703f5aSAndreas Boehler    if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined'])
1767d5703f5aSAndreas Boehler    {
1768d5703f5aSAndreas Boehler        $componentType = $filters['comp-filters'][0]['name'];
1769d5703f5aSAndreas Boehler
1770d5703f5aSAndreas Boehler        // Checking if we need post-filters
1771d5703f5aSAndreas Boehler        if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters'])
1772d5703f5aSAndreas Boehler        {
1773d5703f5aSAndreas Boehler            $requirePostFilter = false;
1774d5703f5aSAndreas Boehler        }
1775d5703f5aSAndreas Boehler        // There was a time-range filter
1776d5703f5aSAndreas Boehler        if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range']))
1777d5703f5aSAndreas Boehler        {
1778d5703f5aSAndreas Boehler            $timeRange = $filters['comp-filters'][0]['time-range'];
1779d5703f5aSAndreas Boehler
1780d5703f5aSAndreas Boehler            // If start time OR the end time is not specified, we can do a
1781d5703f5aSAndreas Boehler            // 100% accurate mysql query.
1782d5703f5aSAndreas Boehler            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end']))
1783d5703f5aSAndreas Boehler            {
1784d5703f5aSAndreas Boehler                $requirePostFilter = false;
1785d5703f5aSAndreas Boehler            }
1786d5703f5aSAndreas Boehler        }
1787d5703f5aSAndreas Boehler
1788d5703f5aSAndreas Boehler    }
1789d5703f5aSAndreas Boehler
1790d5703f5aSAndreas Boehler    if ($requirePostFilter)
1791d5703f5aSAndreas Boehler    {
1792d5703f5aSAndreas Boehler        $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?";
1793d5703f5aSAndreas Boehler    }
1794d5703f5aSAndreas Boehler    else
1795d5703f5aSAndreas Boehler    {
1796d5703f5aSAndreas Boehler        $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1797d5703f5aSAndreas Boehler    }
1798d5703f5aSAndreas Boehler
1799d5703f5aSAndreas Boehler    $values = array(
1800d5703f5aSAndreas Boehler        $calendarId
1801d5703f5aSAndreas Boehler    );
1802d5703f5aSAndreas Boehler
1803d5703f5aSAndreas Boehler    if ($componentType)
1804d5703f5aSAndreas Boehler    {
1805d5703f5aSAndreas Boehler        $query .= " AND componenttype = ?";
1806d5703f5aSAndreas Boehler        $values[] = $componentType;
1807d5703f5aSAndreas Boehler    }
1808d5703f5aSAndreas Boehler
1809d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['start'])
1810d5703f5aSAndreas Boehler    {
1811d5703f5aSAndreas Boehler        $query .= " AND lastoccurence > ?";
1812d5703f5aSAndreas Boehler        $values[] = $timeRange['start']->getTimeStamp();
1813d5703f5aSAndreas Boehler    }
1814d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['end'])
1815d5703f5aSAndreas Boehler    {
1816d5703f5aSAndreas Boehler        $query .= " AND firstoccurence < ?";
1817d5703f5aSAndreas Boehler        $values[] = $timeRange['end']->getTimeStamp();
1818d5703f5aSAndreas Boehler    }
1819d5703f5aSAndreas Boehler
18205f2c3e2dSAndreas Boehler    $res = $sqlite->query($query, $values);
18215f2c3e2dSAndreas Boehler    $arr = $sqlite->res2arr($res);
1822d5703f5aSAndreas Boehler
1823d5703f5aSAndreas Boehler    $result = array();
1824d5703f5aSAndreas Boehler    foreach($arr as $row)
1825d5703f5aSAndreas Boehler    {
1826d5703f5aSAndreas Boehler        if ($requirePostFilter)
1827d5703f5aSAndreas Boehler        {
1828d5703f5aSAndreas Boehler            if (!$this->validateFilterForObject($row, $filters))
1829d5703f5aSAndreas Boehler            {
1830d5703f5aSAndreas Boehler                continue;
1831d5703f5aSAndreas Boehler            }
1832d5703f5aSAndreas Boehler        }
1833d5703f5aSAndreas Boehler        $result[] = $row['uri'];
1834d5703f5aSAndreas Boehler
1835d5703f5aSAndreas Boehler    }
1836d5703f5aSAndreas Boehler
1837d5703f5aSAndreas Boehler    return $result;
1838d5703f5aSAndreas Boehler  }
1839d5703f5aSAndreas Boehler
1840d5703f5aSAndreas Boehler  /**
1841d5703f5aSAndreas Boehler   * This method validates if a filter (as passed to calendarQuery) matches
1842d5703f5aSAndreas Boehler   * the given object. Taken from Sabre's PDO backend
1843d5703f5aSAndreas Boehler   *
1844d5703f5aSAndreas Boehler   * @param array $object
1845d5703f5aSAndreas Boehler   * @param array $filters
1846d5703f5aSAndreas Boehler   * @return bool
1847d5703f5aSAndreas Boehler   */
1848d5703f5aSAndreas Boehler  protected function validateFilterForObject($object, $filters)
1849d5703f5aSAndreas Boehler  {
1850d5703f5aSAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1851d5703f5aSAndreas Boehler      // Unfortunately, setting the 'calendardata' here is optional. If
1852d5703f5aSAndreas Boehler      // it was excluded, we actually need another call to get this as
1853d5703f5aSAndreas Boehler      // well.
1854d5703f5aSAndreas Boehler      if (!isset($object['calendardata']))
1855d5703f5aSAndreas Boehler      {
1856d5703f5aSAndreas Boehler          $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']);
1857d5703f5aSAndreas Boehler      }
1858d5703f5aSAndreas Boehler
1859d5703f5aSAndreas Boehler      $vObject = \Sabre\VObject\Reader::read($object['calendardata']);
1860d5703f5aSAndreas Boehler      $validator = new \Sabre\CalDAV\CalendarQueryValidator();
1861d5703f5aSAndreas Boehler
186239787131SAndreas Boehler      $res = $validator->validate($vObject, $filters);
186339787131SAndreas Boehler      return $res;
1864d5703f5aSAndreas Boehler
1865d5703f5aSAndreas Boehler  }
1866d5703f5aSAndreas Boehler
1867d5703f5aSAndreas Boehler  /**
1868d5703f5aSAndreas Boehler   * Retrieve changes for a given calendar based on the given syncToken.
1869d5703f5aSAndreas Boehler   *
1870d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1871d5703f5aSAndreas Boehler   * @param int $syncToken The supplied sync token
1872d5703f5aSAndreas Boehler   * @param int $syncLevel The sync level
1873d5703f5aSAndreas Boehler   * @param int $limit The limit of changes
1874d5703f5aSAndreas Boehler   *
1875d5703f5aSAndreas Boehler   * @return array The result
1876d5703f5aSAndreas Boehler   */
1877d5703f5aSAndreas Boehler  public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null)
1878d5703f5aSAndreas Boehler  {
1879d5703f5aSAndreas Boehler      // Current synctoken
1880d5703f5aSAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
1881d5703f5aSAndreas Boehler
1882d5703f5aSAndreas Boehler      if ($currentToken === false) return null;
1883d5703f5aSAndreas Boehler
1884d5703f5aSAndreas Boehler      $result = array(
1885d5703f5aSAndreas Boehler          'syncToken' => $currentToken,
1886d5703f5aSAndreas Boehler          'added'     => array(),
1887d5703f5aSAndreas Boehler          'modified'  => array(),
1888d5703f5aSAndreas Boehler          'deleted'   => array(),
1889d5703f5aSAndreas Boehler      );
18905f2c3e2dSAndreas Boehler      $sqlite = $this->getDB();
18915f2c3e2dSAndreas Boehler      if(!$sqlite)
18925f2c3e2dSAndreas Boehler        return false;
1893d5703f5aSAndreas Boehler
1894d5703f5aSAndreas Boehler      if ($syncToken)
1895d5703f5aSAndreas Boehler      {
1896d5703f5aSAndreas Boehler
1897d5703f5aSAndreas Boehler          $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
1898d5703f5aSAndreas Boehler          if ($limit > 0) $query .= " LIMIT " . (int)$limit;
1899d5703f5aSAndreas Boehler
1900d5703f5aSAndreas Boehler          // Fetching all changes
19015f2c3e2dSAndreas Boehler          $res = $sqlite->query($query, $syncToken, $currentToken, $calid);
1902d5703f5aSAndreas Boehler          if($res === false)
1903d5703f5aSAndreas Boehler              return null;
1904d5703f5aSAndreas Boehler
19055f2c3e2dSAndreas Boehler          $arr = $sqlite->res2arr($res);
1906d5703f5aSAndreas Boehler          $changes = array();
1907d5703f5aSAndreas Boehler
1908d5703f5aSAndreas Boehler          // This loop ensures that any duplicates are overwritten, only the
1909d5703f5aSAndreas Boehler          // last change on a node is relevant.
1910d5703f5aSAndreas Boehler          foreach($arr as $row)
1911d5703f5aSAndreas Boehler          {
1912d5703f5aSAndreas Boehler              $changes[$row['uri']] = $row['operation'];
1913d5703f5aSAndreas Boehler          }
1914d5703f5aSAndreas Boehler
1915d5703f5aSAndreas Boehler          foreach ($changes as $uri => $operation)
1916d5703f5aSAndreas Boehler          {
1917d5703f5aSAndreas Boehler              switch ($operation)
1918d5703f5aSAndreas Boehler              {
1919d5703f5aSAndreas Boehler                  case 1 :
1920d5703f5aSAndreas Boehler                      $result['added'][] = $uri;
1921d5703f5aSAndreas Boehler                      break;
1922d5703f5aSAndreas Boehler                  case 2 :
1923d5703f5aSAndreas Boehler                      $result['modified'][] = $uri;
1924d5703f5aSAndreas Boehler                      break;
1925d5703f5aSAndreas Boehler                  case 3 :
1926d5703f5aSAndreas Boehler                      $result['deleted'][] = $uri;
1927d5703f5aSAndreas Boehler                      break;
1928d5703f5aSAndreas Boehler              }
1929d5703f5aSAndreas Boehler
1930d5703f5aSAndreas Boehler          }
1931d5703f5aSAndreas Boehler      }
1932d5703f5aSAndreas Boehler      else
1933d5703f5aSAndreas Boehler      {
1934d5703f5aSAndreas Boehler          // No synctoken supplied, this is the initial sync.
1935d5703f5aSAndreas Boehler          $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
19365f2c3e2dSAndreas Boehler          $res = $sqlite->query($query);
19375f2c3e2dSAndreas Boehler          $arr = $sqlite->res2arr($res);
1938d5703f5aSAndreas Boehler
1939d5703f5aSAndreas Boehler          $result['added'] = $arr;
1940d5703f5aSAndreas Boehler      }
1941d5703f5aSAndreas Boehler      return $result;
1942d5703f5aSAndreas Boehler  }
1943d5703f5aSAndreas Boehler
1944a1a3b679SAndreas Boehler}
1945