xref: /plugin/davcal/helper.php (revision 59b682392adb931ce4baa6720b492139695dc7fd)
1a1a3b679SAndreas Boehler<?php
2a1a3b679SAndreas Boehler/**
3cb71a62aSAndreas Boehler  * Helper Class for the DAVCal plugin
4a1a3b679SAndreas Boehler  * This helper does the actual work.
5a1a3b679SAndreas Boehler  *
6a1a3b679SAndreas Boehler  */
7a1a3b679SAndreas Boehler
8a1a3b679SAndreas Boehler// must be run within Dokuwiki
9a1a3b679SAndreas Boehlerif(!defined('DOKU_INC')) die();
10a1a3b679SAndreas Boehler
11a1a3b679SAndreas Boehlerclass helper_plugin_davcal extends DokuWiki_Plugin {
12a1a3b679SAndreas Boehler
13a1a3b679SAndreas Boehler  protected $sqlite = null;
14185e2535SAndreas Boehler  protected $cachedValues = array();
15a1a3b679SAndreas Boehler
16a1a3b679SAndreas Boehler  /**
17cb71a62aSAndreas Boehler    * Constructor to load the configuration and the SQLite plugin
18a1a3b679SAndreas Boehler    */
19a1a3b679SAndreas Boehler  public function helper_plugin_davcal() {
20a1a3b679SAndreas Boehler    $this->sqlite =& plugin_load('helper', 'sqlite');
2121d04f73SAndreas Boehler    global $conf;
2221d04f73SAndreas Boehler    dbglog('---- DAVCAL helper.php init');
23a1a3b679SAndreas Boehler    if(!$this->sqlite)
24a1a3b679SAndreas Boehler    {
2521d04f73SAndreas Boehler        dbglog('This plugin requires the sqlite plugin. Please install it.');
26a1a3b679SAndreas Boehler        msg('This plugin requires the sqlite plugin. Please install it.');
27a1a3b679SAndreas Boehler        return;
28a1a3b679SAndreas Boehler    }
29a1a3b679SAndreas Boehler
30a1a3b679SAndreas Boehler    if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
31a1a3b679SAndreas Boehler    {
3221d04f73SAndreas Boehler        dbglog('Error initialising the SQLite DB for DAVCal');
33a1a3b679SAndreas Boehler        return;
34a1a3b679SAndreas Boehler    }
35a1a3b679SAndreas Boehler  }
36a1a3b679SAndreas Boehler
37cb71a62aSAndreas Boehler  /**
38185e2535SAndreas Boehler   * Retrieve meta data for a given page
39185e2535SAndreas Boehler   *
40185e2535SAndreas Boehler   * @param string $id optional The page ID
41185e2535SAndreas Boehler   * @return array The metadata
42185e2535SAndreas Boehler   */
43185e2535SAndreas Boehler  private function getMeta($id = null) {
44185e2535SAndreas Boehler    global $ID;
45185e2535SAndreas Boehler    global $INFO;
46185e2535SAndreas Boehler
47185e2535SAndreas Boehler    if ($id === null) $id = $ID;
48185e2535SAndreas Boehler
49185e2535SAndreas Boehler    if($ID === $id && $INFO['meta']) {
50185e2535SAndreas Boehler        $meta = $INFO['meta'];
51185e2535SAndreas Boehler    } else {
52185e2535SAndreas Boehler        $meta = p_get_metadata($id);
53185e2535SAndreas Boehler    }
54185e2535SAndreas Boehler
55185e2535SAndreas Boehler    return $meta;
56185e2535SAndreas Boehler  }
57185e2535SAndreas Boehler
58185e2535SAndreas Boehler  /**
59185e2535SAndreas Boehler   * Retrieve the meta data for a given page
60185e2535SAndreas Boehler   *
61185e2535SAndreas Boehler   * @param string $id optional The page ID
62185e2535SAndreas Boehler   * @return array with meta data
63185e2535SAndreas Boehler   */
64185e2535SAndreas Boehler  public function getCalendarMetaForPage($id = null)
65185e2535SAndreas Boehler  {
66185e2535SAndreas Boehler      if(is_null($id))
67185e2535SAndreas Boehler      {
68185e2535SAndreas Boehler          global $ID;
69185e2535SAndreas Boehler          $id = $ID;
70185e2535SAndreas Boehler      }
71185e2535SAndreas Boehler
72185e2535SAndreas Boehler      $meta = $this->getMeta($id);
73185e2535SAndreas Boehler      if(isset($meta['plugin_davcal']))
74185e2535SAndreas Boehler        return $meta['plugin_davcal'];
75185e2535SAndreas Boehler      else
76185e2535SAndreas Boehler        return array();
77185e2535SAndreas Boehler  }
78185e2535SAndreas Boehler
79185e2535SAndreas Boehler  /**
80d71c9934SAndreas Boehler   * Check the permission of a user for a given calendar ID
81d71c9934SAndreas Boehler   *
82d71c9934SAndreas Boehler   * @param string $id The calendar ID to check
83d71c9934SAndreas Boehler   * @return int AUTH_* constants
84d71c9934SAndreas Boehler   */
85d71c9934SAndreas Boehler  public function checkCalendarPermission($id)
86d71c9934SAndreas Boehler  {
87d71c9934SAndreas Boehler      if(strpos($page, 'webdav://') === 0)
88d71c9934SAndreas Boehler      {
89d71c9934SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
90d71c9934SAndreas Boehler          if(is_null($wdc))
91d71c9934SAndreas Boehler            return AUTH_NONE;
92d71c9934SAndreas Boehler          $connectionId = str_replace('webdav://', '', $page);
93d71c9934SAndreas Boehler          $settings = $wdc->getConnection($connectionId);
94d71c9934SAndreas Boehler          if($settings === false)
95d71c9934SAndreas Boehler            return AUTH_NONE;
96d71c9934SAndreas Boehler          if($settings['write'] === '1')
97d71c9934SAndreas Boehler            return AUTH_CREATE;
98d71c9934SAndreas Boehler          return AUTH_READ;
99d71c9934SAndreas Boehler      }
100d71c9934SAndreas Boehler      else
101d71c9934SAndreas Boehler      {
102d71c9934SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
103d71c9934SAndreas Boehler          // We return AUTH_READ if the calendar does not exist. This makes
104d71c9934SAndreas Boehler          // davcal happy when there are just included calendars
105d71c9934SAndreas Boehler          if($calid === false)
106d71c9934SAndreas Boehler            return AUTH_READ;
107d71c9934SAndreas Boehler          return auth_quickaclcheck($id);
108d71c9934SAndreas Boehler      }
109d71c9934SAndreas Boehler  }
110d71c9934SAndreas Boehler
111d71c9934SAndreas Boehler  /**
11280e1ddf7SAndreas Boehler   * Filter calendar pages and return only those where the current
11380e1ddf7SAndreas Boehler   * user has at least read permission.
11480e1ddf7SAndreas Boehler   *
11580e1ddf7SAndreas Boehler   * @param array $calendarPages Array with calendar pages to check
11680e1ddf7SAndreas Boehler   * @return array with filtered calendar pages
11780e1ddf7SAndreas Boehler   */
11880e1ddf7SAndreas Boehler  public function filterCalendarPagesByUserPermission($calendarPages)
11980e1ddf7SAndreas Boehler  {
12080e1ddf7SAndreas Boehler      $retList = array();
12180e1ddf7SAndreas Boehler      foreach($calendarPages as $page => $data)
12280e1ddf7SAndreas Boehler      {
1230b805092SAndreas Boehler          // WebDAV Connections are always readable
1240b805092SAndreas Boehler          if(strpos($page, 'webdav://') === 0)
1250b805092SAndreas Boehler          {
1260b805092SAndreas Boehler              $retList[$page] = $data;
1270b805092SAndreas Boehler          }
1280b805092SAndreas Boehler          elseif(auth_quickaclcheck($page) >= AUTH_READ)
12980e1ddf7SAndreas Boehler          {
13080e1ddf7SAndreas Boehler              $retList[$page] = $data;
13180e1ddf7SAndreas Boehler          }
13280e1ddf7SAndreas Boehler      }
13380e1ddf7SAndreas Boehler      return $retList;
13480e1ddf7SAndreas Boehler  }
13580e1ddf7SAndreas Boehler
13680e1ddf7SAndreas Boehler  /**
137185e2535SAndreas Boehler   * Get all calendar pages used by a given page
138185e2535SAndreas Boehler   * based on the stored metadata
139185e2535SAndreas Boehler   *
140185e2535SAndreas Boehler   * @param string $id optional The page id
141185e2535SAndreas Boehler   * @return mixed The pages as array or false
142185e2535SAndreas Boehler   */
143185e2535SAndreas Boehler  public function getCalendarPagesByMeta($id = null)
144185e2535SAndreas Boehler  {
145185e2535SAndreas Boehler      if(is_null($id))
146185e2535SAndreas Boehler      {
147185e2535SAndreas Boehler          global $ID;
148185e2535SAndreas Boehler          $id = $ID;
149185e2535SAndreas Boehler      }
150185e2535SAndreas Boehler
151185e2535SAndreas Boehler      $meta = $this->getCalendarMetaForPage($id);
1520b805092SAndreas Boehler
153185e2535SAndreas Boehler      if(isset($meta['id']))
154ed764890SAndreas Boehler      {
155ed764890SAndreas Boehler          // Filter the list of pages by permission
15680e1ddf7SAndreas Boehler          $pages = $this->filterCalendarPagesByUserPermission($meta['id']);
15780e1ddf7SAndreas Boehler          if(empty($pages))
158ed764890SAndreas Boehler            return false;
15980e1ddf7SAndreas Boehler          return $pages;
160ed764890SAndreas Boehler      }
161185e2535SAndreas Boehler      return false;
162185e2535SAndreas Boehler  }
163185e2535SAndreas Boehler
164185e2535SAndreas Boehler  /**
165185e2535SAndreas Boehler   * Get a list of calendar names/pages/ids/colors
166185e2535SAndreas Boehler   * for an array of page ids
167185e2535SAndreas Boehler   *
168185e2535SAndreas Boehler   * @param array $calendarPages The calendar pages to retrieve
169185e2535SAndreas Boehler   * @return array The list
170185e2535SAndreas Boehler   */
171185e2535SAndreas Boehler  public function getCalendarMapForIDs($calendarPages)
172185e2535SAndreas Boehler  {
173185e2535SAndreas Boehler      $data = array();
1744a2bf5eeSAndreas Boehler      foreach($calendarPages as $page => $color)
175185e2535SAndreas Boehler      {
1760b805092SAndreas Boehler            if(strpos($page, 'webdav://') === 0)
1770b805092SAndreas Boehler            {
1780b805092SAndreas Boehler                $wdc =& plugin_load('helper', 'webdavclient');
1790b805092SAndreas Boehler                if(is_null($wdc))
1800b805092SAndreas Boehler                    continue;
1810b805092SAndreas Boehler                $connectionId = str_replace('webdav://', '', $page);
1820b805092SAndreas Boehler                $settings = $wdc->getConnection($connectionId);
1832393a702SAndreas Boehler                if($settings === false)
1842393a702SAndreas Boehler                    continue;
1850b805092SAndreas Boehler                $name = $settings['displayname'];
186d71c9934SAndreas Boehler                $write = ($settings['write'] === '1');
1870b805092SAndreas Boehler                $calid = $connectionId;
188cd2f100dSAndreas Boehler                $color = '#3a87ad';
1890b805092SAndreas Boehler            }
1900b805092SAndreas Boehler            else
1910b805092SAndreas Boehler            {
192185e2535SAndreas Boehler                $calid = $this->getCalendarIdForPage($page);
193185e2535SAndreas Boehler                if($calid !== false)
194185e2535SAndreas Boehler                {
195185e2535SAndreas Boehler                    $settings = $this->getCalendarSettings($calid);
196185e2535SAndreas Boehler                    $name = $settings['displayname'];
197cd2f100dSAndreas Boehler                    $color = $settings['calendarcolor'];
198ed764890SAndreas Boehler                    $write = (auth_quickaclcheck($page) > AUTH_READ);
1990b805092SAndreas Boehler                }
2000b805092SAndreas Boehler                else
2010b805092SAndreas Boehler                {
2020b805092SAndreas Boehler                    continue;
2030b805092SAndreas Boehler                }
2040b805092SAndreas Boehler            }
205185e2535SAndreas Boehler            $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
206ed764890SAndreas Boehler                            'color' => $color, 'write' => $write);
207185e2535SAndreas Boehler      }
208185e2535SAndreas Boehler      return $data;
209185e2535SAndreas Boehler  }
210185e2535SAndreas Boehler
211185e2535SAndreas Boehler  /**
212185e2535SAndreas Boehler   * Get the saved calendar color for a given page.
213185e2535SAndreas Boehler   *
214185e2535SAndreas Boehler   * @param string $id optional The page ID
215185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
216185e2535SAndreas Boehler   */
217185e2535SAndreas Boehler  public function getCalendarColorForPage($id = null)
218185e2535SAndreas Boehler  {
219185e2535SAndreas Boehler      if(is_null($id))
220185e2535SAndreas Boehler      {
221185e2535SAndreas Boehler          global $ID;
222185e2535SAndreas Boehler          $id = $ID;
223185e2535SAndreas Boehler      }
224185e2535SAndreas Boehler
225185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
226185e2535SAndreas Boehler      if($calid === false)
227185e2535SAndreas Boehler        return false;
228185e2535SAndreas Boehler
229185e2535SAndreas Boehler      return $this->getCalendarColorForCalendar($calid);
230185e2535SAndreas Boehler  }
231185e2535SAndreas Boehler
232185e2535SAndreas Boehler  /**
233185e2535SAndreas Boehler   * Get the saved calendar color for a given calendar ID.
234185e2535SAndreas Boehler   *
235185e2535SAndreas Boehler   * @param string $id optional The calendar ID
236185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
237185e2535SAndreas Boehler   */
238185e2535SAndreas Boehler  public function getCalendarColorForCalendar($calid)
239185e2535SAndreas Boehler  {
240185e2535SAndreas Boehler      if(isset($this->cachedValues['calendarcolor'][$calid]))
241185e2535SAndreas Boehler        return $this->cachedValues['calendarcolor'][$calid];
242185e2535SAndreas Boehler
243185e2535SAndreas Boehler      $row = $this->getCalendarSettings($calid);
244185e2535SAndreas Boehler
245185e2535SAndreas Boehler      if(!isset($row['calendarcolor']))
246185e2535SAndreas Boehler        return false;
247185e2535SAndreas Boehler
248185e2535SAndreas Boehler      $color = $row['calendarcolor'];
249185e2535SAndreas Boehler      $this->cachedValues['calendarcolor'][$calid] = $color;
250185e2535SAndreas Boehler      return $color;
251185e2535SAndreas Boehler  }
252185e2535SAndreas Boehler
253185e2535SAndreas Boehler  /**
254e86c8dd3SAndreas Boehler   * Get the user's principal URL for iOS sync
255e86c8dd3SAndreas Boehler   * @param string $user the user name
256e86c8dd3SAndreas Boehler   * @return the URL to the principal sync
257e86c8dd3SAndreas Boehler   */
258e86c8dd3SAndreas Boehler  public function getPrincipalUrlForUser($user)
259e86c8dd3SAndreas Boehler  {
260e86c8dd3SAndreas Boehler      if(is_null($user))
261e86c8dd3SAndreas Boehler        return false;
262e86c8dd3SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user;
263e86c8dd3SAndreas Boehler      return $url;
264e86c8dd3SAndreas Boehler  }
265e86c8dd3SAndreas Boehler
266e86c8dd3SAndreas Boehler  /**
267185e2535SAndreas Boehler   * Set the calendar color for a given page.
268185e2535SAndreas Boehler   *
269185e2535SAndreas Boehler   * @param string $color The color definition
270185e2535SAndreas Boehler   * @param string $id optional The page ID
271185e2535SAndreas Boehler   * @return boolean True on success, otherwise false
272185e2535SAndreas Boehler   */
273185e2535SAndreas Boehler  public function setCalendarColorForPage($color, $id = null)
274185e2535SAndreas Boehler  {
275185e2535SAndreas Boehler      if(is_null($id))
276185e2535SAndreas Boehler      {
277185e2535SAndreas Boehler          global $ID;
278185e2535SAndreas Boehler          $id = $ID;
279185e2535SAndreas Boehler      }
280185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
281185e2535SAndreas Boehler      if($calid === false)
282185e2535SAndreas Boehler        return false;
283185e2535SAndreas Boehler
28451f4febbSAndreas Boehler      $query = "UPDATE calendars SET calendarcolor = ? ".
28551f4febbSAndreas Boehler               " WHERE id = ?";
28651f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $color, $calid);
287185e2535SAndreas Boehler      if($res !== false)
288185e2535SAndreas Boehler      {
289185e2535SAndreas Boehler        $this->cachedValues['calendarcolor'][$calid] = $color;
290185e2535SAndreas Boehler        return true;
291185e2535SAndreas Boehler      }
292185e2535SAndreas Boehler      return false;
293185e2535SAndreas Boehler  }
294185e2535SAndreas Boehler
295185e2535SAndreas Boehler  /**
296cb71a62aSAndreas Boehler   * Set the calendar name and description for a given page with a given
297cb71a62aSAndreas Boehler   * page id.
298cb71a62aSAndreas Boehler   * If the calendar doesn't exist, the calendar is created!
299cb71a62aSAndreas Boehler   *
300cb71a62aSAndreas Boehler   * @param string  $name The name of the new calendar
301cb71a62aSAndreas Boehler   * @param string  $description The description of the new calendar
302cb71a62aSAndreas Boehler   * @param string  $id (optional) The ID of the page
303cb71a62aSAndreas Boehler   * @param string  $userid The userid of the creating user
304cb71a62aSAndreas Boehler   *
305cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false.
306cb71a62aSAndreas Boehler   */
307a1a3b679SAndreas Boehler  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
308a1a3b679SAndreas Boehler  {
309a1a3b679SAndreas Boehler      if(is_null($id))
310a1a3b679SAndreas Boehler      {
311a1a3b679SAndreas Boehler          global $ID;
312a1a3b679SAndreas Boehler          $id = $ID;
313a1a3b679SAndreas Boehler      }
314a1a3b679SAndreas Boehler      if(is_null($userid))
31534a47953SAndreas Boehler      {
31634a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
31734a47953SAndreas Boehler        {
318a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
31934a47953SAndreas Boehler        }
32034a47953SAndreas Boehler        else
32134a47953SAndreas Boehler        {
32234a47953SAndreas Boehler          $userid = uniqid('davcal-');
32334a47953SAndreas Boehler        }
32434a47953SAndreas Boehler      }
325a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
326a1a3b679SAndreas Boehler      if($calid === false)
327a1a3b679SAndreas Boehler        return $this->createCalendarForPage($name, $description, $id, $userid);
328a1a3b679SAndreas Boehler
32951f4febbSAndreas Boehler      $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?";
33051f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $name, $description, $calid);
331b269830cSAndreas Boehler      if($res !== false)
332b269830cSAndreas Boehler        return true;
333b269830cSAndreas Boehler      return false;
334a1a3b679SAndreas Boehler  }
335a1a3b679SAndreas Boehler
336cb71a62aSAndreas Boehler  /**
337d5703f5aSAndreas Boehler   * Update a calendar's displayname
338d5703f5aSAndreas Boehler   *
339d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
340d5703f5aSAndreas Boehler   * @param string $name The new calendar name
341d5703f5aSAndreas Boehler   *
342d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
343d5703f5aSAndreas Boehler   */
344d5703f5aSAndreas Boehler  public function updateCalendarName($calid, $name)
345d5703f5aSAndreas Boehler  {
346d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET displayname = ? WHERE id = ?";
347d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $name);
348d5703f5aSAndreas Boehler      if($res !== false)
349d5703f5aSAndreas Boehler      {
350d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
351d5703f5aSAndreas Boehler        return true;
352d5703f5aSAndreas Boehler      }
353d5703f5aSAndreas Boehler      return false;
354d5703f5aSAndreas Boehler  }
355d5703f5aSAndreas Boehler
356d5703f5aSAndreas Boehler  /**
357d5703f5aSAndreas Boehler   * Update the calendar description
358d5703f5aSAndreas Boehler   *
359d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
360d5703f5aSAndreas Boehler   * @param string $description The new calendar's description
361d5703f5aSAndreas Boehler   *
362d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
363d5703f5aSAndreas Boehler   */
364d5703f5aSAndreas Boehler  public function updateCalendarDescription($calid, $description)
365d5703f5aSAndreas Boehler  {
366d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET description = ? WHERE id = ?";
367d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $description);
368d5703f5aSAndreas Boehler      if($res !== false)
369d5703f5aSAndreas Boehler      {
370d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
371d5703f5aSAndreas Boehler        return true;
372d5703f5aSAndreas Boehler      }
373d5703f5aSAndreas Boehler      return false;
374d5703f5aSAndreas Boehler  }
375d5703f5aSAndreas Boehler
376d5703f5aSAndreas Boehler  /**
377d5703f5aSAndreas Boehler   * Update a calendar's timezone information
378d5703f5aSAndreas Boehler   *
379d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
380d5703f5aSAndreas Boehler   * @param string $timezone The new timezone to set
381d5703f5aSAndreas Boehler   *
382d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
383d5703f5aSAndreas Boehler   */
384d5703f5aSAndreas Boehler  public function updateCalendarTimezone($calid, $timezone)
385d5703f5aSAndreas Boehler  {
386d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET timezone = ? WHERE id = ?";
387d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $timezone);
388d5703f5aSAndreas Boehler      if($res !== false)
389d5703f5aSAndreas Boehler      {
390d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
391d5703f5aSAndreas Boehler        return true;
392d5703f5aSAndreas Boehler      }
393d5703f5aSAndreas Boehler      return false;
394d5703f5aSAndreas Boehler  }
395d5703f5aSAndreas Boehler
396d5703f5aSAndreas Boehler  /**
397cb71a62aSAndreas Boehler   * Save the personal settings to the SQLite database 'calendarsettings'.
398cb71a62aSAndreas Boehler   *
399cb71a62aSAndreas Boehler   * @param array  $settings The settings array to store
400cb71a62aSAndreas Boehler   * @param string $userid (optional) The userid to store
401cb71a62aSAndreas Boehler   *
402cb71a62aSAndreas Boehler   * @param boolean True on success, otherwise false
403cb71a62aSAndreas Boehler   */
404a495d34cSAndreas Boehler  public function savePersonalSettings($settings, $userid = null)
405a495d34cSAndreas Boehler  {
406a495d34cSAndreas Boehler      if(is_null($userid))
40734a47953SAndreas Boehler      {
40834a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
40934a47953SAndreas Boehler          {
410a495d34cSAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
41134a47953SAndreas Boehler          }
41234a47953SAndreas Boehler          else
41334a47953SAndreas Boehler          {
41434a47953SAndreas Boehler              return false;
41534a47953SAndreas Boehler          }
41634a47953SAndreas Boehler      }
417a495d34cSAndreas Boehler      $this->sqlite->query("BEGIN TRANSACTION");
418a495d34cSAndreas Boehler
41951f4febbSAndreas Boehler      $query = "DELETE FROM calendarsettings WHERE userid = ?";
42051f4febbSAndreas Boehler      $this->sqlite->query($query, $userid);
421bd883736SAndreas Boehler
422a495d34cSAndreas Boehler      foreach($settings as $key => $value)
423a495d34cSAndreas Boehler      {
42451f4febbSAndreas Boehler          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)";
42551f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $userid, $key, $value);
426a495d34cSAndreas Boehler          if($res === false)
427a495d34cSAndreas Boehler              return false;
428a495d34cSAndreas Boehler      }
429a495d34cSAndreas Boehler      $this->sqlite->query("COMMIT TRANSACTION");
430185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
431a495d34cSAndreas Boehler      return true;
432a495d34cSAndreas Boehler  }
433a495d34cSAndreas Boehler
434cb71a62aSAndreas Boehler  /**
435cb71a62aSAndreas Boehler   * Retrieve the settings array for a given user id.
436cb71a62aSAndreas Boehler   * Some sane defaults are returned, currently:
437cb71a62aSAndreas Boehler   *
438cb71a62aSAndreas Boehler   *    timezone    => local
439cb71a62aSAndreas Boehler   *    weeknumbers => 0
440cb71a62aSAndreas Boehler   *    workweek    => 0
441cb71a62aSAndreas Boehler   *
442cb71a62aSAndreas Boehler   * @param string $userid (optional) The user id to retrieve
443cb71a62aSAndreas Boehler   *
444cb71a62aSAndreas Boehler   * @return array The settings array
445cb71a62aSAndreas Boehler   */
446a495d34cSAndreas Boehler  public function getPersonalSettings($userid = null)
447a495d34cSAndreas Boehler  {
448bd883736SAndreas Boehler      // Some sane default settings
449bd883736SAndreas Boehler      $settings = array(
450fb813b30SAndreas Boehler        'timezone' => $this->getConf('timezone'),
451fb813b30SAndreas Boehler        'weeknumbers' => $this->getConf('weeknumbers'),
452fb813b30SAndreas Boehler        'workweek' => $this->getConf('workweek'),
4531d5bdcd0SAndreas Boehler        'monday' => $this->getConf('monday'),
4541d5bdcd0SAndreas Boehler        'timeformat' => $this->getConf('timeformat')
455bd883736SAndreas Boehler      );
45634a47953SAndreas Boehler      if(is_null($userid))
45734a47953SAndreas Boehler      {
45834a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
45934a47953SAndreas Boehler          {
46034a47953SAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
46134a47953SAndreas Boehler          }
46234a47953SAndreas Boehler          else
46334a47953SAndreas Boehler          {
46434a47953SAndreas Boehler            return $settings;
46534a47953SAndreas Boehler          }
46634a47953SAndreas Boehler      }
46734a47953SAndreas Boehler
46834a47953SAndreas Boehler      if(isset($this->cachedValues['settings'][$userid]))
46934a47953SAndreas Boehler        return $this->cachedValues['settings'][$userid];
47051f4febbSAndreas Boehler      $query = "SELECT key, value FROM calendarsettings WHERE userid = ?";
47151f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $userid);
472a495d34cSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
473a495d34cSAndreas Boehler      foreach($arr as $row)
474a495d34cSAndreas Boehler      {
475a495d34cSAndreas Boehler          $settings[$row['key']] = $row['value'];
476a495d34cSAndreas Boehler      }
477185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
478a495d34cSAndreas Boehler      return $settings;
479a495d34cSAndreas Boehler  }
480a495d34cSAndreas Boehler
481cb71a62aSAndreas Boehler  /**
482cb71a62aSAndreas Boehler   * Retrieve the calendar ID based on a page ID from the SQLite table
483cb71a62aSAndreas Boehler   * 'pagetocalendarmapping'.
484cb71a62aSAndreas Boehler   *
485cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to retrieve the corresponding calendar
486cb71a62aSAndreas Boehler   *
487cb71a62aSAndreas Boehler   * @return mixed the ID on success, otherwise false
488cb71a62aSAndreas Boehler   */
489a1a3b679SAndreas Boehler  public function getCalendarIdForPage($id = null)
490a1a3b679SAndreas Boehler  {
491a1a3b679SAndreas Boehler      if(is_null($id))
492a1a3b679SAndreas Boehler      {
493a1a3b679SAndreas Boehler          global $ID;
494a1a3b679SAndreas Boehler          $id = $ID;
495a1a3b679SAndreas Boehler      }
496a1a3b679SAndreas Boehler
497185e2535SAndreas Boehler      if(isset($this->cachedValues['calid'][$id]))
498185e2535SAndreas Boehler        return $this->cachedValues['calid'][$id];
499185e2535SAndreas Boehler
50051f4febbSAndreas Boehler      $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?";
50151f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $id);
502a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
503a1a3b679SAndreas Boehler      if(isset($row['calid']))
504185e2535SAndreas Boehler      {
505185e2535SAndreas Boehler        $calid = $row['calid'];
506185e2535SAndreas Boehler        $this->cachedValues['calid'] = $calid;
507185e2535SAndreas Boehler        return $calid;
508185e2535SAndreas Boehler      }
509a1a3b679SAndreas Boehler      return false;
510a1a3b679SAndreas Boehler  }
511a1a3b679SAndreas Boehler
512cb71a62aSAndreas Boehler  /**
513cb71a62aSAndreas Boehler   * Retrieve the complete calendar id to page mapping.
514cb71a62aSAndreas Boehler   * This is necessary to be able to retrieve a list of
515cb71a62aSAndreas Boehler   * calendars for a given user and check the access rights.
516cb71a62aSAndreas Boehler   *
517cb71a62aSAndreas Boehler   * @return array The mapping array
518cb71a62aSAndreas Boehler   */
519a1a3b679SAndreas Boehler  public function getCalendarIdToPageMapping()
520a1a3b679SAndreas Boehler  {
521a1a3b679SAndreas Boehler      $query = "SELECT calid, page FROM pagetocalendarmapping";
522a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
523a1a3b679SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
524a1a3b679SAndreas Boehler      return $arr;
525a1a3b679SAndreas Boehler  }
526a1a3b679SAndreas Boehler
527cb71a62aSAndreas Boehler  /**
528cb71a62aSAndreas Boehler   * Retrieve all calendar IDs a given user has access to.
529cb71a62aSAndreas Boehler   * The user is specified by the principalUri, so the
530cb71a62aSAndreas Boehler   * user name is actually split from the URI component.
531cb71a62aSAndreas Boehler   *
532cb71a62aSAndreas Boehler   * Access rights are checked against DokuWiki's ACL
533cb71a62aSAndreas Boehler   * and applied accordingly.
534cb71a62aSAndreas Boehler   *
535cb71a62aSAndreas Boehler   * @param string $principalUri The principal URI to work on
536cb71a62aSAndreas Boehler   *
537cb71a62aSAndreas Boehler   * @return array An associative array of calendar IDs
538cb71a62aSAndreas Boehler   */
539a1a3b679SAndreas Boehler  public function getCalendarIdsForUser($principalUri)
540a1a3b679SAndreas Boehler  {
54134a47953SAndreas Boehler      global $auth;
542a1a3b679SAndreas Boehler      $user = explode('/', $principalUri);
543a1a3b679SAndreas Boehler      $user = end($user);
544a1a3b679SAndreas Boehler      $mapping = $this->getCalendarIdToPageMapping();
545a1a3b679SAndreas Boehler      $calids = array();
54634a47953SAndreas Boehler      $ud = $auth->getUserData($user);
54734a47953SAndreas Boehler      $groups = $ud['grps'];
548a1a3b679SAndreas Boehler      foreach($mapping as $row)
549a1a3b679SAndreas Boehler      {
550a1a3b679SAndreas Boehler          $id = $row['calid'];
55113b16484SAndreas Boehler          $enabled = $this->getCalendarStatus($id);
55213b16484SAndreas Boehler          if($enabled == false)
55313b16484SAndreas Boehler            continue;
554a1a3b679SAndreas Boehler          $page = $row['page'];
55534a47953SAndreas Boehler          $acl = auth_aclcheck($page, $user, $groups);
556a1a3b679SAndreas Boehler          if($acl >= AUTH_READ)
557a1a3b679SAndreas Boehler          {
558a1a3b679SAndreas Boehler              $write = $acl > AUTH_READ;
559a1a3b679SAndreas Boehler              $calids[$id] = array('readonly' => !$write);
560a1a3b679SAndreas Boehler          }
561a1a3b679SAndreas Boehler      }
562a1a3b679SAndreas Boehler      return $calids;
563a1a3b679SAndreas Boehler  }
564a1a3b679SAndreas Boehler
565cb71a62aSAndreas Boehler  /**
566cb71a62aSAndreas Boehler   * Create a new calendar for a given page ID and set name and description
567cb71a62aSAndreas Boehler   * accordingly. Also update the pagetocalendarmapping table on success.
568cb71a62aSAndreas Boehler   *
569cb71a62aSAndreas Boehler   * @param string $name The calendar's name
570cb71a62aSAndreas Boehler   * @param string $description The calendar's description
571cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to work on
572cb71a62aSAndreas Boehler   * @param string $userid (optional) The user ID that created the calendar
573cb71a62aSAndreas Boehler   *
574cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
575cb71a62aSAndreas Boehler   */
576a1a3b679SAndreas Boehler  public function createCalendarForPage($name, $description, $id = null, $userid = null)
577a1a3b679SAndreas Boehler  {
578a1a3b679SAndreas Boehler      if(is_null($id))
579a1a3b679SAndreas Boehler      {
580a1a3b679SAndreas Boehler          global $ID;
581a1a3b679SAndreas Boehler          $id = $ID;
582a1a3b679SAndreas Boehler      }
583a1a3b679SAndreas Boehler      if(is_null($userid))
58434a47953SAndreas Boehler      {
58534a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
58634a47953SAndreas Boehler        {
587a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
58834a47953SAndreas Boehler        }
58934a47953SAndreas Boehler        else
59034a47953SAndreas Boehler        {
59134a47953SAndreas Boehler          $userid = uniqid('davcal-');
59234a47953SAndreas Boehler        }
59334a47953SAndreas Boehler      }
594a1a3b679SAndreas Boehler      $values = array('principals/'.$userid,
595a1a3b679SAndreas Boehler                      $name,
596a1a3b679SAndreas Boehler                      str_replace(array('/', ' ', ':'), '_', $id),
597a1a3b679SAndreas Boehler                      $description,
598a1a3b679SAndreas Boehler                      'VEVENT,VTODO',
59955a741c0SAndreas Boehler                      0,
60055a741c0SAndreas Boehler                      1);
60151f4febbSAndreas Boehler      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ".
60251f4febbSAndreas Boehler               "VALUES (?, ?, ?, ?, ?, ?, ?)";
60351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]);
60455a741c0SAndreas Boehler      if($res === false)
60555a741c0SAndreas Boehler        return false;
606cb71a62aSAndreas Boehler
607cb71a62aSAndreas Boehler      // Get the new calendar ID
60851f4febbSAndreas Boehler      $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ".
60951f4febbSAndreas Boehler               "uri = ? AND description = ?";
61051f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]);
611a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
612cb71a62aSAndreas Boehler
613cb71a62aSAndreas Boehler      // Update the pagetocalendarmapping table with the new calendar ID
614a1a3b679SAndreas Boehler      if(isset($row['id']))
615a1a3b679SAndreas Boehler      {
61651f4febbSAndreas Boehler          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)";
61751f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $id, $row['id']);
61855a741c0SAndreas Boehler          return ($res !== false);
619a1a3b679SAndreas Boehler      }
620a1a3b679SAndreas Boehler
621a1a3b679SAndreas Boehler      return false;
622a1a3b679SAndreas Boehler  }
623a1a3b679SAndreas Boehler
624cb71a62aSAndreas Boehler  /**
625d5703f5aSAndreas Boehler   * Add a new calendar entry to the given calendar. Calendar data is
626d5703f5aSAndreas Boehler   * specified as ICS file, thus it needs to be parsed first.
627d5703f5aSAndreas Boehler   *
628d5703f5aSAndreas Boehler   * This is mainly needed for the sync support.
629d5703f5aSAndreas Boehler   *
630d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
631d5703f5aSAndreas Boehler   * @param string $uri The new object URI
632d5703f5aSAndreas Boehler   * @param string $ics The ICS file
633d5703f5aSAndreas Boehler   *
634d5703f5aSAndreas Boehler   * @return mixed The etag.
635d5703f5aSAndreas Boehler   */
636d5703f5aSAndreas Boehler  public function addCalendarEntryToCalendarByICS($calid, $uri, $ics)
637d5703f5aSAndreas Boehler  {
638d5703f5aSAndreas Boehler    $extraData = $this->getDenormalizedData($ics);
639d5703f5aSAndreas Boehler
640d5703f5aSAndreas Boehler    $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)";
641d5703f5aSAndreas Boehler    $res = $this->sqlite->query($query,
642d5703f5aSAndreas Boehler            $calid,
643d5703f5aSAndreas Boehler            $uri,
644d5703f5aSAndreas Boehler            $ics,
645d5703f5aSAndreas Boehler            time(),
646d5703f5aSAndreas Boehler            $extraData['etag'],
647d5703f5aSAndreas Boehler            $extraData['size'],
648d5703f5aSAndreas Boehler            $extraData['componentType'],
649d5703f5aSAndreas Boehler            $extraData['firstOccurence'],
650d5703f5aSAndreas Boehler            $extraData['lastOccurence'],
651d5703f5aSAndreas Boehler            $extraData['uid']);
652d5703f5aSAndreas Boehler            // If successfully, update the sync token database
653d5703f5aSAndreas Boehler    if($res !== false)
654d5703f5aSAndreas Boehler    {
655d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, $uri, 'added');
656d5703f5aSAndreas Boehler    }
657d5703f5aSAndreas Boehler    return $extraData['etag'];
658d5703f5aSAndreas Boehler  }
659d5703f5aSAndreas Boehler
660d5703f5aSAndreas Boehler  /**
661d5703f5aSAndreas Boehler   * Edit a calendar entry by providing a new ICS file. This is mainly
662d5703f5aSAndreas Boehler   * needed for the sync support.
663d5703f5aSAndreas Boehler   *
664d5703f5aSAndreas Boehler   * @param int $calid The calendar's IS
665d5703f5aSAndreas Boehler   * @param string $uri The object's URI to modify
666d5703f5aSAndreas Boehler   * @param string $ics The new object's ICS file
667d5703f5aSAndreas Boehler   */
668d5703f5aSAndreas Boehler  public function editCalendarEntryToCalendarByICS($calid, $uri, $ics)
669d5703f5aSAndreas Boehler  {
670d5703f5aSAndreas Boehler      $extraData = $this->getDenormalizedData($ics);
671d5703f5aSAndreas Boehler
672d5703f5aSAndreas Boehler      $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?";
673d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query,
674d5703f5aSAndreas Boehler        $ics,
675d5703f5aSAndreas Boehler        time(),
676d5703f5aSAndreas Boehler        $extraData['etag'],
677d5703f5aSAndreas Boehler        $extraData['size'],
678d5703f5aSAndreas Boehler        $extraData['componentType'],
679d5703f5aSAndreas Boehler        $extraData['firstOccurence'],
680d5703f5aSAndreas Boehler        $extraData['lastOccurence'],
681d5703f5aSAndreas Boehler        $extraData['uid'],
682d5703f5aSAndreas Boehler        $calid,
683d5703f5aSAndreas Boehler        $uri
684d5703f5aSAndreas Boehler      );
685d5703f5aSAndreas Boehler      if($res !== false)
686d5703f5aSAndreas Boehler      {
687d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'modified');
688d5703f5aSAndreas Boehler      }
689d5703f5aSAndreas Boehler      return $extraData['etag'];
690d5703f5aSAndreas Boehler  }
691d5703f5aSAndreas Boehler
692d5703f5aSAndreas Boehler  /**
693cb71a62aSAndreas Boehler   * Add a new iCal entry for a given page, i.e. a given calendar.
694cb71a62aSAndreas Boehler   *
695cb71a62aSAndreas Boehler   * The parameter array needs to contain
696cb71a62aSAndreas Boehler   *   detectedtz       => The timezone as detected by the browser
69782a48dfbSAndreas Boehler   *   currenttz        => The timezone in use by the calendar
698cb71a62aSAndreas Boehler   *   eventfrom        => The event's start date
699cb71a62aSAndreas Boehler   *   eventfromtime    => The event's start time
700cb71a62aSAndreas Boehler   *   eventto          => The event's end date
701cb71a62aSAndreas Boehler   *   eventtotime      => The event's end time
702cb71a62aSAndreas Boehler   *   eventname        => The event's name
703cb71a62aSAndreas Boehler   *   eventdescription => The event's description
704cb71a62aSAndreas Boehler   *
705cb71a62aSAndreas Boehler   * @param string $id The page ID to work on
706cb71a62aSAndreas Boehler   * @param string $user The user who created the calendar
707cb71a62aSAndreas Boehler   * @param string $params A parameter array with values to create
708cb71a62aSAndreas Boehler   *
709cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
710cb71a62aSAndreas Boehler   */
711a1a3b679SAndreas Boehler  public function addCalendarEntryToCalendarForPage($id, $user, $params)
712a1a3b679SAndreas Boehler  {
71382a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
71482a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
71582a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
716a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
717bd883736SAndreas Boehler      else
718bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
719cb71a62aSAndreas Boehler
720cb71a62aSAndreas Boehler      // Retrieve dates from settings
721b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
722b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
723b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
724b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
725cb71a62aSAndreas Boehler
726cb71a62aSAndreas Boehler      // Load SabreDAV
7279bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
728a1a3b679SAndreas Boehler      $vcalendar = new \Sabre\VObject\Component\VCalendar();
729cb71a62aSAndreas Boehler
730cb71a62aSAndreas Boehler      // Add VCalendar, UID and Event Name
731a1a3b679SAndreas Boehler      $event = $vcalendar->add('VEVENT');
732b269830cSAndreas Boehler      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
733b269830cSAndreas Boehler      $event->add('UID', $uuid);
734a1a3b679SAndreas Boehler      $event->summary = $params['eventname'];
735cb71a62aSAndreas Boehler
736cb71a62aSAndreas Boehler      // Add a description if requested
7370eebc909SAndreas Boehler      $description = $params['eventdescription'];
7380eebc909SAndreas Boehler      if($description !== '')
7390eebc909SAndreas Boehler        $event->add('DESCRIPTION', $description);
740cb71a62aSAndreas Boehler
7412b7be5bdSAndreas Boehler      // Add a location if requested
7422b7be5bdSAndreas Boehler      $location = $params['eventlocation'];
7432b7be5bdSAndreas Boehler      if($location !== '')
7442b7be5bdSAndreas Boehler        $event->add('LOCATION', $location);
7452b7be5bdSAndreas Boehler
7464ecb526cSAndreas Boehler      // Add attachments
7474ecb526cSAndreas Boehler      $attachments = $params['attachments'];
74882a48dfbSAndreas Boehler      if(!is_null($attachments))
7494ecb526cSAndreas Boehler        foreach($attachments as $attachment)
7504ecb526cSAndreas Boehler          $event->add('ATTACH', $attachment);
7514ecb526cSAndreas Boehler
752cb71a62aSAndreas Boehler      // Create a timestamp for last modified, created and dtstamp values in UTC
753b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
754b269830cSAndreas Boehler      $event->add('DTSTAMP', $dtStamp);
755b269830cSAndreas Boehler      $event->add('CREATED', $dtStamp);
756b269830cSAndreas Boehler      $event->add('LAST-MODIFIED', $dtStamp);
757cb71a62aSAndreas Boehler
758cb71a62aSAndreas Boehler      // Adjust the start date, based on the given timezone information
759b269830cSAndreas Boehler      $dtStart = new \DateTime();
760a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
761b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
762cb71a62aSAndreas Boehler
763cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
764b269830cSAndreas Boehler      if($params['allday'] != '1')
765b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
766cb71a62aSAndreas Boehler
767cb71a62aSAndreas Boehler      // Adjust the end date, based on the given timezone information
768b269830cSAndreas Boehler      $dtEnd = new \DateTime();
769a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
770b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
771cb71a62aSAndreas Boehler
772cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
773b269830cSAndreas Boehler      if($params['allday'] != '1')
774b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
775cb71a62aSAndreas Boehler
776b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
777b269830cSAndreas Boehler      if($params['allday'] == '1')
778b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
779cb71a62aSAndreas Boehler
780cb71a62aSAndreas Boehler      // Really add Start and End events
781b269830cSAndreas Boehler      $dtStartEv = $event->add('DTSTART', $dtStart);
782b269830cSAndreas Boehler      $dtEndEv = $event->add('DTEND', $dtEnd);
783cb71a62aSAndreas Boehler
784cb71a62aSAndreas Boehler      // Adjust the DATE format for allday events
785b269830cSAndreas Boehler      if($params['allday'] == '1')
786b269830cSAndreas Boehler      {
787b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
788b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
789b269830cSAndreas Boehler      }
790cb71a62aSAndreas Boehler
791809cb0faSAndreas Boehler      $eventStr = $vcalendar->serialize();
792809cb0faSAndreas Boehler
793809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
794809cb0faSAndreas Boehler      {
795809cb0faSAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
796809cb0faSAndreas Boehler          if(is_null($wdc))
797809cb0faSAndreas Boehler            return false;
798809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
799809cb0faSAndreas Boehler          return $wdc->addCalendarEntry($connectionId, $eventStr);
800809cb0faSAndreas Boehler      }
801809cb0faSAndreas Boehler      else
802809cb0faSAndreas Boehler      {
803cb71a62aSAndreas Boehler          // Actually add the values to the database
804a1a3b679SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
805a1a3b679SAndreas Boehler          $uri = uniqid('dokuwiki-').'.ics';
8061bb22c2bSAndreas Boehler          $now = new \DateTime();
807a1a3b679SAndreas Boehler
80851f4febbSAndreas Boehler          $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
80951f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT',
81051f4febbSAndreas Boehler                                      $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(),
81151f4febbSAndreas Boehler                                      strlen($eventStr), md5($eventStr), $uuid);
812cb71a62aSAndreas Boehler
813cb71a62aSAndreas Boehler          // If successfully, update the sync token database
81455a741c0SAndreas Boehler          if($res !== false)
81555a741c0SAndreas Boehler          {
81655a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'added');
817a1a3b679SAndreas Boehler              return true;
818a1a3b679SAndreas Boehler          }
819809cb0faSAndreas Boehler      }
82055a741c0SAndreas Boehler      return false;
82155a741c0SAndreas Boehler  }
822a1a3b679SAndreas Boehler
823cb71a62aSAndreas Boehler  /**
824cb71a62aSAndreas Boehler   * Retrieve the calendar settings of a given calendar id
825cb71a62aSAndreas Boehler   *
826cb71a62aSAndreas Boehler   * @param string $calid The calendar ID
827cb71a62aSAndreas Boehler   *
828cb71a62aSAndreas Boehler   * @return array The calendar settings array
829cb71a62aSAndreas Boehler   */
830b269830cSAndreas Boehler  public function getCalendarSettings($calid)
831b269830cSAndreas Boehler  {
83213b16484SAndreas Boehler      $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled FROM calendars WHERE id= ? ";
83351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
834b269830cSAndreas Boehler      $row = $this->sqlite->res2row($res);
835b269830cSAndreas Boehler      return $row;
836b269830cSAndreas Boehler  }
837b269830cSAndreas Boehler
838cb71a62aSAndreas Boehler  /**
83913b16484SAndreas Boehler   * Retrieve the calendar status of a given calendar id
84013b16484SAndreas Boehler   *
84113b16484SAndreas Boehler   * @param string $calid The calendar ID
84213b16484SAndreas Boehler   * @return boolean True if calendar is enabled, otherwise false
84313b16484SAndreas Boehler   */
84413b16484SAndreas Boehler  public function getCalendarStatus($calid)
84513b16484SAndreas Boehler  {
84613b16484SAndreas Boehler      $query = "SELECT disabled FROM calendars WHERE id = ?";
84713b16484SAndreas Boehler      $res = $this->sqlite->query($query, $calid);
84813b16484SAndreas Boehler      $row = $this->sqlite->res2row($res);
84913b16484SAndreas Boehler      if($row['disabled'] == 1)
85013b16484SAndreas Boehler        return false;
85113b16484SAndreas Boehler      else
85213b16484SAndreas Boehler        return true;
85313b16484SAndreas Boehler  }
85413b16484SAndreas Boehler
85513b16484SAndreas Boehler  /**
85613b16484SAndreas Boehler   * Disable a calendar for a given page
85713b16484SAndreas Boehler   *
85813b16484SAndreas Boehler   * @param string $id The page ID
85913b16484SAndreas Boehler   *
86013b16484SAndreas Boehler   * @return boolean true on success, otherwise false
86113b16484SAndreas Boehler   */
86213b16484SAndreas Boehler  public function disableCalendarForPage($id)
86313b16484SAndreas Boehler  {
86413b16484SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
86513b16484SAndreas Boehler      if($calid === false)
86613b16484SAndreas Boehler        return false;
86713b16484SAndreas Boehler      $query = "UPDATE calendars SET disabled = 1 WHERE id = ?";
86813b16484SAndreas Boehler      $res = $this->sqlite->query($query, $calid);
86913b16484SAndreas Boehler      if($res !== false)
87013b16484SAndreas Boehler        return true;
87113b16484SAndreas Boehler      return false;
87213b16484SAndreas Boehler  }
87313b16484SAndreas Boehler
87413b16484SAndreas Boehler  /**
87513b16484SAndreas Boehler   * Enable a calendar for a given page
87613b16484SAndreas Boehler   *
87713b16484SAndreas Boehler   * @param string $id The page ID
87813b16484SAndreas Boehler   *
87913b16484SAndreas Boehler   * @return boolean true on success, otherwise false
88013b16484SAndreas Boehler   */
88113b16484SAndreas Boehler  public function enableCalendarForPage($id)
88213b16484SAndreas Boehler  {
88313b16484SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
88413b16484SAndreas Boehler      if($calid === false)
88513b16484SAndreas Boehler        return false;
88613b16484SAndreas Boehler      $query = "UPDATE calendars SET disabled = 0 WHERE id = ?";
88713b16484SAndreas Boehler      $res = $this->sqlite->query($query, $calid);
88813b16484SAndreas Boehler      if($res !== false)
88913b16484SAndreas Boehler        return true;
89013b16484SAndreas Boehler      return false;
89113b16484SAndreas Boehler  }
89213b16484SAndreas Boehler
89313b16484SAndreas Boehler  /**
894cb71a62aSAndreas Boehler   * Retrieve all events that are within a given date range,
895cb71a62aSAndreas Boehler   * based on the timezone setting.
896cb71a62aSAndreas Boehler   *
897cb71a62aSAndreas Boehler   * There is also support for retrieving recurring events,
898cb71a62aSAndreas Boehler   * using Sabre's VObject Iterator. Recurring events are represented
899cb71a62aSAndreas Boehler   * as individual calendar entries with the same UID.
900cb71a62aSAndreas Boehler   *
901cb71a62aSAndreas Boehler   * @param string $id The page ID to work with
902cb71a62aSAndreas Boehler   * @param string $user The user ID to work with
903cb71a62aSAndreas Boehler   * @param string $startDate The start date as a string
904cb71a62aSAndreas Boehler   * @param string $endDate The end date as a string
9054a2bf5eeSAndreas Boehler   * @param string $color (optional) The calendar's color
906cb71a62aSAndreas Boehler   *
907cb71a62aSAndreas Boehler   * @return array An array containing the calendar entries.
908cb71a62aSAndreas Boehler   */
9094a2bf5eeSAndreas Boehler  public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null)
910a1a3b679SAndreas Boehler  {
91182a48dfbSAndreas Boehler      if($timezone !== '' && $timezone !== 'local')
91282a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($timezone);
913bd883736SAndreas Boehler      else
914bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
915a1a3b679SAndreas Boehler      $data = array();
916cb71a62aSAndreas Boehler
917a469597cSAndreas Boehler      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?";
918a469597cSAndreas Boehler      $startTs = null;
919a469597cSAndreas Boehler      $endTs = null;
920a469597cSAndreas Boehler      if($startDate !== null)
921a469597cSAndreas Boehler      {
922a1a3b679SAndreas Boehler        $startTs = new \DateTime($startDate);
923a469597cSAndreas Boehler        $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp());
924a469597cSAndreas Boehler      }
925a469597cSAndreas Boehler      if($endDate !== null)
926a469597cSAndreas Boehler      {
927a1a3b679SAndreas Boehler        $endTs = new \DateTime($endDate);
928a469597cSAndreas Boehler        $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp());
929a469597cSAndreas Boehler      }
930cb71a62aSAndreas Boehler
9310b805092SAndreas Boehler      // Load SabreDAV
9320b805092SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
9330b805092SAndreas Boehler
9340b805092SAndreas Boehler      if(strpos($id, 'webdav://') === 0)
9350b805092SAndreas Boehler      {
9360b805092SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
9370b805092SAndreas Boehler          if(is_null($wdc))
9380b805092SAndreas Boehler            return $data;
9390b805092SAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
9400b805092SAndreas Boehler          $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate);
9410b805092SAndreas Boehler      }
9420b805092SAndreas Boehler      else
9430b805092SAndreas Boehler      {
9440b805092SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
9450b805092SAndreas Boehler          if(is_null($color))
9460b805092SAndreas Boehler            $color = $this->getCalendarColorForCalendar($calid);
9470b805092SAndreas Boehler
948*59b68239SAndreas Boehler          $enabled = $this->getCalendarStatus($calid);
949*59b68239SAndreas Boehler          if($enabled === false)
950*59b68239SAndreas Boehler            return $data;
951*59b68239SAndreas Boehler
952cb71a62aSAndreas Boehler          // Retrieve matching calendar objects
953a469597cSAndreas Boehler          $res = $this->sqlite->query($query, $calid);
954a1a3b679SAndreas Boehler          $arr = $this->sqlite->res2arr($res);
9550b805092SAndreas Boehler      }
956cb71a62aSAndreas Boehler
957cb71a62aSAndreas Boehler      // Parse individual calendar entries
958a1a3b679SAndreas Boehler      foreach($arr as $row)
959a1a3b679SAndreas Boehler      {
960a1a3b679SAndreas Boehler          if(isset($row['calendardata']))
961a1a3b679SAndreas Boehler          {
962b269830cSAndreas Boehler              $entry = array();
963a1a3b679SAndreas Boehler              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
964ebc4eb57SAndreas Boehler              $recurrence = $vcal->VEVENT->RRULE;
965cb71a62aSAndreas Boehler              // If it is a recurring event, pass it through Sabre's EventIterator
966ebc4eb57SAndreas Boehler              if($recurrence != null)
967ebc4eb57SAndreas Boehler              {
968ebc4eb57SAndreas Boehler                  $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
969ebc4eb57SAndreas Boehler                  $rEvents->rewind();
970e9b7d302SAndreas Boehler                  while($rEvents->valid())
971ebc4eb57SAndreas Boehler                  {
972ebc4eb57SAndreas Boehler                      $event = $rEvents->getEventObject();
973cb71a62aSAndreas Boehler                      // If we are after the given time range, exit
974a469597cSAndreas Boehler                      if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()))
975e9b7d302SAndreas Boehler                          break;
976cb71a62aSAndreas Boehler
977cb71a62aSAndreas Boehler                      // If we are before the given time range, continue
978a469597cSAndreas Boehler                      if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()))
979ebc4eb57SAndreas Boehler                      {
980ebc4eb57SAndreas Boehler                          $rEvents->next();
981ebc4eb57SAndreas Boehler                          continue;
982ebc4eb57SAndreas Boehler                      }
983cb71a62aSAndreas Boehler
984cb71a62aSAndreas Boehler                      // If we are within the given time range, parse the event
985185e2535SAndreas Boehler                      $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
986ebc4eb57SAndreas Boehler                      $rEvents->next();
987ebc4eb57SAndreas Boehler                  }
988ebc4eb57SAndreas Boehler              }
989ebc4eb57SAndreas Boehler              else
990185e2535SAndreas Boehler                $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
991ebc4eb57SAndreas Boehler          }
992ebc4eb57SAndreas Boehler      }
993ebc4eb57SAndreas Boehler      return $data;
994ebc4eb57SAndreas Boehler  }
995ebc4eb57SAndreas Boehler
996cb71a62aSAndreas Boehler  /**
997cb71a62aSAndreas Boehler   * Helper function that parses the iCal data of a VEVENT to a calendar entry.
998cb71a62aSAndreas Boehler   *
999cb71a62aSAndreas Boehler   * @param \Sabre\VObject\VEvent $event The event to parse
1000cb71a62aSAndreas Boehler   * @param \DateTimeZone $timezone The timezone object
1001cb71a62aSAndreas Boehler   * @param string $uid The entry's UID
10023c86dda8SAndreas Boehler   * @param boolean $recurring (optional) Set to true to define a recurring event
1003cb71a62aSAndreas Boehler   *
1004cb71a62aSAndreas Boehler   * @return array The parse calendar entry
1005cb71a62aSAndreas Boehler   */
1006185e2535SAndreas Boehler  private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
1007ebc4eb57SAndreas Boehler  {
1008ebc4eb57SAndreas Boehler      $entry = array();
1009ebc4eb57SAndreas Boehler      $start = $event->DTSTART;
1010cb71a62aSAndreas Boehler      // Parse only if the start date/time is present
1011b269830cSAndreas Boehler      if($start !== null)
1012b269830cSAndreas Boehler      {
1013b269830cSAndreas Boehler        $dtStart = $start->getDateTime();
1014b269830cSAndreas Boehler        $dtStart->setTimezone($timezone);
1015bf0ad2b4SAndreas Boehler
1016bf0ad2b4SAndreas Boehler        // moment.js doesn't like times be given even if
1017bf0ad2b4SAndreas Boehler        // allDay is set to true
1018bf0ad2b4SAndreas Boehler        // This should fix T23
1019b269830cSAndreas Boehler        if($start['VALUE'] == 'DATE')
1020bf0ad2b4SAndreas Boehler        {
1021b269830cSAndreas Boehler          $entry['allDay'] = true;
1022bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format("Y-m-d");
1023bf0ad2b4SAndreas Boehler        }
1024b269830cSAndreas Boehler        else
1025bf0ad2b4SAndreas Boehler        {
1026b269830cSAndreas Boehler          $entry['allDay'] = false;
1027bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format(\DateTime::ATOM);
1028bf0ad2b4SAndreas Boehler        }
1029b269830cSAndreas Boehler      }
1030ebc4eb57SAndreas Boehler      $end = $event->DTEND;
1031bf0ad2b4SAndreas Boehler      // Parse only if the end date/time is present
1032b269830cSAndreas Boehler      if($end !== null)
1033b269830cSAndreas Boehler      {
1034b269830cSAndreas Boehler        $dtEnd = $end->getDateTime();
1035b269830cSAndreas Boehler        $dtEnd->setTimezone($timezone);
1036bf0ad2b4SAndreas Boehler        if($end['VALUE'] == 'DATE')
1037bf0ad2b4SAndreas Boehler          $entry['end'] = $dtEnd->format("Y-m-d");
1038bf0ad2b4SAndreas Boehler        else
1039b269830cSAndreas Boehler          $entry['end'] = $dtEnd->format(\DateTime::ATOM);
1040b269830cSAndreas Boehler      }
1041ebc4eb57SAndreas Boehler      $description = $event->DESCRIPTION;
10420eebc909SAndreas Boehler      if($description !== null)
10430eebc909SAndreas Boehler        $entry['description'] = (string)$description;
10440eebc909SAndreas Boehler      else
10450eebc909SAndreas Boehler        $entry['description'] = '';
10464ecb526cSAndreas Boehler      $attachments = $event->ATTACH;
10474ecb526cSAndreas Boehler      if($attachments !== null)
10484ecb526cSAndreas Boehler      {
10494ecb526cSAndreas Boehler        $entry['attachments'] = array();
10504ecb526cSAndreas Boehler        foreach($attachments as $attachment)
10514ecb526cSAndreas Boehler          $entry['attachments'][] = (string)$attachment;
10524ecb526cSAndreas Boehler      }
1053ebc4eb57SAndreas Boehler      $entry['title'] = (string)$event->summary;
10542b7be5bdSAndreas Boehler      $entry['location'] = (string)$event->location;
1055ebc4eb57SAndreas Boehler      $entry['id'] = $uid;
1056185e2535SAndreas Boehler      $entry['page'] = $page;
1057185e2535SAndreas Boehler      $entry['color'] = $color;
10583c86dda8SAndreas Boehler      $entry['recurring'] = $recurring;
1059185e2535SAndreas Boehler
1060ebc4eb57SAndreas Boehler      return $entry;
1061a1a3b679SAndreas Boehler  }
1062a1a3b679SAndreas Boehler
1063cb71a62aSAndreas Boehler  /**
1064cb71a62aSAndreas Boehler   * Retrieve an event by its UID
1065cb71a62aSAndreas Boehler   *
1066cb71a62aSAndreas Boehler   * @param string $uid The event's UID
1067cb71a62aSAndreas Boehler   *
1068cb71a62aSAndreas Boehler   * @return mixed The table row with the given event
1069cb71a62aSAndreas Boehler   */
1070a1a3b679SAndreas Boehler  public function getEventWithUid($uid)
1071a1a3b679SAndreas Boehler  {
107251f4febbSAndreas Boehler      $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?";
107351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
1074a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
1075a1a3b679SAndreas Boehler      return $row;
1076a1a3b679SAndreas Boehler  }
1077a1a3b679SAndreas Boehler
1078cb71a62aSAndreas Boehler  /**
1079d5703f5aSAndreas Boehler   * Retrieve information of a calendar's object, not including the actual
1080*59b68239SAndreas Boehler   * calendar data! This is mainly needed for the sync support.
1081d5703f5aSAndreas Boehler   *
1082d5703f5aSAndreas Boehler   * @param int $calid The calendar ID
1083d5703f5aSAndreas Boehler   *
1084d5703f5aSAndreas Boehler   * @return mixed The result
1085d5703f5aSAndreas Boehler   */
1086d5703f5aSAndreas Boehler  public function getCalendarObjects($calid)
1087d5703f5aSAndreas Boehler  {
1088d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?";
1089d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1090d5703f5aSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
1091d5703f5aSAndreas Boehler      return $arr;
1092d5703f5aSAndreas Boehler  }
1093d5703f5aSAndreas Boehler
1094d5703f5aSAndreas Boehler  /**
1095d5703f5aSAndreas Boehler   * Retrieve a single calendar object by calendar ID and URI
1096d5703f5aSAndreas Boehler   *
1097d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1098d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1099d5703f5aSAndreas Boehler   *
1100d5703f5aSAndreas Boehler   * @return mixed The result
1101d5703f5aSAndreas Boehler   */
1102d5703f5aSAndreas Boehler  public function getCalendarObjectByUri($calid, $uri)
1103d5703f5aSAndreas Boehler  {
1104d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1105d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1106d5703f5aSAndreas Boehler      $row = $this->sqlite->res2row($res);
1107d5703f5aSAndreas Boehler      return $row;
1108d5703f5aSAndreas Boehler  }
1109d5703f5aSAndreas Boehler
1110d5703f5aSAndreas Boehler  /**
1111d5703f5aSAndreas Boehler   * Retrieve several calendar objects by specifying an array of URIs.
1112d5703f5aSAndreas Boehler   * This is mainly neede for sync.
1113d5703f5aSAndreas Boehler   *
1114d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1115d5703f5aSAndreas Boehler   * @param array $uris An array of URIs
1116d5703f5aSAndreas Boehler   *
1117d5703f5aSAndreas Boehler   * @return mixed The result
1118d5703f5aSAndreas Boehler   */
1119d5703f5aSAndreas Boehler  public function getMultipleCalendarObjectsByUri($calid, $uris)
1120d5703f5aSAndreas Boehler  {
1121d5703f5aSAndreas Boehler        $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN (";
1122d5703f5aSAndreas Boehler        // Inserting a whole bunch of question marks
1123d5703f5aSAndreas Boehler        $query .= implode(',', array_fill(0, count($uris), '?'));
1124d5703f5aSAndreas Boehler        $query .= ')';
1125d5703f5aSAndreas Boehler        $vals = array_merge(array($calid), $uris);
1126d5703f5aSAndreas Boehler
1127d5703f5aSAndreas Boehler        $res = $this->sqlite->query($query, $vals);
1128d5703f5aSAndreas Boehler        $arr = $this->sqlite->res2arr($res);
1129d5703f5aSAndreas Boehler        return $arr;
1130d5703f5aSAndreas Boehler  }
1131d5703f5aSAndreas Boehler
1132d5703f5aSAndreas Boehler  /**
1133cb71a62aSAndreas Boehler   * Retrieve all calendar events for a given calendar ID
1134cb71a62aSAndreas Boehler   *
1135cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1136cb71a62aSAndreas Boehler   *
1137cb71a62aSAndreas Boehler   * @return array An array containing all calendar data
1138cb71a62aSAndreas Boehler   */
1139f69bb449SAndreas Boehler  public function getAllCalendarEvents($calid)
1140f69bb449SAndreas Boehler  {
1141*59b68239SAndreas Boehler      $enabled = $this->getCalendarStatus($calid);
1142*59b68239SAndreas Boehler      if($enabled === false)
1143*59b68239SAndreas Boehler        return false;
11447e0b8590SAndreas Boehler      $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?";
114551f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1146f69bb449SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
1147f69bb449SAndreas Boehler      return $arr;
1148f69bb449SAndreas Boehler  }
1149f69bb449SAndreas Boehler
1150cb71a62aSAndreas Boehler  /**
1151cb71a62aSAndreas Boehler   * Edit a calendar entry for a page, given by its parameters.
1152cb71a62aSAndreas Boehler   * The params array has the same format as @see addCalendarEntryForPage
1153cb71a62aSAndreas Boehler   *
1154cb71a62aSAndreas Boehler   * @param string $id The page's ID to work on
1155cb71a62aSAndreas Boehler   * @param string $user The user's ID to work on
1156cb71a62aSAndreas Boehler   * @param array $params The parameter array for the edited calendar event
1157cb71a62aSAndreas Boehler   *
1158cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1159cb71a62aSAndreas Boehler   */
1160a1a3b679SAndreas Boehler  public function editCalendarEntryForPage($id, $user, $params)
1161a1a3b679SAndreas Boehler  {
116282a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
116382a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
116482a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
1165a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
1166bd883736SAndreas Boehler      else
1167bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
1168cb71a62aSAndreas Boehler
1169cb71a62aSAndreas Boehler      // Parse dates
1170b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
1171b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
1172b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
1173b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
1174cb71a62aSAndreas Boehler
1175cb71a62aSAndreas Boehler      // Retrieve the existing event based on the UID
117655a741c0SAndreas Boehler      $uid = $params['uid'];
1177809cb0faSAndreas Boehler
1178809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1179809cb0faSAndreas Boehler      {
1180809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1181809cb0faSAndreas Boehler        if(is_null($wdc))
1182809cb0faSAndreas Boehler          return false;
1183809cb0faSAndreas Boehler        $event = $wdc->getCalendarEntryByUid($uid);
1184809cb0faSAndreas Boehler      }
1185809cb0faSAndreas Boehler      else
1186809cb0faSAndreas Boehler      {
118755a741c0SAndreas Boehler        $event = $this->getEventWithUid($uid);
1188809cb0faSAndreas Boehler      }
1189cb71a62aSAndreas Boehler
1190cb71a62aSAndreas Boehler      // Load SabreDAV
11919bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1192a1a3b679SAndreas Boehler      if(!isset($event['calendardata']))
1193a1a3b679SAndreas Boehler        return false;
119455a741c0SAndreas Boehler      $uri = $event['uri'];
119555a741c0SAndreas Boehler      $calid = $event['calendarid'];
1196cb71a62aSAndreas Boehler
1197cb71a62aSAndreas Boehler      // Parse the existing event
1198a1a3b679SAndreas Boehler      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
1199b269830cSAndreas Boehler      $vevent = $vcal->VEVENT;
1200cb71a62aSAndreas Boehler
1201cb71a62aSAndreas Boehler      // Set the new event values
1202b269830cSAndreas Boehler      $vevent->summary = $params['eventname'];
1203b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
12040eebc909SAndreas Boehler      $description = $params['eventdescription'];
12052b7be5bdSAndreas Boehler      $location = $params['eventlocation'];
1206cb71a62aSAndreas Boehler
1207cb71a62aSAndreas Boehler      // Remove existing timestamps to overwrite them
12080eebc909SAndreas Boehler      $vevent->remove('DESCRIPTION');
1209b269830cSAndreas Boehler      $vevent->remove('DTSTAMP');
1210b269830cSAndreas Boehler      $vevent->remove('LAST-MODIFIED');
12114ecb526cSAndreas Boehler      $vevent->remove('ATTACH');
12122b7be5bdSAndreas Boehler      $vevent->remove('LOCATION');
1213cb71a62aSAndreas Boehler
12142b7be5bdSAndreas Boehler      // Add new time stamps, description and location
1215b269830cSAndreas Boehler      $vevent->add('DTSTAMP', $dtStamp);
1216b269830cSAndreas Boehler      $vevent->add('LAST-MODIFIED', $dtStamp);
12170eebc909SAndreas Boehler      if($description !== '')
12180eebc909SAndreas Boehler        $vevent->add('DESCRIPTION', $description);
12192b7be5bdSAndreas Boehler      if($location !== '')
12202b7be5bdSAndreas Boehler        $vevent->add('LOCATION', $location);
1221cb71a62aSAndreas Boehler
12224ecb526cSAndreas Boehler      // Add attachments
12234ecb526cSAndreas Boehler      $attachments = $params['attachments'];
122482a48dfbSAndreas Boehler      if(!is_null($attachments))
12254ecb526cSAndreas Boehler        foreach($attachments as $attachment)
12264ecb526cSAndreas Boehler          $vevent->add('ATTACH', $attachment);
12274ecb526cSAndreas Boehler
1228cb71a62aSAndreas Boehler      // Setup DTSTART
1229b269830cSAndreas Boehler      $dtStart = new \DateTime();
1230a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
1231b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
1232b269830cSAndreas Boehler      if($params['allday'] != '1')
1233b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
1234cb71a62aSAndreas Boehler
12354ecb526cSAndreas Boehler      // Setup DTEND
1236b269830cSAndreas Boehler      $dtEnd = new \DateTime();
1237a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
1238b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
1239b269830cSAndreas Boehler      if($params['allday'] != '1')
1240b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
1241cb71a62aSAndreas Boehler
1242b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
1243b269830cSAndreas Boehler      if($params['allday'] == '1')
1244b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
1245b269830cSAndreas Boehler      $vevent->remove('DTSTART');
1246b269830cSAndreas Boehler      $vevent->remove('DTEND');
1247b269830cSAndreas Boehler      $dtStartEv = $vevent->add('DTSTART', $dtStart);
1248b269830cSAndreas Boehler      $dtEndEv = $vevent->add('DTEND', $dtEnd);
1249cb71a62aSAndreas Boehler
1250cb71a62aSAndreas Boehler      // Remove the time for allday events
1251b269830cSAndreas Boehler      if($params['allday'] == '1')
1252b269830cSAndreas Boehler      {
1253b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
1254b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
1255b269830cSAndreas Boehler      }
1256a1a3b679SAndreas Boehler      $eventStr = $vcal->serialize();
1257809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1258809cb0faSAndreas Boehler      {
1259809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
1260809cb0faSAndreas Boehler          return $wdc->editCalendarEntry($connectionId, $uid, $eventStr);
1261809cb0faSAndreas Boehler      }
1262809cb0faSAndreas Boehler      else
1263809cb0faSAndreas Boehler      {
1264809cb0faSAndreas Boehler          $now = new DateTime();
1265cb71a62aSAndreas Boehler          // Actually write to the database
126651f4febbSAndreas Boehler          $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ".
126751f4febbSAndreas Boehler                   "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?";
126851f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(),
126951f4febbSAndreas Boehler                                      $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid);
127055a741c0SAndreas Boehler          if($res !== false)
127155a741c0SAndreas Boehler          {
127255a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'modified');
1273a1a3b679SAndreas Boehler              return true;
1274a1a3b679SAndreas Boehler          }
1275809cb0faSAndreas Boehler      }
127655a741c0SAndreas Boehler      return false;
127755a741c0SAndreas Boehler  }
1278a1a3b679SAndreas Boehler
1279cb71a62aSAndreas Boehler  /**
1280d5703f5aSAndreas Boehler   * Delete an event from a calendar by calendar ID and URI
1281d5703f5aSAndreas Boehler   *
1282d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1283d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1284d5703f5aSAndreas Boehler   *
1285d5703f5aSAndreas Boehler   * @return true
1286d5703f5aSAndreas Boehler   */
1287d5703f5aSAndreas Boehler  public function deleteCalendarEntryForCalendarByUri($calid, $uri)
1288d5703f5aSAndreas Boehler  {
1289d5703f5aSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1290d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1291d5703f5aSAndreas Boehler      if($res !== false)
1292d5703f5aSAndreas Boehler      {
1293d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
1294d5703f5aSAndreas Boehler      }
1295d5703f5aSAndreas Boehler      return true;
1296d5703f5aSAndreas Boehler  }
1297d5703f5aSAndreas Boehler
1298d5703f5aSAndreas Boehler  /**
1299cb71a62aSAndreas Boehler   * Delete a calendar entry for a given page. Actually, the event is removed
1300cb71a62aSAndreas Boehler   * based on the entry's UID, so that page ID is no used.
1301cb71a62aSAndreas Boehler   *
1302cb71a62aSAndreas Boehler   * @param string $id The page's ID (unused)
1303cb71a62aSAndreas Boehler   * @param array $params The parameter array to work with
1304cb71a62aSAndreas Boehler   *
1305cb71a62aSAndreas Boehler   * @return boolean True
1306cb71a62aSAndreas Boehler   */
1307a1a3b679SAndreas Boehler  public function deleteCalendarEntryForPage($id, $params)
1308a1a3b679SAndreas Boehler  {
1309a1a3b679SAndreas Boehler      $uid = $params['uid'];
1310809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1311809cb0faSAndreas Boehler      {
1312809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1313809cb0faSAndreas Boehler        if(is_null($wdc))
1314809cb0faSAndreas Boehler          return false;
1315809cb0faSAndreas Boehler        $connectionId = str_replace('webdav://', '', $id);
1316809cb0faSAndreas Boehler        $result = $wdc->deleteCalendarEntry($connectionId, $uid);
1317809cb0faSAndreas Boehler        return $result;
1318809cb0faSAndreas Boehler      }
131955a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
13202c14b82bSAndreas Boehler      $calid = $event['calendarid'];
132155a741c0SAndreas Boehler      $uri = $event['uri'];
132251f4febbSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE uid = ?";
132351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
132455a741c0SAndreas Boehler      if($res !== false)
132555a741c0SAndreas Boehler      {
132655a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
132755a741c0SAndreas Boehler      }
1328a1a3b679SAndreas Boehler      return true;
1329a1a3b679SAndreas Boehler  }
1330a1a3b679SAndreas Boehler
1331cb71a62aSAndreas Boehler  /**
1332cb71a62aSAndreas Boehler   * Retrieve the current sync token for a calendar
1333cb71a62aSAndreas Boehler   *
1334cb71a62aSAndreas Boehler   * @param string $calid The calendar id
1335cb71a62aSAndreas Boehler   *
1336cb71a62aSAndreas Boehler   * @return mixed The synctoken or false
1337cb71a62aSAndreas Boehler   */
133855a741c0SAndreas Boehler  public function getSyncTokenForCalendar($calid)
133955a741c0SAndreas Boehler  {
1340b269830cSAndreas Boehler      $row = $this->getCalendarSettings($calid);
134155a741c0SAndreas Boehler      if(isset($row['synctoken']))
134255a741c0SAndreas Boehler          return $row['synctoken'];
134355a741c0SAndreas Boehler      return false;
134455a741c0SAndreas Boehler  }
134555a741c0SAndreas Boehler
1346cb71a62aSAndreas Boehler  /**
1347cb71a62aSAndreas Boehler   * Helper function to convert the operation name to
1348cb71a62aSAndreas Boehler   * an operation code as stored in the database
1349cb71a62aSAndreas Boehler   *
1350cb71a62aSAndreas Boehler   * @param string $operationName The operation name
1351cb71a62aSAndreas Boehler   *
1352cb71a62aSAndreas Boehler   * @return mixed The operation code or false
1353cb71a62aSAndreas Boehler   */
135455a741c0SAndreas Boehler  public function operationNameToOperation($operationName)
135555a741c0SAndreas Boehler  {
135655a741c0SAndreas Boehler      switch($operationName)
135755a741c0SAndreas Boehler      {
135855a741c0SAndreas Boehler          case 'added':
135955a741c0SAndreas Boehler              return 1;
136055a741c0SAndreas Boehler          break;
136155a741c0SAndreas Boehler          case 'modified':
136255a741c0SAndreas Boehler              return 2;
136355a741c0SAndreas Boehler          break;
136455a741c0SAndreas Boehler          case 'deleted':
136555a741c0SAndreas Boehler              return 3;
136655a741c0SAndreas Boehler          break;
136755a741c0SAndreas Boehler      }
136855a741c0SAndreas Boehler      return false;
136955a741c0SAndreas Boehler  }
137055a741c0SAndreas Boehler
1371cb71a62aSAndreas Boehler  /**
1372cb71a62aSAndreas Boehler   * Update the sync token log based on the calendar id and the
1373cb71a62aSAndreas Boehler   * operation that was performed.
1374cb71a62aSAndreas Boehler   *
1375cb71a62aSAndreas Boehler   * @param string $calid The calendar ID that was modified
1376cb71a62aSAndreas Boehler   * @param string $uri The calendar URI that was modified
1377cb71a62aSAndreas Boehler   * @param string $operation The operation that was performed
1378cb71a62aSAndreas Boehler   *
1379cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1380cb71a62aSAndreas Boehler   */
138155a741c0SAndreas Boehler  private function updateSyncTokenLog($calid, $uri, $operation)
138255a741c0SAndreas Boehler  {
138355a741c0SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
138455a741c0SAndreas Boehler      $operationCode = $this->operationNameToOperation($operation);
138555a741c0SAndreas Boehler      if(($operationCode === false) || ($currentToken === false))
138655a741c0SAndreas Boehler          return false;
138755a741c0SAndreas Boehler      $values = array($uri,
138855a741c0SAndreas Boehler                      $currentToken,
138955a741c0SAndreas Boehler                      $calid,
139055a741c0SAndreas Boehler                      $operationCode
139155a741c0SAndreas Boehler      );
139251f4febbSAndreas Boehler      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)";
139351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode);
139455a741c0SAndreas Boehler      if($res === false)
139555a741c0SAndreas Boehler        return false;
139655a741c0SAndreas Boehler      $currentToken++;
139751f4febbSAndreas Boehler      $query = "UPDATE calendars SET synctoken = ? WHERE id = ?";
139851f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $currentToken, $calid);
139955a741c0SAndreas Boehler      return ($res !== false);
140055a741c0SAndreas Boehler  }
140155a741c0SAndreas Boehler
1402cb71a62aSAndreas Boehler  /**
1403cb71a62aSAndreas Boehler   * Return the sync URL for a given Page, i.e. a calendar
1404cb71a62aSAndreas Boehler   *
1405cb71a62aSAndreas Boehler   * @param string $id The page's ID
1406cb71a62aSAndreas Boehler   * @param string $user (optional) The user's ID
1407cb71a62aSAndreas Boehler   *
1408cb71a62aSAndreas Boehler   * @return mixed The sync url or false
1409cb71a62aSAndreas Boehler   */
1410b269830cSAndreas Boehler  public function getSyncUrlForPage($id, $user = null)
1411b269830cSAndreas Boehler  {
141234a47953SAndreas Boehler      if(is_null($userid))
141334a47953SAndreas Boehler      {
141434a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
141534a47953SAndreas Boehler        {
141634a47953SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
141734a47953SAndreas Boehler        }
141834a47953SAndreas Boehler        else
141934a47953SAndreas Boehler        {
142034a47953SAndreas Boehler          return false;
142134a47953SAndreas Boehler        }
142234a47953SAndreas Boehler      }
1423b269830cSAndreas Boehler
1424b269830cSAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1425b269830cSAndreas Boehler      if($calid === false)
1426b269830cSAndreas Boehler        return false;
1427b269830cSAndreas Boehler
1428b269830cSAndreas Boehler      $calsettings = $this->getCalendarSettings($calid);
1429b269830cSAndreas Boehler      if(!isset($calsettings['uri']))
1430b269830cSAndreas Boehler        return false;
1431b269830cSAndreas Boehler
1432b269830cSAndreas Boehler      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
1433b269830cSAndreas Boehler      return $syncurl;
1434b269830cSAndreas Boehler  }
1435b269830cSAndreas Boehler
1436cb71a62aSAndreas Boehler  /**
1437cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given page
1438cb71a62aSAndreas Boehler   *
1439cb71a62aSAndreas Boehler   * @param string $id the page ID
1440cb71a62aSAndreas Boehler   *
1441cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1442cb71a62aSAndreas Boehler   */
1443f69bb449SAndreas Boehler  public function getPrivateURLForPage($id)
1444f69bb449SAndreas Boehler  {
1445f69bb449SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1446f69bb449SAndreas Boehler      if($calid === false)
1447f69bb449SAndreas Boehler        return false;
1448f69bb449SAndreas Boehler
1449f69bb449SAndreas Boehler      return $this->getPrivateURLForCalendar($calid);
1450f69bb449SAndreas Boehler  }
1451f69bb449SAndreas Boehler
1452cb71a62aSAndreas Boehler  /**
1453cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given calendar ID
1454cb71a62aSAndreas Boehler   *
1455cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1456cb71a62aSAndreas Boehler   *
1457cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1458cb71a62aSAndreas Boehler   */
1459f69bb449SAndreas Boehler  public function getPrivateURLForCalendar($calid)
1460f69bb449SAndreas Boehler  {
1461185e2535SAndreas Boehler      if(isset($this->cachedValues['privateurl'][$calid]))
1462185e2535SAndreas Boehler        return $this->cachedValues['privateurl'][$calid];
146351f4febbSAndreas Boehler      $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?";
146451f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1465f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1466f69bb449SAndreas Boehler      if(!isset($row['url']))
1467f69bb449SAndreas Boehler      {
1468f69bb449SAndreas Boehler          $url = uniqid("dokuwiki-").".ics";
146951f4febbSAndreas Boehler          $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)";
147051f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $url, $calid);
1471f69bb449SAndreas Boehler          if($res === false)
1472f69bb449SAndreas Boehler            return false;
1473f69bb449SAndreas Boehler      }
1474f69bb449SAndreas Boehler      else
1475f69bb449SAndreas Boehler      {
1476f69bb449SAndreas Boehler          $url = $row['url'];
1477f69bb449SAndreas Boehler      }
1478185e2535SAndreas Boehler
1479185e2535SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url;
1480185e2535SAndreas Boehler      $this->cachedValues['privateurl'][$calid] = $url;
1481185e2535SAndreas Boehler      return $url;
1482f69bb449SAndreas Boehler  }
1483f69bb449SAndreas Boehler
1484cb71a62aSAndreas Boehler  /**
1485cb71a62aSAndreas Boehler   * Retrieve the calendar ID for a given private calendar URL
1486cb71a62aSAndreas Boehler   *
1487cb71a62aSAndreas Boehler   * @param string $url The private URL
1488cb71a62aSAndreas Boehler   *
1489cb71a62aSAndreas Boehler   * @return mixed The calendar ID or false
1490cb71a62aSAndreas Boehler   */
1491f69bb449SAndreas Boehler  public function getCalendarForPrivateURL($url)
1492f69bb449SAndreas Boehler  {
149351f4febbSAndreas Boehler      $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?";
149451f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $url);
1495f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1496f69bb449SAndreas Boehler      if(!isset($row['calid']))
1497f69bb449SAndreas Boehler        return false;
1498f69bb449SAndreas Boehler      return $row['calid'];
1499f69bb449SAndreas Boehler  }
1500f69bb449SAndreas Boehler
1501cb71a62aSAndreas Boehler  /**
1502cb71a62aSAndreas Boehler   * Return a given calendar as ICS feed, i.e. all events in one ICS file.
1503cb71a62aSAndreas Boehler   *
15047e0b8590SAndreas Boehler   * @param string $calid The calendar ID to retrieve
1505cb71a62aSAndreas Boehler   *
1506cb71a62aSAndreas Boehler   * @return mixed The calendar events as string or false
1507cb71a62aSAndreas Boehler   */
1508f69bb449SAndreas Boehler  public function getCalendarAsICSFeed($calid)
1509f69bb449SAndreas Boehler  {
1510f69bb449SAndreas Boehler      $calSettings = $this->getCalendarSettings($calid);
1511f69bb449SAndreas Boehler      if($calSettings === false)
1512f69bb449SAndreas Boehler        return false;
1513f69bb449SAndreas Boehler      $events = $this->getAllCalendarEvents($calid);
1514f69bb449SAndreas Boehler      if($events === false)
1515f69bb449SAndreas Boehler        return false;
1516f69bb449SAndreas Boehler
15177e0b8590SAndreas Boehler      // Load SabreDAV
15187e0b8590SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
15197e0b8590SAndreas Boehler      $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:";
15207e0b8590SAndreas Boehler      $out .= $calSettings['displayname']."\r\n";
1521f69bb449SAndreas Boehler      foreach($events as $event)
1522f69bb449SAndreas Boehler      {
15237e0b8590SAndreas Boehler          $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
15247e0b8590SAndreas Boehler          $evt = $vcal->VEVENT;
15257e0b8590SAndreas Boehler          $out .= $evt->serialize();
1526f69bb449SAndreas Boehler      }
15277e0b8590SAndreas Boehler      $out .= "END:VCALENDAR\r\n";
1528f69bb449SAndreas Boehler      return $out;
1529f69bb449SAndreas Boehler  }
1530f69bb449SAndreas Boehler
15317c7c6b0bSAndreas Boehler  /**
15327c7c6b0bSAndreas Boehler   * Retrieve a configuration option for the plugin
15337c7c6b0bSAndreas Boehler   *
15347c7c6b0bSAndreas Boehler   * @param string $key The key to query
153521d04f73SAndreas Boehler   * @return mixed The option set, null if not found
15367c7c6b0bSAndreas Boehler   */
15377c7c6b0bSAndreas Boehler  public function getConfig($key)
15387c7c6b0bSAndreas Boehler  {
15397c7c6b0bSAndreas Boehler      return $this->getConf($key);
15407c7c6b0bSAndreas Boehler  }
15417c7c6b0bSAndreas Boehler
1542d5703f5aSAndreas Boehler  /**
1543d5703f5aSAndreas Boehler   * Parses some information from calendar objects, used for optimized
1544d5703f5aSAndreas Boehler   * calendar-queries. Taken nearly unmodified from Sabre's PDO backend
1545d5703f5aSAndreas Boehler   *
1546d5703f5aSAndreas Boehler   * Returns an array with the following keys:
1547d5703f5aSAndreas Boehler   *   * etag - An md5 checksum of the object without the quotes.
1548d5703f5aSAndreas Boehler   *   * size - Size of the object in bytes
1549d5703f5aSAndreas Boehler   *   * componentType - VEVENT, VTODO or VJOURNAL
1550d5703f5aSAndreas Boehler   *   * firstOccurence
1551d5703f5aSAndreas Boehler   *   * lastOccurence
1552d5703f5aSAndreas Boehler   *   * uid - value of the UID property
1553d5703f5aSAndreas Boehler   *
1554d5703f5aSAndreas Boehler   * @param string $calendarData
1555d5703f5aSAndreas Boehler   * @return array
1556d5703f5aSAndreas Boehler   */
1557d5703f5aSAndreas Boehler  protected function getDenormalizedData($calendarData)
1558d5703f5aSAndreas Boehler  {
1559d5703f5aSAndreas Boehler    require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1560d5703f5aSAndreas Boehler
1561d5703f5aSAndreas Boehler    $vObject = \Sabre\VObject\Reader::read($calendarData);
1562d5703f5aSAndreas Boehler    $componentType = null;
1563d5703f5aSAndreas Boehler    $component = null;
1564d5703f5aSAndreas Boehler    $firstOccurence = null;
1565d5703f5aSAndreas Boehler    $lastOccurence = null;
1566d5703f5aSAndreas Boehler    $uid = null;
1567d5703f5aSAndreas Boehler    foreach ($vObject->getComponents() as $component)
1568d5703f5aSAndreas Boehler    {
1569d5703f5aSAndreas Boehler        if ($component->name !== 'VTIMEZONE')
1570d5703f5aSAndreas Boehler        {
1571d5703f5aSAndreas Boehler            $componentType = $component->name;
1572d5703f5aSAndreas Boehler            $uid = (string)$component->UID;
1573d5703f5aSAndreas Boehler            break;
1574d5703f5aSAndreas Boehler        }
1575d5703f5aSAndreas Boehler    }
1576d5703f5aSAndreas Boehler    if (!$componentType)
1577d5703f5aSAndreas Boehler    {
1578d5703f5aSAndreas Boehler        return false;
1579d5703f5aSAndreas Boehler    }
1580d5703f5aSAndreas Boehler    if ($componentType === 'VEVENT')
1581d5703f5aSAndreas Boehler    {
1582d5703f5aSAndreas Boehler        $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
1583d5703f5aSAndreas Boehler        // Finding the last occurence is a bit harder
1584d5703f5aSAndreas Boehler        if (!isset($component->RRULE))
1585d5703f5aSAndreas Boehler        {
1586d5703f5aSAndreas Boehler            if (isset($component->DTEND))
1587d5703f5aSAndreas Boehler            {
1588d5703f5aSAndreas Boehler                $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
1589d5703f5aSAndreas Boehler            }
1590d5703f5aSAndreas Boehler            elseif (isset($component->DURATION))
1591d5703f5aSAndreas Boehler            {
1592d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1593d5703f5aSAndreas Boehler                $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
1594d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1595d5703f5aSAndreas Boehler            }
1596d5703f5aSAndreas Boehler            elseif (!$component->DTSTART->hasTime())
1597d5703f5aSAndreas Boehler            {
1598d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1599d5703f5aSAndreas Boehler                $endDate->modify('+1 day');
1600d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1601d5703f5aSAndreas Boehler            }
1602d5703f5aSAndreas Boehler            else
1603d5703f5aSAndreas Boehler            {
1604d5703f5aSAndreas Boehler                $lastOccurence = $firstOccurence;
1605d5703f5aSAndreas Boehler            }
1606d5703f5aSAndreas Boehler        }
1607d5703f5aSAndreas Boehler        else
1608d5703f5aSAndreas Boehler        {
1609d5703f5aSAndreas Boehler            $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
1610d5703f5aSAndreas Boehler            $maxDate = new \DateTime('2038-01-01');
1611d5703f5aSAndreas Boehler            if ($it->isInfinite())
1612d5703f5aSAndreas Boehler            {
1613d5703f5aSAndreas Boehler                $lastOccurence = $maxDate->getTimeStamp();
1614d5703f5aSAndreas Boehler            }
1615d5703f5aSAndreas Boehler            else
1616d5703f5aSAndreas Boehler            {
1617d5703f5aSAndreas Boehler                $end = $it->getDtEnd();
1618d5703f5aSAndreas Boehler                while ($it->valid() && $end < $maxDate)
1619d5703f5aSAndreas Boehler                {
1620d5703f5aSAndreas Boehler                    $end = $it->getDtEnd();
1621d5703f5aSAndreas Boehler                    $it->next();
1622d5703f5aSAndreas Boehler                }
1623d5703f5aSAndreas Boehler                $lastOccurence = $end->getTimeStamp();
1624d5703f5aSAndreas Boehler            }
1625d5703f5aSAndreas Boehler        }
1626d5703f5aSAndreas Boehler    }
1627d5703f5aSAndreas Boehler
1628d5703f5aSAndreas Boehler    return array(
1629d5703f5aSAndreas Boehler        'etag'           => md5($calendarData),
1630d5703f5aSAndreas Boehler        'size'           => strlen($calendarData),
1631d5703f5aSAndreas Boehler        'componentType'  => $componentType,
1632d5703f5aSAndreas Boehler        'firstOccurence' => $firstOccurence,
1633d5703f5aSAndreas Boehler        'lastOccurence'  => $lastOccurence,
1634d5703f5aSAndreas Boehler        'uid'            => $uid,
1635d5703f5aSAndreas Boehler    );
1636d5703f5aSAndreas Boehler
1637d5703f5aSAndreas Boehler  }
1638d5703f5aSAndreas Boehler
1639d5703f5aSAndreas Boehler  /**
1640d5703f5aSAndreas Boehler   * Query a calendar by ID and taking several filters into account.
1641d5703f5aSAndreas Boehler   * This is heavily based on Sabre's PDO backend.
1642d5703f5aSAndreas Boehler   *
1643d5703f5aSAndreas Boehler   * @param int $calendarId The calendar's ID
1644d5703f5aSAndreas Boehler   * @param array $filters The filter array to apply
1645d5703f5aSAndreas Boehler   *
1646d5703f5aSAndreas Boehler   * @return mixed The result
1647d5703f5aSAndreas Boehler   */
1648d5703f5aSAndreas Boehler  public function calendarQuery($calendarId, $filters)
1649d5703f5aSAndreas Boehler  {
165039787131SAndreas Boehler    dbglog('davcal::helper::calendarQuery');
1651d5703f5aSAndreas Boehler    $componentType = null;
1652d5703f5aSAndreas Boehler    $requirePostFilter = true;
1653d5703f5aSAndreas Boehler    $timeRange = null;
1654d5703f5aSAndreas Boehler
1655d5703f5aSAndreas Boehler    // if no filters were specified, we don't need to filter after a query
1656d5703f5aSAndreas Boehler    if (!$filters['prop-filters'] && !$filters['comp-filters'])
1657d5703f5aSAndreas Boehler    {
1658d5703f5aSAndreas Boehler        $requirePostFilter = false;
1659d5703f5aSAndreas Boehler    }
1660d5703f5aSAndreas Boehler
1661d5703f5aSAndreas Boehler    // Figuring out if there's a component filter
1662d5703f5aSAndreas Boehler    if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined'])
1663d5703f5aSAndreas Boehler    {
1664d5703f5aSAndreas Boehler        $componentType = $filters['comp-filters'][0]['name'];
1665d5703f5aSAndreas Boehler
1666d5703f5aSAndreas Boehler        // Checking if we need post-filters
1667d5703f5aSAndreas Boehler        if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters'])
1668d5703f5aSAndreas Boehler        {
1669d5703f5aSAndreas Boehler            $requirePostFilter = false;
1670d5703f5aSAndreas Boehler        }
1671d5703f5aSAndreas Boehler        // There was a time-range filter
1672d5703f5aSAndreas Boehler        if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range']))
1673d5703f5aSAndreas Boehler        {
1674d5703f5aSAndreas Boehler            $timeRange = $filters['comp-filters'][0]['time-range'];
1675d5703f5aSAndreas Boehler
1676d5703f5aSAndreas Boehler            // If start time OR the end time is not specified, we can do a
1677d5703f5aSAndreas Boehler            // 100% accurate mysql query.
1678d5703f5aSAndreas Boehler            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end']))
1679d5703f5aSAndreas Boehler            {
1680d5703f5aSAndreas Boehler                $requirePostFilter = false;
1681d5703f5aSAndreas Boehler            }
1682d5703f5aSAndreas Boehler        }
1683d5703f5aSAndreas Boehler
1684d5703f5aSAndreas Boehler    }
1685d5703f5aSAndreas Boehler
1686d5703f5aSAndreas Boehler    if ($requirePostFilter)
1687d5703f5aSAndreas Boehler    {
1688d5703f5aSAndreas Boehler        $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?";
1689d5703f5aSAndreas Boehler    }
1690d5703f5aSAndreas Boehler    else
1691d5703f5aSAndreas Boehler    {
1692d5703f5aSAndreas Boehler        $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1693d5703f5aSAndreas Boehler    }
1694d5703f5aSAndreas Boehler
1695d5703f5aSAndreas Boehler    $values = array(
1696d5703f5aSAndreas Boehler        $calendarId
1697d5703f5aSAndreas Boehler    );
1698d5703f5aSAndreas Boehler
1699d5703f5aSAndreas Boehler    if ($componentType)
1700d5703f5aSAndreas Boehler    {
1701d5703f5aSAndreas Boehler        $query .= " AND componenttype = ?";
1702d5703f5aSAndreas Boehler        $values[] = $componentType;
1703d5703f5aSAndreas Boehler    }
1704d5703f5aSAndreas Boehler
1705d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['start'])
1706d5703f5aSAndreas Boehler    {
1707d5703f5aSAndreas Boehler        $query .= " AND lastoccurence > ?";
1708d5703f5aSAndreas Boehler        $values[] = $timeRange['start']->getTimeStamp();
1709d5703f5aSAndreas Boehler    }
1710d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['end'])
1711d5703f5aSAndreas Boehler    {
1712d5703f5aSAndreas Boehler        $query .= " AND firstoccurence < ?";
1713d5703f5aSAndreas Boehler        $values[] = $timeRange['end']->getTimeStamp();
1714d5703f5aSAndreas Boehler    }
1715d5703f5aSAndreas Boehler
1716d5703f5aSAndreas Boehler    $res = $this->sqlite->query($query, $values);
1717d5703f5aSAndreas Boehler    $arr = $this->sqlite->res2arr($res);
1718d5703f5aSAndreas Boehler
1719d5703f5aSAndreas Boehler    $result = array();
1720d5703f5aSAndreas Boehler    foreach($arr as $row)
1721d5703f5aSAndreas Boehler    {
1722d5703f5aSAndreas Boehler        if ($requirePostFilter)
1723d5703f5aSAndreas Boehler        {
1724d5703f5aSAndreas Boehler            if (!$this->validateFilterForObject($row, $filters))
1725d5703f5aSAndreas Boehler            {
1726d5703f5aSAndreas Boehler                continue;
1727d5703f5aSAndreas Boehler            }
1728d5703f5aSAndreas Boehler        }
1729d5703f5aSAndreas Boehler        $result[] = $row['uri'];
1730d5703f5aSAndreas Boehler
1731d5703f5aSAndreas Boehler    }
1732d5703f5aSAndreas Boehler
1733d5703f5aSAndreas Boehler    return $result;
1734d5703f5aSAndreas Boehler  }
1735d5703f5aSAndreas Boehler
1736d5703f5aSAndreas Boehler  /**
1737d5703f5aSAndreas Boehler   * This method validates if a filter (as passed to calendarQuery) matches
1738d5703f5aSAndreas Boehler   * the given object. Taken from Sabre's PDO backend
1739d5703f5aSAndreas Boehler   *
1740d5703f5aSAndreas Boehler   * @param array $object
1741d5703f5aSAndreas Boehler   * @param array $filters
1742d5703f5aSAndreas Boehler   * @return bool
1743d5703f5aSAndreas Boehler   */
1744d5703f5aSAndreas Boehler  protected function validateFilterForObject($object, $filters)
1745d5703f5aSAndreas Boehler  {
1746d5703f5aSAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1747d5703f5aSAndreas Boehler      // Unfortunately, setting the 'calendardata' here is optional. If
1748d5703f5aSAndreas Boehler      // it was excluded, we actually need another call to get this as
1749d5703f5aSAndreas Boehler      // well.
1750d5703f5aSAndreas Boehler      if (!isset($object['calendardata']))
1751d5703f5aSAndreas Boehler      {
1752d5703f5aSAndreas Boehler          $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']);
1753d5703f5aSAndreas Boehler      }
1754d5703f5aSAndreas Boehler
1755d5703f5aSAndreas Boehler      $vObject = \Sabre\VObject\Reader::read($object['calendardata']);
1756d5703f5aSAndreas Boehler      $validator = new \Sabre\CalDAV\CalendarQueryValidator();
1757d5703f5aSAndreas Boehler
175839787131SAndreas Boehler      $res = $validator->validate($vObject, $filters);
175939787131SAndreas Boehler      return $res;
1760d5703f5aSAndreas Boehler
1761d5703f5aSAndreas Boehler  }
1762d5703f5aSAndreas Boehler
1763d5703f5aSAndreas Boehler  /**
1764d5703f5aSAndreas Boehler   * Retrieve changes for a given calendar based on the given syncToken.
1765d5703f5aSAndreas Boehler   *
1766d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1767d5703f5aSAndreas Boehler   * @param int $syncToken The supplied sync token
1768d5703f5aSAndreas Boehler   * @param int $syncLevel The sync level
1769d5703f5aSAndreas Boehler   * @param int $limit The limit of changes
1770d5703f5aSAndreas Boehler   *
1771d5703f5aSAndreas Boehler   * @return array The result
1772d5703f5aSAndreas Boehler   */
1773d5703f5aSAndreas Boehler  public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null)
1774d5703f5aSAndreas Boehler  {
1775d5703f5aSAndreas Boehler      // Current synctoken
1776d5703f5aSAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
1777d5703f5aSAndreas Boehler
1778d5703f5aSAndreas Boehler      if ($currentToken === false) return null;
1779d5703f5aSAndreas Boehler
1780d5703f5aSAndreas Boehler      $result = array(
1781d5703f5aSAndreas Boehler          'syncToken' => $currentToken,
1782d5703f5aSAndreas Boehler          'added'     => array(),
1783d5703f5aSAndreas Boehler          'modified'  => array(),
1784d5703f5aSAndreas Boehler          'deleted'   => array(),
1785d5703f5aSAndreas Boehler      );
1786d5703f5aSAndreas Boehler
1787d5703f5aSAndreas Boehler      if ($syncToken)
1788d5703f5aSAndreas Boehler      {
1789d5703f5aSAndreas Boehler
1790d5703f5aSAndreas Boehler          $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
1791d5703f5aSAndreas Boehler          if ($limit > 0) $query .= " LIMIT " . (int)$limit;
1792d5703f5aSAndreas Boehler
1793d5703f5aSAndreas Boehler          // Fetching all changes
1794d5703f5aSAndreas Boehler          $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid);
1795d5703f5aSAndreas Boehler          if($res === false)
1796d5703f5aSAndreas Boehler              return null;
1797d5703f5aSAndreas Boehler
1798d5703f5aSAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1799d5703f5aSAndreas Boehler          $changes = array();
1800d5703f5aSAndreas Boehler
1801d5703f5aSAndreas Boehler          // This loop ensures that any duplicates are overwritten, only the
1802d5703f5aSAndreas Boehler          // last change on a node is relevant.
1803d5703f5aSAndreas Boehler          foreach($arr as $row)
1804d5703f5aSAndreas Boehler          {
1805d5703f5aSAndreas Boehler              $changes[$row['uri']] = $row['operation'];
1806d5703f5aSAndreas Boehler          }
1807d5703f5aSAndreas Boehler
1808d5703f5aSAndreas Boehler          foreach ($changes as $uri => $operation)
1809d5703f5aSAndreas Boehler          {
1810d5703f5aSAndreas Boehler              switch ($operation)
1811d5703f5aSAndreas Boehler              {
1812d5703f5aSAndreas Boehler                  case 1 :
1813d5703f5aSAndreas Boehler                      $result['added'][] = $uri;
1814d5703f5aSAndreas Boehler                      break;
1815d5703f5aSAndreas Boehler                  case 2 :
1816d5703f5aSAndreas Boehler                      $result['modified'][] = $uri;
1817d5703f5aSAndreas Boehler                      break;
1818d5703f5aSAndreas Boehler                  case 3 :
1819d5703f5aSAndreas Boehler                      $result['deleted'][] = $uri;
1820d5703f5aSAndreas Boehler                      break;
1821d5703f5aSAndreas Boehler              }
1822d5703f5aSAndreas Boehler
1823d5703f5aSAndreas Boehler          }
1824d5703f5aSAndreas Boehler      }
1825d5703f5aSAndreas Boehler      else
1826d5703f5aSAndreas Boehler      {
1827d5703f5aSAndreas Boehler          // No synctoken supplied, this is the initial sync.
1828d5703f5aSAndreas Boehler          $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1829d5703f5aSAndreas Boehler          $res = $this->sqlite->query($query);
1830d5703f5aSAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1831d5703f5aSAndreas Boehler
1832d5703f5aSAndreas Boehler          $result['added'] = $arr;
1833d5703f5aSAndreas Boehler      }
1834d5703f5aSAndreas Boehler      return $result;
1835d5703f5aSAndreas Boehler  }
1836d5703f5aSAndreas Boehler
1837a1a3b679SAndreas Boehler}
1838