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