xref: /plugin/davcal/helper.php (revision d71c99346f37a0cdcbce20489c6915b99714a98d)
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    if($conf['allowdebug'])
2321d04f73SAndreas Boehler        dbglog('---- DAVCAL helper.php init');
24a1a3b679SAndreas Boehler    if(!$this->sqlite)
25a1a3b679SAndreas Boehler    {
2621d04f73SAndreas Boehler        if($conf['allowdebug'])
2721d04f73SAndreas Boehler            dbglog('This plugin requires the sqlite plugin. Please install it.');
28a1a3b679SAndreas Boehler        msg('This plugin requires the sqlite plugin. Please install it.');
29a1a3b679SAndreas Boehler        return;
30a1a3b679SAndreas Boehler    }
31a1a3b679SAndreas Boehler
32a1a3b679SAndreas Boehler    if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
33a1a3b679SAndreas Boehler    {
3421d04f73SAndreas Boehler        if($conf['allowdebug'])
3521d04f73SAndreas Boehler            dbglog('Error initialising the SQLite DB for DAVCal');
36a1a3b679SAndreas Boehler        return;
37a1a3b679SAndreas Boehler    }
38a1a3b679SAndreas Boehler  }
39a1a3b679SAndreas Boehler
40cb71a62aSAndreas Boehler  /**
41185e2535SAndreas Boehler   * Retrieve meta data for a given page
42185e2535SAndreas Boehler   *
43185e2535SAndreas Boehler   * @param string $id optional The page ID
44185e2535SAndreas Boehler   * @return array The metadata
45185e2535SAndreas Boehler   */
46185e2535SAndreas Boehler  private function getMeta($id = null) {
47185e2535SAndreas Boehler    global $ID;
48185e2535SAndreas Boehler    global $INFO;
49185e2535SAndreas Boehler
50185e2535SAndreas Boehler    if ($id === null) $id = $ID;
51185e2535SAndreas Boehler
52185e2535SAndreas Boehler    if($ID === $id && $INFO['meta']) {
53185e2535SAndreas Boehler        $meta = $INFO['meta'];
54185e2535SAndreas Boehler    } else {
55185e2535SAndreas Boehler        $meta = p_get_metadata($id);
56185e2535SAndreas Boehler    }
57185e2535SAndreas Boehler
58185e2535SAndreas Boehler    return $meta;
59185e2535SAndreas Boehler  }
60185e2535SAndreas Boehler
61185e2535SAndreas Boehler  /**
62185e2535SAndreas Boehler   * Retrieve the meta data for a given page
63185e2535SAndreas Boehler   *
64185e2535SAndreas Boehler   * @param string $id optional The page ID
65185e2535SAndreas Boehler   * @return array with meta data
66185e2535SAndreas Boehler   */
67185e2535SAndreas Boehler  public function getCalendarMetaForPage($id = null)
68185e2535SAndreas Boehler  {
69185e2535SAndreas Boehler      if(is_null($id))
70185e2535SAndreas Boehler      {
71185e2535SAndreas Boehler          global $ID;
72185e2535SAndreas Boehler          $id = $ID;
73185e2535SAndreas Boehler      }
74185e2535SAndreas Boehler
75185e2535SAndreas Boehler      $meta = $this->getMeta($id);
76185e2535SAndreas Boehler      if(isset($meta['plugin_davcal']))
77185e2535SAndreas Boehler        return $meta['plugin_davcal'];
78185e2535SAndreas Boehler      else
79185e2535SAndreas Boehler        return array();
80185e2535SAndreas Boehler  }
81185e2535SAndreas Boehler
82185e2535SAndreas Boehler  /**
83*d71c9934SAndreas Boehler   * Check the permission of a user for a given calendar ID
84*d71c9934SAndreas Boehler   *
85*d71c9934SAndreas Boehler   * @param string $id The calendar ID to check
86*d71c9934SAndreas Boehler   * @return int AUTH_* constants
87*d71c9934SAndreas Boehler   */
88*d71c9934SAndreas Boehler  public function checkCalendarPermission($id)
89*d71c9934SAndreas Boehler  {
90*d71c9934SAndreas Boehler      if(strpos($page, 'webdav://') === 0)
91*d71c9934SAndreas Boehler      {
92*d71c9934SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
93*d71c9934SAndreas Boehler          if(is_null($wdc))
94*d71c9934SAndreas Boehler            return AUTH_NONE;
95*d71c9934SAndreas Boehler          $connectionId = str_replace('webdav://', '', $page);
96*d71c9934SAndreas Boehler          $settings = $wdc->getConnection($connectionId);
97*d71c9934SAndreas Boehler          if($settings === false)
98*d71c9934SAndreas Boehler            return AUTH_NONE;
99*d71c9934SAndreas Boehler          if($settings['write'] === '1')
100*d71c9934SAndreas Boehler            return AUTH_CREATE;
101*d71c9934SAndreas Boehler          return AUTH_READ;
102*d71c9934SAndreas Boehler      }
103*d71c9934SAndreas Boehler      else
104*d71c9934SAndreas Boehler      {
105*d71c9934SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
106*d71c9934SAndreas Boehler          // We return AUTH_READ if the calendar does not exist. This makes
107*d71c9934SAndreas Boehler          // davcal happy when there are just included calendars
108*d71c9934SAndreas Boehler          if($calid === false)
109*d71c9934SAndreas Boehler            return AUTH_READ;
110*d71c9934SAndreas Boehler          return auth_quickaclcheck($id);
111*d71c9934SAndreas Boehler      }
112*d71c9934SAndreas Boehler  }
113*d71c9934SAndreas Boehler
114*d71c9934SAndreas Boehler  /**
11580e1ddf7SAndreas Boehler   * Filter calendar pages and return only those where the current
11680e1ddf7SAndreas Boehler   * user has at least read permission.
11780e1ddf7SAndreas Boehler   *
11880e1ddf7SAndreas Boehler   * @param array $calendarPages Array with calendar pages to check
11980e1ddf7SAndreas Boehler   * @return array with filtered calendar pages
12080e1ddf7SAndreas Boehler   */
12180e1ddf7SAndreas Boehler  public function filterCalendarPagesByUserPermission($calendarPages)
12280e1ddf7SAndreas Boehler  {
12380e1ddf7SAndreas Boehler      $retList = array();
12480e1ddf7SAndreas Boehler      foreach($calendarPages as $page => $data)
12580e1ddf7SAndreas Boehler      {
1260b805092SAndreas Boehler          // WebDAV Connections are always readable
1270b805092SAndreas Boehler          if(strpos($page, 'webdav://') === 0)
1280b805092SAndreas Boehler          {
1290b805092SAndreas Boehler              $retList[$page] = $data;
1300b805092SAndreas Boehler          }
1310b805092SAndreas Boehler          elseif(auth_quickaclcheck($page) >= AUTH_READ)
13280e1ddf7SAndreas Boehler          {
13380e1ddf7SAndreas Boehler              $retList[$page] = $data;
13480e1ddf7SAndreas Boehler          }
13580e1ddf7SAndreas Boehler      }
13680e1ddf7SAndreas Boehler      return $retList;
13780e1ddf7SAndreas Boehler  }
13880e1ddf7SAndreas Boehler
13980e1ddf7SAndreas Boehler  /**
140185e2535SAndreas Boehler   * Get all calendar pages used by a given page
141185e2535SAndreas Boehler   * based on the stored metadata
142185e2535SAndreas Boehler   *
143185e2535SAndreas Boehler   * @param string $id optional The page id
144185e2535SAndreas Boehler   * @return mixed The pages as array or false
145185e2535SAndreas Boehler   */
146185e2535SAndreas Boehler  public function getCalendarPagesByMeta($id = null)
147185e2535SAndreas Boehler  {
148185e2535SAndreas Boehler      if(is_null($id))
149185e2535SAndreas Boehler      {
150185e2535SAndreas Boehler          global $ID;
151185e2535SAndreas Boehler          $id = $ID;
152185e2535SAndreas Boehler      }
153185e2535SAndreas Boehler
154185e2535SAndreas Boehler      $meta = $this->getCalendarMetaForPage($id);
1550b805092SAndreas Boehler
156185e2535SAndreas Boehler      if(isset($meta['id']))
157ed764890SAndreas Boehler      {
158ed764890SAndreas Boehler          // Filter the list of pages by permission
15980e1ddf7SAndreas Boehler          $pages = $this->filterCalendarPagesByUserPermission($meta['id']);
16080e1ddf7SAndreas Boehler          if(empty($pages))
161ed764890SAndreas Boehler            return false;
16280e1ddf7SAndreas Boehler          return $pages;
163ed764890SAndreas Boehler      }
164185e2535SAndreas Boehler      return false;
165185e2535SAndreas Boehler  }
166185e2535SAndreas Boehler
167185e2535SAndreas Boehler  /**
168185e2535SAndreas Boehler   * Get a list of calendar names/pages/ids/colors
169185e2535SAndreas Boehler   * for an array of page ids
170185e2535SAndreas Boehler   *
171185e2535SAndreas Boehler   * @param array $calendarPages The calendar pages to retrieve
172185e2535SAndreas Boehler   * @return array The list
173185e2535SAndreas Boehler   */
174185e2535SAndreas Boehler  public function getCalendarMapForIDs($calendarPages)
175185e2535SAndreas Boehler  {
176185e2535SAndreas Boehler      $data = array();
1774a2bf5eeSAndreas Boehler      foreach($calendarPages as $page => $color)
178185e2535SAndreas Boehler      {
1790b805092SAndreas Boehler            if(strpos($page, 'webdav://') === 0)
1800b805092SAndreas Boehler            {
1810b805092SAndreas Boehler                $wdc =& plugin_load('helper', 'webdavclient');
1820b805092SAndreas Boehler                if(is_null($wdc))
1830b805092SAndreas Boehler                    continue;
1840b805092SAndreas Boehler                $connectionId = str_replace('webdav://', '', $page);
1850b805092SAndreas Boehler                $settings = $wdc->getConnection($connectionId);
1862393a702SAndreas Boehler                if($settings === false)
1872393a702SAndreas Boehler                    continue;
1880b805092SAndreas Boehler                $name = $settings['displayname'];
189*d71c9934SAndreas Boehler                $write = ($settings['write'] === '1');
1900b805092SAndreas Boehler                $calid = $connectionId;
1910b805092SAndreas Boehler            }
1920b805092SAndreas Boehler            else
1930b805092SAndreas Boehler            {
194185e2535SAndreas Boehler                $calid = $this->getCalendarIdForPage($page);
195185e2535SAndreas Boehler                if($calid !== false)
196185e2535SAndreas Boehler                {
197185e2535SAndreas Boehler                    $settings = $this->getCalendarSettings($calid);
198185e2535SAndreas Boehler                    $name = $settings['displayname'];
1990b805092SAndreas Boehler                    //$color = $settings['calendarcolor'];
200ed764890SAndreas Boehler                    $write = (auth_quickaclcheck($page) > AUTH_READ);
2010b805092SAndreas Boehler                }
2020b805092SAndreas Boehler                else
2030b805092SAndreas Boehler                {
2040b805092SAndreas Boehler                    continue;
2050b805092SAndreas Boehler                }
2060b805092SAndreas Boehler            }
207185e2535SAndreas Boehler            $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
208ed764890SAndreas Boehler                            'color' => $color, 'write' => $write);
209185e2535SAndreas Boehler      }
210185e2535SAndreas Boehler      return $data;
211185e2535SAndreas Boehler  }
212185e2535SAndreas Boehler
213185e2535SAndreas Boehler  /**
214185e2535SAndreas Boehler   * Get the saved calendar color for a given page.
215185e2535SAndreas Boehler   *
216185e2535SAndreas Boehler   * @param string $id optional The page ID
217185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
218185e2535SAndreas Boehler   */
219185e2535SAndreas Boehler  public function getCalendarColorForPage($id = null)
220185e2535SAndreas Boehler  {
221185e2535SAndreas Boehler      if(is_null($id))
222185e2535SAndreas Boehler      {
223185e2535SAndreas Boehler          global $ID;
224185e2535SAndreas Boehler          $id = $ID;
225185e2535SAndreas Boehler      }
226185e2535SAndreas Boehler
227185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
228185e2535SAndreas Boehler      if($calid === false)
229185e2535SAndreas Boehler        return false;
230185e2535SAndreas Boehler
231185e2535SAndreas Boehler      return $this->getCalendarColorForCalendar($calid);
232185e2535SAndreas Boehler  }
233185e2535SAndreas Boehler
234185e2535SAndreas Boehler  /**
235185e2535SAndreas Boehler   * Get the saved calendar color for a given calendar ID.
236185e2535SAndreas Boehler   *
237185e2535SAndreas Boehler   * @param string $id optional The calendar ID
238185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
239185e2535SAndreas Boehler   */
240185e2535SAndreas Boehler  public function getCalendarColorForCalendar($calid)
241185e2535SAndreas Boehler  {
242185e2535SAndreas Boehler      if(isset($this->cachedValues['calendarcolor'][$calid]))
243185e2535SAndreas Boehler        return $this->cachedValues['calendarcolor'][$calid];
244185e2535SAndreas Boehler
245185e2535SAndreas Boehler      $row = $this->getCalendarSettings($calid);
246185e2535SAndreas Boehler
247185e2535SAndreas Boehler      if(!isset($row['calendarcolor']))
248185e2535SAndreas Boehler        return false;
249185e2535SAndreas Boehler
250185e2535SAndreas Boehler      $color = $row['calendarcolor'];
251185e2535SAndreas Boehler      $this->cachedValues['calendarcolor'][$calid] = $color;
252185e2535SAndreas Boehler      return $color;
253185e2535SAndreas Boehler  }
254185e2535SAndreas Boehler
255185e2535SAndreas Boehler  /**
256e86c8dd3SAndreas Boehler   * Get the user's principal URL for iOS sync
257e86c8dd3SAndreas Boehler   * @param string $user the user name
258e86c8dd3SAndreas Boehler   * @return the URL to the principal sync
259e86c8dd3SAndreas Boehler   */
260e86c8dd3SAndreas Boehler  public function getPrincipalUrlForUser($user)
261e86c8dd3SAndreas Boehler  {
262e86c8dd3SAndreas Boehler      if(is_null($user))
263e86c8dd3SAndreas Boehler        return false;
264e86c8dd3SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user;
265e86c8dd3SAndreas Boehler      return $url;
266e86c8dd3SAndreas Boehler  }
267e86c8dd3SAndreas Boehler
268e86c8dd3SAndreas Boehler  /**
269185e2535SAndreas Boehler   * Set the calendar color for a given page.
270185e2535SAndreas Boehler   *
271185e2535SAndreas Boehler   * @param string $color The color definition
272185e2535SAndreas Boehler   * @param string $id optional The page ID
273185e2535SAndreas Boehler   * @return boolean True on success, otherwise false
274185e2535SAndreas Boehler   */
275185e2535SAndreas Boehler  public function setCalendarColorForPage($color, $id = null)
276185e2535SAndreas Boehler  {
277185e2535SAndreas Boehler      if(is_null($id))
278185e2535SAndreas Boehler      {
279185e2535SAndreas Boehler          global $ID;
280185e2535SAndreas Boehler          $id = $ID;
281185e2535SAndreas Boehler      }
282185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
283185e2535SAndreas Boehler      if($calid === false)
284185e2535SAndreas Boehler        return false;
285185e2535SAndreas Boehler
28651f4febbSAndreas Boehler      $query = "UPDATE calendars SET calendarcolor = ? ".
28751f4febbSAndreas Boehler               " WHERE id = ?";
28851f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $color, $calid);
289185e2535SAndreas Boehler      if($res !== false)
290185e2535SAndreas Boehler      {
291185e2535SAndreas Boehler        $this->cachedValues['calendarcolor'][$calid] = $color;
292185e2535SAndreas Boehler        return true;
293185e2535SAndreas Boehler      }
294185e2535SAndreas Boehler      return false;
295185e2535SAndreas Boehler  }
296185e2535SAndreas Boehler
297185e2535SAndreas Boehler  /**
298cb71a62aSAndreas Boehler   * Set the calendar name and description for a given page with a given
299cb71a62aSAndreas Boehler   * page id.
300cb71a62aSAndreas Boehler   * If the calendar doesn't exist, the calendar is created!
301cb71a62aSAndreas Boehler   *
302cb71a62aSAndreas Boehler   * @param string  $name The name of the new calendar
303cb71a62aSAndreas Boehler   * @param string  $description The description of the new calendar
304cb71a62aSAndreas Boehler   * @param string  $id (optional) The ID of the page
305cb71a62aSAndreas Boehler   * @param string  $userid The userid of the creating user
306cb71a62aSAndreas Boehler   *
307cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false.
308cb71a62aSAndreas Boehler   */
309a1a3b679SAndreas Boehler  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
310a1a3b679SAndreas Boehler  {
311a1a3b679SAndreas Boehler      if(is_null($id))
312a1a3b679SAndreas Boehler      {
313a1a3b679SAndreas Boehler          global $ID;
314a1a3b679SAndreas Boehler          $id = $ID;
315a1a3b679SAndreas Boehler      }
316a1a3b679SAndreas Boehler      if(is_null($userid))
31734a47953SAndreas Boehler      {
31834a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
31934a47953SAndreas Boehler        {
320a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
32134a47953SAndreas Boehler        }
32234a47953SAndreas Boehler        else
32334a47953SAndreas Boehler        {
32434a47953SAndreas Boehler          $userid = uniqid('davcal-');
32534a47953SAndreas Boehler        }
32634a47953SAndreas Boehler      }
327a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
328a1a3b679SAndreas Boehler      if($calid === false)
329a1a3b679SAndreas Boehler        return $this->createCalendarForPage($name, $description, $id, $userid);
330a1a3b679SAndreas Boehler
33151f4febbSAndreas Boehler      $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?";
33251f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $name, $description, $calid);
333b269830cSAndreas Boehler      if($res !== false)
334b269830cSAndreas Boehler        return true;
335b269830cSAndreas Boehler      return false;
336a1a3b679SAndreas Boehler  }
337a1a3b679SAndreas Boehler
338cb71a62aSAndreas Boehler  /**
339d5703f5aSAndreas Boehler   * Update a calendar's displayname
340d5703f5aSAndreas Boehler   *
341d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
342d5703f5aSAndreas Boehler   * @param string $name The new calendar name
343d5703f5aSAndreas Boehler   *
344d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
345d5703f5aSAndreas Boehler   */
346d5703f5aSAndreas Boehler  public function updateCalendarName($calid, $name)
347d5703f5aSAndreas Boehler  {
348d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET displayname = ? WHERE id = ?";
349d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $name);
350d5703f5aSAndreas Boehler      if($res !== false)
351d5703f5aSAndreas Boehler      {
352d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
353d5703f5aSAndreas Boehler        return true;
354d5703f5aSAndreas Boehler      }
355d5703f5aSAndreas Boehler      return false;
356d5703f5aSAndreas Boehler  }
357d5703f5aSAndreas Boehler
358d5703f5aSAndreas Boehler  /**
359d5703f5aSAndreas Boehler   * Update the calendar description
360d5703f5aSAndreas Boehler   *
361d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
362d5703f5aSAndreas Boehler   * @param string $description The new calendar's description
363d5703f5aSAndreas Boehler   *
364d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
365d5703f5aSAndreas Boehler   */
366d5703f5aSAndreas Boehler  public function updateCalendarDescription($calid, $description)
367d5703f5aSAndreas Boehler  {
368d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET description = ? WHERE id = ?";
369d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $description);
370d5703f5aSAndreas Boehler      if($res !== false)
371d5703f5aSAndreas Boehler      {
372d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
373d5703f5aSAndreas Boehler        return true;
374d5703f5aSAndreas Boehler      }
375d5703f5aSAndreas Boehler      return false;
376d5703f5aSAndreas Boehler  }
377d5703f5aSAndreas Boehler
378d5703f5aSAndreas Boehler  /**
379d5703f5aSAndreas Boehler   * Update a calendar's timezone information
380d5703f5aSAndreas Boehler   *
381d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
382d5703f5aSAndreas Boehler   * @param string $timezone The new timezone to set
383d5703f5aSAndreas Boehler   *
384d5703f5aSAndreas Boehler   * @return boolean True on success, otherwise false
385d5703f5aSAndreas Boehler   */
386d5703f5aSAndreas Boehler  public function updateCalendarTimezone($calid, $timezone)
387d5703f5aSAndreas Boehler  {
388d5703f5aSAndreas Boehler      $query = "UPDATE calendars SET timezone = ? WHERE id = ?";
389d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $timezone);
390d5703f5aSAndreas Boehler      if($res !== false)
391d5703f5aSAndreas Boehler      {
392d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
393d5703f5aSAndreas Boehler        return true;
394d5703f5aSAndreas Boehler      }
395d5703f5aSAndreas Boehler      return false;
396d5703f5aSAndreas Boehler  }
397d5703f5aSAndreas Boehler
398d5703f5aSAndreas Boehler  /**
399cb71a62aSAndreas Boehler   * Save the personal settings to the SQLite database 'calendarsettings'.
400cb71a62aSAndreas Boehler   *
401cb71a62aSAndreas Boehler   * @param array  $settings The settings array to store
402cb71a62aSAndreas Boehler   * @param string $userid (optional) The userid to store
403cb71a62aSAndreas Boehler   *
404cb71a62aSAndreas Boehler   * @param boolean True on success, otherwise false
405cb71a62aSAndreas Boehler   */
406a495d34cSAndreas Boehler  public function savePersonalSettings($settings, $userid = null)
407a495d34cSAndreas Boehler  {
408a495d34cSAndreas Boehler      if(is_null($userid))
40934a47953SAndreas Boehler      {
41034a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
41134a47953SAndreas Boehler          {
412a495d34cSAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
41334a47953SAndreas Boehler          }
41434a47953SAndreas Boehler          else
41534a47953SAndreas Boehler          {
41634a47953SAndreas Boehler              return false;
41734a47953SAndreas Boehler          }
41834a47953SAndreas Boehler      }
419a495d34cSAndreas Boehler      $this->sqlite->query("BEGIN TRANSACTION");
420a495d34cSAndreas Boehler
42151f4febbSAndreas Boehler      $query = "DELETE FROM calendarsettings WHERE userid = ?";
42251f4febbSAndreas Boehler      $this->sqlite->query($query, $userid);
423bd883736SAndreas Boehler
424a495d34cSAndreas Boehler      foreach($settings as $key => $value)
425a495d34cSAndreas Boehler      {
42651f4febbSAndreas Boehler          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)";
42751f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $userid, $key, $value);
428a495d34cSAndreas Boehler          if($res === false)
429a495d34cSAndreas Boehler              return false;
430a495d34cSAndreas Boehler      }
431a495d34cSAndreas Boehler      $this->sqlite->query("COMMIT TRANSACTION");
432185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
433a495d34cSAndreas Boehler      return true;
434a495d34cSAndreas Boehler  }
435a495d34cSAndreas Boehler
436cb71a62aSAndreas Boehler  /**
437cb71a62aSAndreas Boehler   * Retrieve the settings array for a given user id.
438cb71a62aSAndreas Boehler   * Some sane defaults are returned, currently:
439cb71a62aSAndreas Boehler   *
440cb71a62aSAndreas Boehler   *    timezone    => local
441cb71a62aSAndreas Boehler   *    weeknumbers => 0
442cb71a62aSAndreas Boehler   *    workweek    => 0
443cb71a62aSAndreas Boehler   *
444cb71a62aSAndreas Boehler   * @param string $userid (optional) The user id to retrieve
445cb71a62aSAndreas Boehler   *
446cb71a62aSAndreas Boehler   * @return array The settings array
447cb71a62aSAndreas Boehler   */
448a495d34cSAndreas Boehler  public function getPersonalSettings($userid = null)
449a495d34cSAndreas Boehler  {
450bd883736SAndreas Boehler      // Some sane default settings
451bd883736SAndreas Boehler      $settings = array(
452fb813b30SAndreas Boehler        'timezone' => $this->getConf('timezone'),
453fb813b30SAndreas Boehler        'weeknumbers' => $this->getConf('weeknumbers'),
454fb813b30SAndreas Boehler        'workweek' => $this->getConf('workweek'),
4551d5bdcd0SAndreas Boehler        'monday' => $this->getConf('monday'),
4561d5bdcd0SAndreas Boehler        'timeformat' => $this->getConf('timeformat')
457bd883736SAndreas Boehler      );
45834a47953SAndreas Boehler      if(is_null($userid))
45934a47953SAndreas Boehler      {
46034a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
46134a47953SAndreas Boehler          {
46234a47953SAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
46334a47953SAndreas Boehler          }
46434a47953SAndreas Boehler          else
46534a47953SAndreas Boehler          {
46634a47953SAndreas Boehler            return $settings;
46734a47953SAndreas Boehler          }
46834a47953SAndreas Boehler      }
46934a47953SAndreas Boehler
47034a47953SAndreas Boehler      if(isset($this->cachedValues['settings'][$userid]))
47134a47953SAndreas Boehler        return $this->cachedValues['settings'][$userid];
47251f4febbSAndreas Boehler      $query = "SELECT key, value FROM calendarsettings WHERE userid = ?";
47351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $userid);
474a495d34cSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
475a495d34cSAndreas Boehler      foreach($arr as $row)
476a495d34cSAndreas Boehler      {
477a495d34cSAndreas Boehler          $settings[$row['key']] = $row['value'];
478a495d34cSAndreas Boehler      }
479185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
480a495d34cSAndreas Boehler      return $settings;
481a495d34cSAndreas Boehler  }
482a495d34cSAndreas Boehler
483cb71a62aSAndreas Boehler  /**
484cb71a62aSAndreas Boehler   * Retrieve the calendar ID based on a page ID from the SQLite table
485cb71a62aSAndreas Boehler   * 'pagetocalendarmapping'.
486cb71a62aSAndreas Boehler   *
487cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to retrieve the corresponding calendar
488cb71a62aSAndreas Boehler   *
489cb71a62aSAndreas Boehler   * @return mixed the ID on success, otherwise false
490cb71a62aSAndreas Boehler   */
491a1a3b679SAndreas Boehler  public function getCalendarIdForPage($id = null)
492a1a3b679SAndreas Boehler  {
493a1a3b679SAndreas Boehler      if(is_null($id))
494a1a3b679SAndreas Boehler      {
495a1a3b679SAndreas Boehler          global $ID;
496a1a3b679SAndreas Boehler          $id = $ID;
497a1a3b679SAndreas Boehler      }
498a1a3b679SAndreas Boehler
499185e2535SAndreas Boehler      if(isset($this->cachedValues['calid'][$id]))
500185e2535SAndreas Boehler        return $this->cachedValues['calid'][$id];
501185e2535SAndreas Boehler
50251f4febbSAndreas Boehler      $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?";
50351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $id);
504a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
505a1a3b679SAndreas Boehler      if(isset($row['calid']))
506185e2535SAndreas Boehler      {
507185e2535SAndreas Boehler        $calid = $row['calid'];
508185e2535SAndreas Boehler        $this->cachedValues['calid'] = $calid;
509185e2535SAndreas Boehler        return $calid;
510185e2535SAndreas Boehler      }
511a1a3b679SAndreas Boehler      return false;
512a1a3b679SAndreas Boehler  }
513a1a3b679SAndreas Boehler
514cb71a62aSAndreas Boehler  /**
515cb71a62aSAndreas Boehler   * Retrieve the complete calendar id to page mapping.
516cb71a62aSAndreas Boehler   * This is necessary to be able to retrieve a list of
517cb71a62aSAndreas Boehler   * calendars for a given user and check the access rights.
518cb71a62aSAndreas Boehler   *
519cb71a62aSAndreas Boehler   * @return array The mapping array
520cb71a62aSAndreas Boehler   */
521a1a3b679SAndreas Boehler  public function getCalendarIdToPageMapping()
522a1a3b679SAndreas Boehler  {
523a1a3b679SAndreas Boehler      $query = "SELECT calid, page FROM pagetocalendarmapping";
524a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
525a1a3b679SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
526a1a3b679SAndreas Boehler      return $arr;
527a1a3b679SAndreas Boehler  }
528a1a3b679SAndreas Boehler
529cb71a62aSAndreas Boehler  /**
530cb71a62aSAndreas Boehler   * Retrieve all calendar IDs a given user has access to.
531cb71a62aSAndreas Boehler   * The user is specified by the principalUri, so the
532cb71a62aSAndreas Boehler   * user name is actually split from the URI component.
533cb71a62aSAndreas Boehler   *
534cb71a62aSAndreas Boehler   * Access rights are checked against DokuWiki's ACL
535cb71a62aSAndreas Boehler   * and applied accordingly.
536cb71a62aSAndreas Boehler   *
537cb71a62aSAndreas Boehler   * @param string $principalUri The principal URI to work on
538cb71a62aSAndreas Boehler   *
539cb71a62aSAndreas Boehler   * @return array An associative array of calendar IDs
540cb71a62aSAndreas Boehler   */
541a1a3b679SAndreas Boehler  public function getCalendarIdsForUser($principalUri)
542a1a3b679SAndreas Boehler  {
54334a47953SAndreas Boehler      global $auth;
544a1a3b679SAndreas Boehler      $user = explode('/', $principalUri);
545a1a3b679SAndreas Boehler      $user = end($user);
546a1a3b679SAndreas Boehler      $mapping = $this->getCalendarIdToPageMapping();
547a1a3b679SAndreas Boehler      $calids = array();
54834a47953SAndreas Boehler      $ud = $auth->getUserData($user);
54934a47953SAndreas Boehler      $groups = $ud['grps'];
550a1a3b679SAndreas Boehler      foreach($mapping as $row)
551a1a3b679SAndreas Boehler      {
552a1a3b679SAndreas Boehler          $id = $row['calid'];
553a1a3b679SAndreas Boehler          $page = $row['page'];
55434a47953SAndreas Boehler          $acl = auth_aclcheck($page, $user, $groups);
555a1a3b679SAndreas Boehler          if($acl >= AUTH_READ)
556a1a3b679SAndreas Boehler          {
557a1a3b679SAndreas Boehler              $write = $acl > AUTH_READ;
558a1a3b679SAndreas Boehler              $calids[$id] = array('readonly' => !$write);
559a1a3b679SAndreas Boehler          }
560a1a3b679SAndreas Boehler      }
561a1a3b679SAndreas Boehler      return $calids;
562a1a3b679SAndreas Boehler  }
563a1a3b679SAndreas Boehler
564cb71a62aSAndreas Boehler  /**
565cb71a62aSAndreas Boehler   * Create a new calendar for a given page ID and set name and description
566cb71a62aSAndreas Boehler   * accordingly. Also update the pagetocalendarmapping table on success.
567cb71a62aSAndreas Boehler   *
568cb71a62aSAndreas Boehler   * @param string $name The calendar's name
569cb71a62aSAndreas Boehler   * @param string $description The calendar's description
570cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to work on
571cb71a62aSAndreas Boehler   * @param string $userid (optional) The user ID that created the calendar
572cb71a62aSAndreas Boehler   *
573cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
574cb71a62aSAndreas Boehler   */
575a1a3b679SAndreas Boehler  public function createCalendarForPage($name, $description, $id = null, $userid = null)
576a1a3b679SAndreas Boehler  {
577a1a3b679SAndreas Boehler      if(is_null($id))
578a1a3b679SAndreas Boehler      {
579a1a3b679SAndreas Boehler          global $ID;
580a1a3b679SAndreas Boehler          $id = $ID;
581a1a3b679SAndreas Boehler      }
582a1a3b679SAndreas Boehler      if(is_null($userid))
58334a47953SAndreas Boehler      {
58434a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
58534a47953SAndreas Boehler        {
586a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
58734a47953SAndreas Boehler        }
58834a47953SAndreas Boehler        else
58934a47953SAndreas Boehler        {
59034a47953SAndreas Boehler          $userid = uniqid('davcal-');
59134a47953SAndreas Boehler        }
59234a47953SAndreas Boehler      }
593a1a3b679SAndreas Boehler      $values = array('principals/'.$userid,
594a1a3b679SAndreas Boehler                      $name,
595a1a3b679SAndreas Boehler                      str_replace(array('/', ' ', ':'), '_', $id),
596a1a3b679SAndreas Boehler                      $description,
597a1a3b679SAndreas Boehler                      'VEVENT,VTODO',
59855a741c0SAndreas Boehler                      0,
59955a741c0SAndreas Boehler                      1);
60051f4febbSAndreas Boehler      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ".
60151f4febbSAndreas Boehler               "VALUES (?, ?, ?, ?, ?, ?, ?)";
60251f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]);
60355a741c0SAndreas Boehler      if($res === false)
60455a741c0SAndreas Boehler        return false;
605cb71a62aSAndreas Boehler
606cb71a62aSAndreas Boehler      // Get the new calendar ID
60751f4febbSAndreas Boehler      $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ".
60851f4febbSAndreas Boehler               "uri = ? AND description = ?";
60951f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]);
610a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
611cb71a62aSAndreas Boehler
612cb71a62aSAndreas Boehler      // Update the pagetocalendarmapping table with the new calendar ID
613a1a3b679SAndreas Boehler      if(isset($row['id']))
614a1a3b679SAndreas Boehler      {
61551f4febbSAndreas Boehler          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)";
61651f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $id, $row['id']);
61755a741c0SAndreas Boehler          return ($res !== false);
618a1a3b679SAndreas Boehler      }
619a1a3b679SAndreas Boehler
620a1a3b679SAndreas Boehler      return false;
621a1a3b679SAndreas Boehler  }
622a1a3b679SAndreas Boehler
623cb71a62aSAndreas Boehler  /**
624d5703f5aSAndreas Boehler   * Add a new calendar entry to the given calendar. Calendar data is
625d5703f5aSAndreas Boehler   * specified as ICS file, thus it needs to be parsed first.
626d5703f5aSAndreas Boehler   *
627d5703f5aSAndreas Boehler   * This is mainly needed for the sync support.
628d5703f5aSAndreas Boehler   *
629d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
630d5703f5aSAndreas Boehler   * @param string $uri The new object URI
631d5703f5aSAndreas Boehler   * @param string $ics The ICS file
632d5703f5aSAndreas Boehler   *
633d5703f5aSAndreas Boehler   * @return mixed The etag.
634d5703f5aSAndreas Boehler   */
635d5703f5aSAndreas Boehler  public function addCalendarEntryToCalendarByICS($calid, $uri, $ics)
636d5703f5aSAndreas Boehler  {
637d5703f5aSAndreas Boehler    $extraData = $this->getDenormalizedData($ics);
638d5703f5aSAndreas Boehler
639d5703f5aSAndreas Boehler    $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)";
640d5703f5aSAndreas Boehler    $res = $this->sqlite->query($query,
641d5703f5aSAndreas Boehler            $calid,
642d5703f5aSAndreas Boehler            $uri,
643d5703f5aSAndreas Boehler            $ics,
644d5703f5aSAndreas Boehler            time(),
645d5703f5aSAndreas Boehler            $extraData['etag'],
646d5703f5aSAndreas Boehler            $extraData['size'],
647d5703f5aSAndreas Boehler            $extraData['componentType'],
648d5703f5aSAndreas Boehler            $extraData['firstOccurence'],
649d5703f5aSAndreas Boehler            $extraData['lastOccurence'],
650d5703f5aSAndreas Boehler            $extraData['uid']);
651d5703f5aSAndreas Boehler            // If successfully, update the sync token database
652d5703f5aSAndreas Boehler    if($res !== false)
653d5703f5aSAndreas Boehler    {
654d5703f5aSAndreas Boehler        $this->updateSyncTokenLog($calid, $uri, 'added');
655d5703f5aSAndreas Boehler    }
656d5703f5aSAndreas Boehler    return $extraData['etag'];
657d5703f5aSAndreas Boehler  }
658d5703f5aSAndreas Boehler
659d5703f5aSAndreas Boehler  /**
660d5703f5aSAndreas Boehler   * Edit a calendar entry by providing a new ICS file. This is mainly
661d5703f5aSAndreas Boehler   * needed for the sync support.
662d5703f5aSAndreas Boehler   *
663d5703f5aSAndreas Boehler   * @param int $calid The calendar's IS
664d5703f5aSAndreas Boehler   * @param string $uri The object's URI to modify
665d5703f5aSAndreas Boehler   * @param string $ics The new object's ICS file
666d5703f5aSAndreas Boehler   */
667d5703f5aSAndreas Boehler  public function editCalendarEntryToCalendarByICS($calid, $uri, $ics)
668d5703f5aSAndreas Boehler  {
669d5703f5aSAndreas Boehler      $extraData = $this->getDenormalizedData($ics);
670d5703f5aSAndreas Boehler
671d5703f5aSAndreas Boehler      $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?";
672d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query,
673d5703f5aSAndreas Boehler        $ics,
674d5703f5aSAndreas Boehler        time(),
675d5703f5aSAndreas Boehler        $extraData['etag'],
676d5703f5aSAndreas Boehler        $extraData['size'],
677d5703f5aSAndreas Boehler        $extraData['componentType'],
678d5703f5aSAndreas Boehler        $extraData['firstOccurence'],
679d5703f5aSAndreas Boehler        $extraData['lastOccurence'],
680d5703f5aSAndreas Boehler        $extraData['uid'],
681d5703f5aSAndreas Boehler        $calid,
682d5703f5aSAndreas Boehler        $uri
683d5703f5aSAndreas Boehler      );
684d5703f5aSAndreas Boehler      if($res !== false)
685d5703f5aSAndreas Boehler      {
686d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'modified');
687d5703f5aSAndreas Boehler      }
688d5703f5aSAndreas Boehler      return $extraData['etag'];
689d5703f5aSAndreas Boehler  }
690d5703f5aSAndreas Boehler
691d5703f5aSAndreas Boehler  /**
692cb71a62aSAndreas Boehler   * Add a new iCal entry for a given page, i.e. a given calendar.
693cb71a62aSAndreas Boehler   *
694cb71a62aSAndreas Boehler   * The parameter array needs to contain
695cb71a62aSAndreas Boehler   *   detectedtz       => The timezone as detected by the browser
69682a48dfbSAndreas Boehler   *   currenttz        => The timezone in use by the calendar
697cb71a62aSAndreas Boehler   *   eventfrom        => The event's start date
698cb71a62aSAndreas Boehler   *   eventfromtime    => The event's start time
699cb71a62aSAndreas Boehler   *   eventto          => The event's end date
700cb71a62aSAndreas Boehler   *   eventtotime      => The event's end time
701cb71a62aSAndreas Boehler   *   eventname        => The event's name
702cb71a62aSAndreas Boehler   *   eventdescription => The event's description
703cb71a62aSAndreas Boehler   *
704cb71a62aSAndreas Boehler   * @param string $id The page ID to work on
705cb71a62aSAndreas Boehler   * @param string $user The user who created the calendar
706cb71a62aSAndreas Boehler   * @param string $params A parameter array with values to create
707cb71a62aSAndreas Boehler   *
708cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
709cb71a62aSAndreas Boehler   */
710a1a3b679SAndreas Boehler  public function addCalendarEntryToCalendarForPage($id, $user, $params)
711a1a3b679SAndreas Boehler  {
71282a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
71382a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
71482a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
715a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
716bd883736SAndreas Boehler      else
717bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
718cb71a62aSAndreas Boehler
719cb71a62aSAndreas Boehler      // Retrieve dates from settings
720b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
721b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
722b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
723b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
724cb71a62aSAndreas Boehler
725cb71a62aSAndreas Boehler      // Load SabreDAV
7269bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
727a1a3b679SAndreas Boehler      $vcalendar = new \Sabre\VObject\Component\VCalendar();
728cb71a62aSAndreas Boehler
729cb71a62aSAndreas Boehler      // Add VCalendar, UID and Event Name
730a1a3b679SAndreas Boehler      $event = $vcalendar->add('VEVENT');
731b269830cSAndreas Boehler      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
732b269830cSAndreas Boehler      $event->add('UID', $uuid);
733a1a3b679SAndreas Boehler      $event->summary = $params['eventname'];
734cb71a62aSAndreas Boehler
735cb71a62aSAndreas Boehler      // Add a description if requested
7360eebc909SAndreas Boehler      $description = $params['eventdescription'];
7370eebc909SAndreas Boehler      if($description !== '')
7380eebc909SAndreas Boehler        $event->add('DESCRIPTION', $description);
739cb71a62aSAndreas Boehler
7404ecb526cSAndreas Boehler      // Add attachments
7414ecb526cSAndreas Boehler      $attachments = $params['attachments'];
74282a48dfbSAndreas Boehler      if(!is_null($attachments))
7434ecb526cSAndreas Boehler        foreach($attachments as $attachment)
7444ecb526cSAndreas Boehler          $event->add('ATTACH', $attachment);
7454ecb526cSAndreas Boehler
746cb71a62aSAndreas Boehler      // Create a timestamp for last modified, created and dtstamp values in UTC
747b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
748b269830cSAndreas Boehler      $event->add('DTSTAMP', $dtStamp);
749b269830cSAndreas Boehler      $event->add('CREATED', $dtStamp);
750b269830cSAndreas Boehler      $event->add('LAST-MODIFIED', $dtStamp);
751cb71a62aSAndreas Boehler
752cb71a62aSAndreas Boehler      // Adjust the start date, based on the given timezone information
753b269830cSAndreas Boehler      $dtStart = new \DateTime();
754a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
755b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
756cb71a62aSAndreas Boehler
757cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
758b269830cSAndreas Boehler      if($params['allday'] != '1')
759b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
760cb71a62aSAndreas Boehler
761cb71a62aSAndreas Boehler      // Adjust the end date, based on the given timezone information
762b269830cSAndreas Boehler      $dtEnd = new \DateTime();
763a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
764b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
765cb71a62aSAndreas Boehler
766cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
767b269830cSAndreas Boehler      if($params['allday'] != '1')
768b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
769cb71a62aSAndreas Boehler
770b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
771b269830cSAndreas Boehler      if($params['allday'] == '1')
772b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
773cb71a62aSAndreas Boehler
774cb71a62aSAndreas Boehler      // Really add Start and End events
775b269830cSAndreas Boehler      $dtStartEv = $event->add('DTSTART', $dtStart);
776b269830cSAndreas Boehler      $dtEndEv = $event->add('DTEND', $dtEnd);
777cb71a62aSAndreas Boehler
778cb71a62aSAndreas Boehler      // Adjust the DATE format for allday events
779b269830cSAndreas Boehler      if($params['allday'] == '1')
780b269830cSAndreas Boehler      {
781b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
782b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
783b269830cSAndreas Boehler      }
784cb71a62aSAndreas Boehler
785809cb0faSAndreas Boehler      $eventStr = $vcalendar->serialize();
786809cb0faSAndreas Boehler
787809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
788809cb0faSAndreas Boehler      {
789809cb0faSAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
790809cb0faSAndreas Boehler          if(is_null($wdc))
791809cb0faSAndreas Boehler            return false;
792809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
793809cb0faSAndreas Boehler          return $wdc->addCalendarEntry($connectionId, $eventStr);
794809cb0faSAndreas Boehler      }
795809cb0faSAndreas Boehler      else
796809cb0faSAndreas Boehler      {
797cb71a62aSAndreas Boehler          // Actually add the values to the database
798a1a3b679SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
799a1a3b679SAndreas Boehler          $uri = uniqid('dokuwiki-').'.ics';
8001bb22c2bSAndreas Boehler          $now = new \DateTime();
801a1a3b679SAndreas Boehler
80251f4febbSAndreas Boehler          $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
80351f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT',
80451f4febbSAndreas Boehler                                      $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(),
80551f4febbSAndreas Boehler                                      strlen($eventStr), md5($eventStr), $uuid);
806cb71a62aSAndreas Boehler
807cb71a62aSAndreas Boehler          // If successfully, update the sync token database
80855a741c0SAndreas Boehler          if($res !== false)
80955a741c0SAndreas Boehler          {
81055a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'added');
811a1a3b679SAndreas Boehler              return true;
812a1a3b679SAndreas Boehler          }
813809cb0faSAndreas Boehler      }
81455a741c0SAndreas Boehler      return false;
81555a741c0SAndreas Boehler  }
816a1a3b679SAndreas Boehler
817cb71a62aSAndreas Boehler  /**
818cb71a62aSAndreas Boehler   * Retrieve the calendar settings of a given calendar id
819cb71a62aSAndreas Boehler   *
820cb71a62aSAndreas Boehler   * @param string $calid The calendar ID
821cb71a62aSAndreas Boehler   *
822cb71a62aSAndreas Boehler   * @return array The calendar settings array
823cb71a62aSAndreas Boehler   */
824b269830cSAndreas Boehler  public function getCalendarSettings($calid)
825b269830cSAndreas Boehler  {
826d5703f5aSAndreas Boehler      $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? ";
82751f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
828b269830cSAndreas Boehler      $row = $this->sqlite->res2row($res);
829b269830cSAndreas Boehler      return $row;
830b269830cSAndreas Boehler  }
831b269830cSAndreas Boehler
832cb71a62aSAndreas Boehler  /**
833cb71a62aSAndreas Boehler   * Retrieve all events that are within a given date range,
834cb71a62aSAndreas Boehler   * based on the timezone setting.
835cb71a62aSAndreas Boehler   *
836cb71a62aSAndreas Boehler   * There is also support for retrieving recurring events,
837cb71a62aSAndreas Boehler   * using Sabre's VObject Iterator. Recurring events are represented
838cb71a62aSAndreas Boehler   * as individual calendar entries with the same UID.
839cb71a62aSAndreas Boehler   *
840cb71a62aSAndreas Boehler   * @param string $id The page ID to work with
841cb71a62aSAndreas Boehler   * @param string $user The user ID to work with
842cb71a62aSAndreas Boehler   * @param string $startDate The start date as a string
843cb71a62aSAndreas Boehler   * @param string $endDate The end date as a string
8444a2bf5eeSAndreas Boehler   * @param string $color (optional) The calendar's color
845cb71a62aSAndreas Boehler   *
846cb71a62aSAndreas Boehler   * @return array An array containing the calendar entries.
847cb71a62aSAndreas Boehler   */
8484a2bf5eeSAndreas Boehler  public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null)
849a1a3b679SAndreas Boehler  {
85082a48dfbSAndreas Boehler      if($timezone !== '' && $timezone !== 'local')
85182a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($timezone);
852bd883736SAndreas Boehler      else
853bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
854a1a3b679SAndreas Boehler      $data = array();
855cb71a62aSAndreas Boehler
856a469597cSAndreas Boehler      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?";
857a469597cSAndreas Boehler      $startTs = null;
858a469597cSAndreas Boehler      $endTs = null;
859a469597cSAndreas Boehler      if($startDate !== null)
860a469597cSAndreas Boehler      {
861a1a3b679SAndreas Boehler        $startTs = new \DateTime($startDate);
862a469597cSAndreas Boehler        $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp());
863a469597cSAndreas Boehler      }
864a469597cSAndreas Boehler      if($endDate !== null)
865a469597cSAndreas Boehler      {
866a1a3b679SAndreas Boehler        $endTs = new \DateTime($endDate);
867a469597cSAndreas Boehler        $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp());
868a469597cSAndreas Boehler      }
869cb71a62aSAndreas Boehler
8700b805092SAndreas Boehler      // Load SabreDAV
8710b805092SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
8720b805092SAndreas Boehler
8730b805092SAndreas Boehler      if(strpos($id, 'webdav://') === 0)
8740b805092SAndreas Boehler      {
8750b805092SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
8760b805092SAndreas Boehler          if(is_null($wdc))
8770b805092SAndreas Boehler            return $data;
8780b805092SAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
8790b805092SAndreas Boehler          $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate);
8800b805092SAndreas Boehler      }
8810b805092SAndreas Boehler      else
8820b805092SAndreas Boehler      {
8830b805092SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
8840b805092SAndreas Boehler          if(is_null($color))
8850b805092SAndreas Boehler            $color = $this->getCalendarColorForCalendar($calid);
8860b805092SAndreas Boehler
887cb71a62aSAndreas Boehler          // Retrieve matching calendar objects
888a469597cSAndreas Boehler          $res = $this->sqlite->query($query, $calid);
889a1a3b679SAndreas Boehler          $arr = $this->sqlite->res2arr($res);
8900b805092SAndreas Boehler      }
891cb71a62aSAndreas Boehler
892cb71a62aSAndreas Boehler      // Parse individual calendar entries
893a1a3b679SAndreas Boehler      foreach($arr as $row)
894a1a3b679SAndreas Boehler      {
895a1a3b679SAndreas Boehler          if(isset($row['calendardata']))
896a1a3b679SAndreas Boehler          {
897b269830cSAndreas Boehler              $entry = array();
898a1a3b679SAndreas Boehler              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
899ebc4eb57SAndreas Boehler              $recurrence = $vcal->VEVENT->RRULE;
900cb71a62aSAndreas Boehler              // If it is a recurring event, pass it through Sabre's EventIterator
901ebc4eb57SAndreas Boehler              if($recurrence != null)
902ebc4eb57SAndreas Boehler              {
903ebc4eb57SAndreas Boehler                  $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
904ebc4eb57SAndreas Boehler                  $rEvents->rewind();
905e9b7d302SAndreas Boehler                  while($rEvents->valid())
906ebc4eb57SAndreas Boehler                  {
907ebc4eb57SAndreas Boehler                      $event = $rEvents->getEventObject();
908cb71a62aSAndreas Boehler                      // If we are after the given time range, exit
909a469597cSAndreas Boehler                      if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()))
910e9b7d302SAndreas Boehler                          break;
911cb71a62aSAndreas Boehler
912cb71a62aSAndreas Boehler                      // If we are before the given time range, continue
913a469597cSAndreas Boehler                      if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()))
914ebc4eb57SAndreas Boehler                      {
915ebc4eb57SAndreas Boehler                          $rEvents->next();
916ebc4eb57SAndreas Boehler                          continue;
917ebc4eb57SAndreas Boehler                      }
918cb71a62aSAndreas Boehler
919cb71a62aSAndreas Boehler                      // If we are within the given time range, parse the event
920185e2535SAndreas Boehler                      $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
921ebc4eb57SAndreas Boehler                      $rEvents->next();
922ebc4eb57SAndreas Boehler                  }
923ebc4eb57SAndreas Boehler              }
924ebc4eb57SAndreas Boehler              else
925185e2535SAndreas Boehler                $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
926ebc4eb57SAndreas Boehler          }
927ebc4eb57SAndreas Boehler      }
928ebc4eb57SAndreas Boehler      return $data;
929ebc4eb57SAndreas Boehler  }
930ebc4eb57SAndreas Boehler
931cb71a62aSAndreas Boehler  /**
932cb71a62aSAndreas Boehler   * Helper function that parses the iCal data of a VEVENT to a calendar entry.
933cb71a62aSAndreas Boehler   *
934cb71a62aSAndreas Boehler   * @param \Sabre\VObject\VEvent $event The event to parse
935cb71a62aSAndreas Boehler   * @param \DateTimeZone $timezone The timezone object
936cb71a62aSAndreas Boehler   * @param string $uid The entry's UID
9373c86dda8SAndreas Boehler   * @param boolean $recurring (optional) Set to true to define a recurring event
938cb71a62aSAndreas Boehler   *
939cb71a62aSAndreas Boehler   * @return array The parse calendar entry
940cb71a62aSAndreas Boehler   */
941185e2535SAndreas Boehler  private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
942ebc4eb57SAndreas Boehler  {
943ebc4eb57SAndreas Boehler      $entry = array();
944ebc4eb57SAndreas Boehler      $start = $event->DTSTART;
945cb71a62aSAndreas Boehler      // Parse only if the start date/time is present
946b269830cSAndreas Boehler      if($start !== null)
947b269830cSAndreas Boehler      {
948b269830cSAndreas Boehler        $dtStart = $start->getDateTime();
949b269830cSAndreas Boehler        $dtStart->setTimezone($timezone);
950bf0ad2b4SAndreas Boehler
951bf0ad2b4SAndreas Boehler        // moment.js doesn't like times be given even if
952bf0ad2b4SAndreas Boehler        // allDay is set to true
953bf0ad2b4SAndreas Boehler        // This should fix T23
954b269830cSAndreas Boehler        if($start['VALUE'] == 'DATE')
955bf0ad2b4SAndreas Boehler        {
956b269830cSAndreas Boehler          $entry['allDay'] = true;
957bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format("Y-m-d");
958bf0ad2b4SAndreas Boehler        }
959b269830cSAndreas Boehler        else
960bf0ad2b4SAndreas Boehler        {
961b269830cSAndreas Boehler          $entry['allDay'] = false;
962bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format(\DateTime::ATOM);
963bf0ad2b4SAndreas Boehler        }
964b269830cSAndreas Boehler      }
965ebc4eb57SAndreas Boehler      $end = $event->DTEND;
966bf0ad2b4SAndreas Boehler      // Parse only if the end date/time is present
967b269830cSAndreas Boehler      if($end !== null)
968b269830cSAndreas Boehler      {
969b269830cSAndreas Boehler        $dtEnd = $end->getDateTime();
970b269830cSAndreas Boehler        $dtEnd->setTimezone($timezone);
971bf0ad2b4SAndreas Boehler        if($end['VALUE'] == 'DATE')
972bf0ad2b4SAndreas Boehler          $entry['end'] = $dtEnd->format("Y-m-d");
973bf0ad2b4SAndreas Boehler        else
974b269830cSAndreas Boehler          $entry['end'] = $dtEnd->format(\DateTime::ATOM);
975b269830cSAndreas Boehler      }
976ebc4eb57SAndreas Boehler      $description = $event->DESCRIPTION;
9770eebc909SAndreas Boehler      if($description !== null)
9780eebc909SAndreas Boehler        $entry['description'] = (string)$description;
9790eebc909SAndreas Boehler      else
9800eebc909SAndreas Boehler        $entry['description'] = '';
9814ecb526cSAndreas Boehler      $attachments = $event->ATTACH;
9824ecb526cSAndreas Boehler      if($attachments !== null)
9834ecb526cSAndreas Boehler      {
9844ecb526cSAndreas Boehler        $entry['attachments'] = array();
9854ecb526cSAndreas Boehler        foreach($attachments as $attachment)
9864ecb526cSAndreas Boehler          $entry['attachments'][] = (string)$attachment;
9874ecb526cSAndreas Boehler      }
988ebc4eb57SAndreas Boehler      $entry['title'] = (string)$event->summary;
989ebc4eb57SAndreas Boehler      $entry['id'] = $uid;
990185e2535SAndreas Boehler      $entry['page'] = $page;
991185e2535SAndreas Boehler      $entry['color'] = $color;
9923c86dda8SAndreas Boehler      $entry['recurring'] = $recurring;
993185e2535SAndreas Boehler
994ebc4eb57SAndreas Boehler      return $entry;
995a1a3b679SAndreas Boehler  }
996a1a3b679SAndreas Boehler
997cb71a62aSAndreas Boehler  /**
998cb71a62aSAndreas Boehler   * Retrieve an event by its UID
999cb71a62aSAndreas Boehler   *
1000cb71a62aSAndreas Boehler   * @param string $uid The event's UID
1001cb71a62aSAndreas Boehler   *
1002cb71a62aSAndreas Boehler   * @return mixed The table row with the given event
1003cb71a62aSAndreas Boehler   */
1004a1a3b679SAndreas Boehler  public function getEventWithUid($uid)
1005a1a3b679SAndreas Boehler  {
100651f4febbSAndreas Boehler      $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?";
100751f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
1008a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
1009a1a3b679SAndreas Boehler      return $row;
1010a1a3b679SAndreas Boehler  }
1011a1a3b679SAndreas Boehler
1012cb71a62aSAndreas Boehler  /**
1013d5703f5aSAndreas Boehler   * Retrieve information of a calendar's object, not including the actual
1014d5703f5aSAndreas Boehler   * calendar data! This is mainly neede for the sync support.
1015d5703f5aSAndreas Boehler   *
1016d5703f5aSAndreas Boehler   * @param int $calid The calendar ID
1017d5703f5aSAndreas Boehler   *
1018d5703f5aSAndreas Boehler   * @return mixed The result
1019d5703f5aSAndreas Boehler   */
1020d5703f5aSAndreas Boehler  public function getCalendarObjects($calid)
1021d5703f5aSAndreas Boehler  {
1022d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?";
1023d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1024d5703f5aSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
1025d5703f5aSAndreas Boehler      return $arr;
1026d5703f5aSAndreas Boehler  }
1027d5703f5aSAndreas Boehler
1028d5703f5aSAndreas Boehler  /**
1029d5703f5aSAndreas Boehler   * Retrieve a single calendar object by calendar ID and URI
1030d5703f5aSAndreas Boehler   *
1031d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1032d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1033d5703f5aSAndreas Boehler   *
1034d5703f5aSAndreas Boehler   * @return mixed The result
1035d5703f5aSAndreas Boehler   */
1036d5703f5aSAndreas Boehler  public function getCalendarObjectByUri($calid, $uri)
1037d5703f5aSAndreas Boehler  {
1038d5703f5aSAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1039d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1040d5703f5aSAndreas Boehler      $row = $this->sqlite->res2row($res);
1041d5703f5aSAndreas Boehler      return $row;
1042d5703f5aSAndreas Boehler  }
1043d5703f5aSAndreas Boehler
1044d5703f5aSAndreas Boehler  /**
1045d5703f5aSAndreas Boehler   * Retrieve several calendar objects by specifying an array of URIs.
1046d5703f5aSAndreas Boehler   * This is mainly neede for sync.
1047d5703f5aSAndreas Boehler   *
1048d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1049d5703f5aSAndreas Boehler   * @param array $uris An array of URIs
1050d5703f5aSAndreas Boehler   *
1051d5703f5aSAndreas Boehler   * @return mixed The result
1052d5703f5aSAndreas Boehler   */
1053d5703f5aSAndreas Boehler  public function getMultipleCalendarObjectsByUri($calid, $uris)
1054d5703f5aSAndreas Boehler  {
1055d5703f5aSAndreas Boehler        $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN (";
1056d5703f5aSAndreas Boehler        // Inserting a whole bunch of question marks
1057d5703f5aSAndreas Boehler        $query .= implode(',', array_fill(0, count($uris), '?'));
1058d5703f5aSAndreas Boehler        $query .= ')';
1059d5703f5aSAndreas Boehler        $vals = array_merge(array($calid), $uris);
1060d5703f5aSAndreas Boehler
1061d5703f5aSAndreas Boehler        $res = $this->sqlite->query($query, $vals);
1062d5703f5aSAndreas Boehler        $arr = $this->sqlite->res2arr($res);
1063d5703f5aSAndreas Boehler        return $arr;
1064d5703f5aSAndreas Boehler  }
1065d5703f5aSAndreas Boehler
1066d5703f5aSAndreas Boehler  /**
1067cb71a62aSAndreas Boehler   * Retrieve all calendar events for a given calendar ID
1068cb71a62aSAndreas Boehler   *
1069cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1070cb71a62aSAndreas Boehler   *
1071cb71a62aSAndreas Boehler   * @return array An array containing all calendar data
1072cb71a62aSAndreas Boehler   */
1073f69bb449SAndreas Boehler  public function getAllCalendarEvents($calid)
1074f69bb449SAndreas Boehler  {
10757e0b8590SAndreas Boehler      $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?";
107651f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1077f69bb449SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
1078f69bb449SAndreas Boehler      return $arr;
1079f69bb449SAndreas Boehler  }
1080f69bb449SAndreas Boehler
1081cb71a62aSAndreas Boehler  /**
1082cb71a62aSAndreas Boehler   * Edit a calendar entry for a page, given by its parameters.
1083cb71a62aSAndreas Boehler   * The params array has the same format as @see addCalendarEntryForPage
1084cb71a62aSAndreas Boehler   *
1085cb71a62aSAndreas Boehler   * @param string $id The page's ID to work on
1086cb71a62aSAndreas Boehler   * @param string $user The user's ID to work on
1087cb71a62aSAndreas Boehler   * @param array $params The parameter array for the edited calendar event
1088cb71a62aSAndreas Boehler   *
1089cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1090cb71a62aSAndreas Boehler   */
1091a1a3b679SAndreas Boehler  public function editCalendarEntryForPage($id, $user, $params)
1092a1a3b679SAndreas Boehler  {
109382a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
109482a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
109582a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
1096a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
1097bd883736SAndreas Boehler      else
1098bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
1099cb71a62aSAndreas Boehler
1100cb71a62aSAndreas Boehler      // Parse dates
1101b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
1102b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
1103b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
1104b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
1105cb71a62aSAndreas Boehler
1106cb71a62aSAndreas Boehler      // Retrieve the existing event based on the UID
110755a741c0SAndreas Boehler      $uid = $params['uid'];
1108809cb0faSAndreas Boehler
1109809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1110809cb0faSAndreas Boehler      {
1111809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1112809cb0faSAndreas Boehler        if(is_null($wdc))
1113809cb0faSAndreas Boehler          return false;
1114809cb0faSAndreas Boehler        $event = $wdc->getCalendarEntryByUid($uid);
1115809cb0faSAndreas Boehler      }
1116809cb0faSAndreas Boehler      else
1117809cb0faSAndreas Boehler      {
111855a741c0SAndreas Boehler        $event = $this->getEventWithUid($uid);
1119809cb0faSAndreas Boehler      }
1120cb71a62aSAndreas Boehler
1121cb71a62aSAndreas Boehler      // Load SabreDAV
11229bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1123a1a3b679SAndreas Boehler      if(!isset($event['calendardata']))
1124a1a3b679SAndreas Boehler        return false;
112555a741c0SAndreas Boehler      $uri = $event['uri'];
112655a741c0SAndreas Boehler      $calid = $event['calendarid'];
1127cb71a62aSAndreas Boehler
1128cb71a62aSAndreas Boehler      // Parse the existing event
1129a1a3b679SAndreas Boehler      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
1130b269830cSAndreas Boehler      $vevent = $vcal->VEVENT;
1131cb71a62aSAndreas Boehler
1132cb71a62aSAndreas Boehler      // Set the new event values
1133b269830cSAndreas Boehler      $vevent->summary = $params['eventname'];
1134b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
11350eebc909SAndreas Boehler      $description = $params['eventdescription'];
1136cb71a62aSAndreas Boehler
1137cb71a62aSAndreas Boehler      // Remove existing timestamps to overwrite them
11380eebc909SAndreas Boehler      $vevent->remove('DESCRIPTION');
1139b269830cSAndreas Boehler      $vevent->remove('DTSTAMP');
1140b269830cSAndreas Boehler      $vevent->remove('LAST-MODIFIED');
11414ecb526cSAndreas Boehler      $vevent->remove('ATTACH');
1142cb71a62aSAndreas Boehler
1143cb71a62aSAndreas Boehler      // Add new time stamps and description
1144b269830cSAndreas Boehler      $vevent->add('DTSTAMP', $dtStamp);
1145b269830cSAndreas Boehler      $vevent->add('LAST-MODIFIED', $dtStamp);
11460eebc909SAndreas Boehler      if($description !== '')
11470eebc909SAndreas Boehler        $vevent->add('DESCRIPTION', $description);
1148cb71a62aSAndreas Boehler
11494ecb526cSAndreas Boehler      // Add attachments
11504ecb526cSAndreas Boehler      $attachments = $params['attachments'];
115182a48dfbSAndreas Boehler      if(!is_null($attachments))
11524ecb526cSAndreas Boehler        foreach($attachments as $attachment)
11534ecb526cSAndreas Boehler          $vevent->add('ATTACH', $attachment);
11544ecb526cSAndreas Boehler
1155cb71a62aSAndreas Boehler      // Setup DTSTART
1156b269830cSAndreas Boehler      $dtStart = new \DateTime();
1157a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
1158b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
1159b269830cSAndreas Boehler      if($params['allday'] != '1')
1160b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
1161cb71a62aSAndreas Boehler
11624ecb526cSAndreas Boehler      // Setup DTEND
1163b269830cSAndreas Boehler      $dtEnd = new \DateTime();
1164a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
1165b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
1166b269830cSAndreas Boehler      if($params['allday'] != '1')
1167b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
1168cb71a62aSAndreas Boehler
1169b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
1170b269830cSAndreas Boehler      if($params['allday'] == '1')
1171b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
1172b269830cSAndreas Boehler      $vevent->remove('DTSTART');
1173b269830cSAndreas Boehler      $vevent->remove('DTEND');
1174b269830cSAndreas Boehler      $dtStartEv = $vevent->add('DTSTART', $dtStart);
1175b269830cSAndreas Boehler      $dtEndEv = $vevent->add('DTEND', $dtEnd);
1176cb71a62aSAndreas Boehler
1177cb71a62aSAndreas Boehler      // Remove the time for allday events
1178b269830cSAndreas Boehler      if($params['allday'] == '1')
1179b269830cSAndreas Boehler      {
1180b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
1181b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
1182b269830cSAndreas Boehler      }
1183a1a3b679SAndreas Boehler      $eventStr = $vcal->serialize();
1184809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1185809cb0faSAndreas Boehler      {
1186809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
1187809cb0faSAndreas Boehler          return $wdc->editCalendarEntry($connectionId, $uid, $eventStr);
1188809cb0faSAndreas Boehler      }
1189809cb0faSAndreas Boehler      else
1190809cb0faSAndreas Boehler      {
1191809cb0faSAndreas Boehler          $now = new DateTime();
1192cb71a62aSAndreas Boehler          // Actually write to the database
119351f4febbSAndreas Boehler          $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ".
119451f4febbSAndreas Boehler                   "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?";
119551f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(),
119651f4febbSAndreas Boehler                                      $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid);
119755a741c0SAndreas Boehler          if($res !== false)
119855a741c0SAndreas Boehler          {
119955a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'modified');
1200a1a3b679SAndreas Boehler              return true;
1201a1a3b679SAndreas Boehler          }
1202809cb0faSAndreas Boehler      }
120355a741c0SAndreas Boehler      return false;
120455a741c0SAndreas Boehler  }
1205a1a3b679SAndreas Boehler
1206cb71a62aSAndreas Boehler  /**
1207d5703f5aSAndreas Boehler   * Delete an event from a calendar by calendar ID and URI
1208d5703f5aSAndreas Boehler   *
1209d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1210d5703f5aSAndreas Boehler   * @param string $uri The object's URI
1211d5703f5aSAndreas Boehler   *
1212d5703f5aSAndreas Boehler   * @return true
1213d5703f5aSAndreas Boehler   */
1214d5703f5aSAndreas Boehler  public function deleteCalendarEntryForCalendarByUri($calid, $uri)
1215d5703f5aSAndreas Boehler  {
1216d5703f5aSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1217d5703f5aSAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1218d5703f5aSAndreas Boehler      if($res !== false)
1219d5703f5aSAndreas Boehler      {
1220d5703f5aSAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
1221d5703f5aSAndreas Boehler      }
1222d5703f5aSAndreas Boehler      return true;
1223d5703f5aSAndreas Boehler  }
1224d5703f5aSAndreas Boehler
1225d5703f5aSAndreas Boehler  /**
1226cb71a62aSAndreas Boehler   * Delete a calendar entry for a given page. Actually, the event is removed
1227cb71a62aSAndreas Boehler   * based on the entry's UID, so that page ID is no used.
1228cb71a62aSAndreas Boehler   *
1229cb71a62aSAndreas Boehler   * @param string $id The page's ID (unused)
1230cb71a62aSAndreas Boehler   * @param array $params The parameter array to work with
1231cb71a62aSAndreas Boehler   *
1232cb71a62aSAndreas Boehler   * @return boolean True
1233cb71a62aSAndreas Boehler   */
1234a1a3b679SAndreas Boehler  public function deleteCalendarEntryForPage($id, $params)
1235a1a3b679SAndreas Boehler  {
1236a1a3b679SAndreas Boehler      $uid = $params['uid'];
1237809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1238809cb0faSAndreas Boehler      {
1239809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1240809cb0faSAndreas Boehler        if(is_null($wdc))
1241809cb0faSAndreas Boehler          return false;
1242809cb0faSAndreas Boehler        $connectionId = str_replace('webdav://', '', $id);
1243809cb0faSAndreas Boehler        $result = $wdc->deleteCalendarEntry($connectionId, $uid);
1244809cb0faSAndreas Boehler        return $result;
1245809cb0faSAndreas Boehler      }
124655a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
12472c14b82bSAndreas Boehler      $calid = $event['calendarid'];
124855a741c0SAndreas Boehler      $uri = $event['uri'];
124951f4febbSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE uid = ?";
125051f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
125155a741c0SAndreas Boehler      if($res !== false)
125255a741c0SAndreas Boehler      {
125355a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
125455a741c0SAndreas Boehler      }
1255a1a3b679SAndreas Boehler      return true;
1256a1a3b679SAndreas Boehler  }
1257a1a3b679SAndreas Boehler
1258cb71a62aSAndreas Boehler  /**
1259cb71a62aSAndreas Boehler   * Retrieve the current sync token for a calendar
1260cb71a62aSAndreas Boehler   *
1261cb71a62aSAndreas Boehler   * @param string $calid The calendar id
1262cb71a62aSAndreas Boehler   *
1263cb71a62aSAndreas Boehler   * @return mixed The synctoken or false
1264cb71a62aSAndreas Boehler   */
126555a741c0SAndreas Boehler  public function getSyncTokenForCalendar($calid)
126655a741c0SAndreas Boehler  {
1267b269830cSAndreas Boehler      $row = $this->getCalendarSettings($calid);
126855a741c0SAndreas Boehler      if(isset($row['synctoken']))
126955a741c0SAndreas Boehler          return $row['synctoken'];
127055a741c0SAndreas Boehler      return false;
127155a741c0SAndreas Boehler  }
127255a741c0SAndreas Boehler
1273cb71a62aSAndreas Boehler  /**
1274cb71a62aSAndreas Boehler   * Helper function to convert the operation name to
1275cb71a62aSAndreas Boehler   * an operation code as stored in the database
1276cb71a62aSAndreas Boehler   *
1277cb71a62aSAndreas Boehler   * @param string $operationName The operation name
1278cb71a62aSAndreas Boehler   *
1279cb71a62aSAndreas Boehler   * @return mixed The operation code or false
1280cb71a62aSAndreas Boehler   */
128155a741c0SAndreas Boehler  public function operationNameToOperation($operationName)
128255a741c0SAndreas Boehler  {
128355a741c0SAndreas Boehler      switch($operationName)
128455a741c0SAndreas Boehler      {
128555a741c0SAndreas Boehler          case 'added':
128655a741c0SAndreas Boehler              return 1;
128755a741c0SAndreas Boehler          break;
128855a741c0SAndreas Boehler          case 'modified':
128955a741c0SAndreas Boehler              return 2;
129055a741c0SAndreas Boehler          break;
129155a741c0SAndreas Boehler          case 'deleted':
129255a741c0SAndreas Boehler              return 3;
129355a741c0SAndreas Boehler          break;
129455a741c0SAndreas Boehler      }
129555a741c0SAndreas Boehler      return false;
129655a741c0SAndreas Boehler  }
129755a741c0SAndreas Boehler
1298cb71a62aSAndreas Boehler  /**
1299cb71a62aSAndreas Boehler   * Update the sync token log based on the calendar id and the
1300cb71a62aSAndreas Boehler   * operation that was performed.
1301cb71a62aSAndreas Boehler   *
1302cb71a62aSAndreas Boehler   * @param string $calid The calendar ID that was modified
1303cb71a62aSAndreas Boehler   * @param string $uri The calendar URI that was modified
1304cb71a62aSAndreas Boehler   * @param string $operation The operation that was performed
1305cb71a62aSAndreas Boehler   *
1306cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1307cb71a62aSAndreas Boehler   */
130855a741c0SAndreas Boehler  private function updateSyncTokenLog($calid, $uri, $operation)
130955a741c0SAndreas Boehler  {
131055a741c0SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
131155a741c0SAndreas Boehler      $operationCode = $this->operationNameToOperation($operation);
131255a741c0SAndreas Boehler      if(($operationCode === false) || ($currentToken === false))
131355a741c0SAndreas Boehler          return false;
131455a741c0SAndreas Boehler      $values = array($uri,
131555a741c0SAndreas Boehler                      $currentToken,
131655a741c0SAndreas Boehler                      $calid,
131755a741c0SAndreas Boehler                      $operationCode
131855a741c0SAndreas Boehler      );
131951f4febbSAndreas Boehler      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)";
132051f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode);
132155a741c0SAndreas Boehler      if($res === false)
132255a741c0SAndreas Boehler        return false;
132355a741c0SAndreas Boehler      $currentToken++;
132451f4febbSAndreas Boehler      $query = "UPDATE calendars SET synctoken = ? WHERE id = ?";
132551f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $currentToken, $calid);
132655a741c0SAndreas Boehler      return ($res !== false);
132755a741c0SAndreas Boehler  }
132855a741c0SAndreas Boehler
1329cb71a62aSAndreas Boehler  /**
1330cb71a62aSAndreas Boehler   * Return the sync URL for a given Page, i.e. a calendar
1331cb71a62aSAndreas Boehler   *
1332cb71a62aSAndreas Boehler   * @param string $id The page's ID
1333cb71a62aSAndreas Boehler   * @param string $user (optional) The user's ID
1334cb71a62aSAndreas Boehler   *
1335cb71a62aSAndreas Boehler   * @return mixed The sync url or false
1336cb71a62aSAndreas Boehler   */
1337b269830cSAndreas Boehler  public function getSyncUrlForPage($id, $user = null)
1338b269830cSAndreas Boehler  {
133934a47953SAndreas Boehler      if(is_null($userid))
134034a47953SAndreas Boehler      {
134134a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
134234a47953SAndreas Boehler        {
134334a47953SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
134434a47953SAndreas Boehler        }
134534a47953SAndreas Boehler        else
134634a47953SAndreas Boehler        {
134734a47953SAndreas Boehler          return false;
134834a47953SAndreas Boehler        }
134934a47953SAndreas Boehler      }
1350b269830cSAndreas Boehler
1351b269830cSAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1352b269830cSAndreas Boehler      if($calid === false)
1353b269830cSAndreas Boehler        return false;
1354b269830cSAndreas Boehler
1355b269830cSAndreas Boehler      $calsettings = $this->getCalendarSettings($calid);
1356b269830cSAndreas Boehler      if(!isset($calsettings['uri']))
1357b269830cSAndreas Boehler        return false;
1358b269830cSAndreas Boehler
1359b269830cSAndreas Boehler      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
1360b269830cSAndreas Boehler      return $syncurl;
1361b269830cSAndreas Boehler  }
1362b269830cSAndreas Boehler
1363cb71a62aSAndreas Boehler  /**
1364cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given page
1365cb71a62aSAndreas Boehler   *
1366cb71a62aSAndreas Boehler   * @param string $id the page ID
1367cb71a62aSAndreas Boehler   *
1368cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1369cb71a62aSAndreas Boehler   */
1370f69bb449SAndreas Boehler  public function getPrivateURLForPage($id)
1371f69bb449SAndreas Boehler  {
1372f69bb449SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1373f69bb449SAndreas Boehler      if($calid === false)
1374f69bb449SAndreas Boehler        return false;
1375f69bb449SAndreas Boehler
1376f69bb449SAndreas Boehler      return $this->getPrivateURLForCalendar($calid);
1377f69bb449SAndreas Boehler  }
1378f69bb449SAndreas Boehler
1379cb71a62aSAndreas Boehler  /**
1380cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given calendar ID
1381cb71a62aSAndreas Boehler   *
1382cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1383cb71a62aSAndreas Boehler   *
1384cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1385cb71a62aSAndreas Boehler   */
1386f69bb449SAndreas Boehler  public function getPrivateURLForCalendar($calid)
1387f69bb449SAndreas Boehler  {
1388185e2535SAndreas Boehler      if(isset($this->cachedValues['privateurl'][$calid]))
1389185e2535SAndreas Boehler        return $this->cachedValues['privateurl'][$calid];
139051f4febbSAndreas Boehler      $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?";
139151f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1392f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1393f69bb449SAndreas Boehler      if(!isset($row['url']))
1394f69bb449SAndreas Boehler      {
1395f69bb449SAndreas Boehler          $url = uniqid("dokuwiki-").".ics";
139651f4febbSAndreas Boehler          $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)";
139751f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $url, $calid);
1398f69bb449SAndreas Boehler          if($res === false)
1399f69bb449SAndreas Boehler            return false;
1400f69bb449SAndreas Boehler      }
1401f69bb449SAndreas Boehler      else
1402f69bb449SAndreas Boehler      {
1403f69bb449SAndreas Boehler          $url = $row['url'];
1404f69bb449SAndreas Boehler      }
1405185e2535SAndreas Boehler
1406185e2535SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url;
1407185e2535SAndreas Boehler      $this->cachedValues['privateurl'][$calid] = $url;
1408185e2535SAndreas Boehler      return $url;
1409f69bb449SAndreas Boehler  }
1410f69bb449SAndreas Boehler
1411cb71a62aSAndreas Boehler  /**
1412cb71a62aSAndreas Boehler   * Retrieve the calendar ID for a given private calendar URL
1413cb71a62aSAndreas Boehler   *
1414cb71a62aSAndreas Boehler   * @param string $url The private URL
1415cb71a62aSAndreas Boehler   *
1416cb71a62aSAndreas Boehler   * @return mixed The calendar ID or false
1417cb71a62aSAndreas Boehler   */
1418f69bb449SAndreas Boehler  public function getCalendarForPrivateURL($url)
1419f69bb449SAndreas Boehler  {
142051f4febbSAndreas Boehler      $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?";
142151f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $url);
1422f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1423f69bb449SAndreas Boehler      if(!isset($row['calid']))
1424f69bb449SAndreas Boehler        return false;
1425f69bb449SAndreas Boehler      return $row['calid'];
1426f69bb449SAndreas Boehler  }
1427f69bb449SAndreas Boehler
1428cb71a62aSAndreas Boehler  /**
1429cb71a62aSAndreas Boehler   * Return a given calendar as ICS feed, i.e. all events in one ICS file.
1430cb71a62aSAndreas Boehler   *
14317e0b8590SAndreas Boehler   * @param string $calid The calendar ID to retrieve
1432cb71a62aSAndreas Boehler   *
1433cb71a62aSAndreas Boehler   * @return mixed The calendar events as string or false
1434cb71a62aSAndreas Boehler   */
1435f69bb449SAndreas Boehler  public function getCalendarAsICSFeed($calid)
1436f69bb449SAndreas Boehler  {
1437f69bb449SAndreas Boehler      $calSettings = $this->getCalendarSettings($calid);
1438f69bb449SAndreas Boehler      if($calSettings === false)
1439f69bb449SAndreas Boehler        return false;
1440f69bb449SAndreas Boehler      $events = $this->getAllCalendarEvents($calid);
1441f69bb449SAndreas Boehler      if($events === false)
1442f69bb449SAndreas Boehler        return false;
1443f69bb449SAndreas Boehler
14447e0b8590SAndreas Boehler      // Load SabreDAV
14457e0b8590SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
14467e0b8590SAndreas Boehler      $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:";
14477e0b8590SAndreas Boehler      $out .= $calSettings['displayname']."\r\n";
1448f69bb449SAndreas Boehler      foreach($events as $event)
1449f69bb449SAndreas Boehler      {
14507e0b8590SAndreas Boehler          $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
14517e0b8590SAndreas Boehler          $evt = $vcal->VEVENT;
14527e0b8590SAndreas Boehler          $out .= $evt->serialize();
1453f69bb449SAndreas Boehler      }
14547e0b8590SAndreas Boehler      $out .= "END:VCALENDAR\r\n";
1455f69bb449SAndreas Boehler      return $out;
1456f69bb449SAndreas Boehler  }
1457f69bb449SAndreas Boehler
14587c7c6b0bSAndreas Boehler  /**
14597c7c6b0bSAndreas Boehler   * Retrieve a configuration option for the plugin
14607c7c6b0bSAndreas Boehler   *
14617c7c6b0bSAndreas Boehler   * @param string $key The key to query
146221d04f73SAndreas Boehler   * @return mixed The option set, null if not found
14637c7c6b0bSAndreas Boehler   */
14647c7c6b0bSAndreas Boehler  public function getConfig($key)
14657c7c6b0bSAndreas Boehler  {
14667c7c6b0bSAndreas Boehler      return $this->getConf($key);
14677c7c6b0bSAndreas Boehler  }
14687c7c6b0bSAndreas Boehler
1469d5703f5aSAndreas Boehler  /**
1470d5703f5aSAndreas Boehler   * Parses some information from calendar objects, used for optimized
1471d5703f5aSAndreas Boehler   * calendar-queries. Taken nearly unmodified from Sabre's PDO backend
1472d5703f5aSAndreas Boehler   *
1473d5703f5aSAndreas Boehler   * Returns an array with the following keys:
1474d5703f5aSAndreas Boehler   *   * etag - An md5 checksum of the object without the quotes.
1475d5703f5aSAndreas Boehler   *   * size - Size of the object in bytes
1476d5703f5aSAndreas Boehler   *   * componentType - VEVENT, VTODO or VJOURNAL
1477d5703f5aSAndreas Boehler   *   * firstOccurence
1478d5703f5aSAndreas Boehler   *   * lastOccurence
1479d5703f5aSAndreas Boehler   *   * uid - value of the UID property
1480d5703f5aSAndreas Boehler   *
1481d5703f5aSAndreas Boehler   * @param string $calendarData
1482d5703f5aSAndreas Boehler   * @return array
1483d5703f5aSAndreas Boehler   */
1484d5703f5aSAndreas Boehler  protected function getDenormalizedData($calendarData)
1485d5703f5aSAndreas Boehler  {
1486d5703f5aSAndreas Boehler    require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1487d5703f5aSAndreas Boehler
1488d5703f5aSAndreas Boehler    $vObject = \Sabre\VObject\Reader::read($calendarData);
1489d5703f5aSAndreas Boehler    $componentType = null;
1490d5703f5aSAndreas Boehler    $component = null;
1491d5703f5aSAndreas Boehler    $firstOccurence = null;
1492d5703f5aSAndreas Boehler    $lastOccurence = null;
1493d5703f5aSAndreas Boehler    $uid = null;
1494d5703f5aSAndreas Boehler    foreach ($vObject->getComponents() as $component)
1495d5703f5aSAndreas Boehler    {
1496d5703f5aSAndreas Boehler        if ($component->name !== 'VTIMEZONE')
1497d5703f5aSAndreas Boehler        {
1498d5703f5aSAndreas Boehler            $componentType = $component->name;
1499d5703f5aSAndreas Boehler            $uid = (string)$component->UID;
1500d5703f5aSAndreas Boehler            break;
1501d5703f5aSAndreas Boehler        }
1502d5703f5aSAndreas Boehler    }
1503d5703f5aSAndreas Boehler    if (!$componentType)
1504d5703f5aSAndreas Boehler    {
1505d5703f5aSAndreas Boehler        return false;
1506d5703f5aSAndreas Boehler    }
1507d5703f5aSAndreas Boehler    if ($componentType === 'VEVENT')
1508d5703f5aSAndreas Boehler    {
1509d5703f5aSAndreas Boehler        $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
1510d5703f5aSAndreas Boehler        // Finding the last occurence is a bit harder
1511d5703f5aSAndreas Boehler        if (!isset($component->RRULE))
1512d5703f5aSAndreas Boehler        {
1513d5703f5aSAndreas Boehler            if (isset($component->DTEND))
1514d5703f5aSAndreas Boehler            {
1515d5703f5aSAndreas Boehler                $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
1516d5703f5aSAndreas Boehler            }
1517d5703f5aSAndreas Boehler            elseif (isset($component->DURATION))
1518d5703f5aSAndreas Boehler            {
1519d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1520d5703f5aSAndreas Boehler                $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
1521d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1522d5703f5aSAndreas Boehler            }
1523d5703f5aSAndreas Boehler            elseif (!$component->DTSTART->hasTime())
1524d5703f5aSAndreas Boehler            {
1525d5703f5aSAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1526d5703f5aSAndreas Boehler                $endDate->modify('+1 day');
1527d5703f5aSAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1528d5703f5aSAndreas Boehler            }
1529d5703f5aSAndreas Boehler            else
1530d5703f5aSAndreas Boehler            {
1531d5703f5aSAndreas Boehler                $lastOccurence = $firstOccurence;
1532d5703f5aSAndreas Boehler            }
1533d5703f5aSAndreas Boehler        }
1534d5703f5aSAndreas Boehler        else
1535d5703f5aSAndreas Boehler        {
1536d5703f5aSAndreas Boehler            $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
1537d5703f5aSAndreas Boehler            $maxDate = new \DateTime('2038-01-01');
1538d5703f5aSAndreas Boehler            if ($it->isInfinite())
1539d5703f5aSAndreas Boehler            {
1540d5703f5aSAndreas Boehler                $lastOccurence = $maxDate->getTimeStamp();
1541d5703f5aSAndreas Boehler            }
1542d5703f5aSAndreas Boehler            else
1543d5703f5aSAndreas Boehler            {
1544d5703f5aSAndreas Boehler                $end = $it->getDtEnd();
1545d5703f5aSAndreas Boehler                while ($it->valid() && $end < $maxDate)
1546d5703f5aSAndreas Boehler                {
1547d5703f5aSAndreas Boehler                    $end = $it->getDtEnd();
1548d5703f5aSAndreas Boehler                    $it->next();
1549d5703f5aSAndreas Boehler                }
1550d5703f5aSAndreas Boehler                $lastOccurence = $end->getTimeStamp();
1551d5703f5aSAndreas Boehler            }
1552d5703f5aSAndreas Boehler        }
1553d5703f5aSAndreas Boehler    }
1554d5703f5aSAndreas Boehler
1555d5703f5aSAndreas Boehler    return array(
1556d5703f5aSAndreas Boehler        'etag'           => md5($calendarData),
1557d5703f5aSAndreas Boehler        'size'           => strlen($calendarData),
1558d5703f5aSAndreas Boehler        'componentType'  => $componentType,
1559d5703f5aSAndreas Boehler        'firstOccurence' => $firstOccurence,
1560d5703f5aSAndreas Boehler        'lastOccurence'  => $lastOccurence,
1561d5703f5aSAndreas Boehler        'uid'            => $uid,
1562d5703f5aSAndreas Boehler    );
1563d5703f5aSAndreas Boehler
1564d5703f5aSAndreas Boehler  }
1565d5703f5aSAndreas Boehler
1566d5703f5aSAndreas Boehler  /**
1567d5703f5aSAndreas Boehler   * Query a calendar by ID and taking several filters into account.
1568d5703f5aSAndreas Boehler   * This is heavily based on Sabre's PDO backend.
1569d5703f5aSAndreas Boehler   *
1570d5703f5aSAndreas Boehler   * @param int $calendarId The calendar's ID
1571d5703f5aSAndreas Boehler   * @param array $filters The filter array to apply
1572d5703f5aSAndreas Boehler   *
1573d5703f5aSAndreas Boehler   * @return mixed The result
1574d5703f5aSAndreas Boehler   */
1575d5703f5aSAndreas Boehler  public function calendarQuery($calendarId, $filters)
1576d5703f5aSAndreas Boehler  {
1577d5703f5aSAndreas Boehler    $componentType = null;
1578d5703f5aSAndreas Boehler    $requirePostFilter = true;
1579d5703f5aSAndreas Boehler    $timeRange = null;
1580d5703f5aSAndreas Boehler
1581d5703f5aSAndreas Boehler    // if no filters were specified, we don't need to filter after a query
1582d5703f5aSAndreas Boehler    if (!$filters['prop-filters'] && !$filters['comp-filters'])
1583d5703f5aSAndreas Boehler    {
1584d5703f5aSAndreas Boehler        $requirePostFilter = false;
1585d5703f5aSAndreas Boehler    }
1586d5703f5aSAndreas Boehler
1587d5703f5aSAndreas Boehler    // Figuring out if there's a component filter
1588d5703f5aSAndreas Boehler    if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined'])
1589d5703f5aSAndreas Boehler    {
1590d5703f5aSAndreas Boehler        $componentType = $filters['comp-filters'][0]['name'];
1591d5703f5aSAndreas Boehler
1592d5703f5aSAndreas Boehler        // Checking if we need post-filters
1593d5703f5aSAndreas Boehler        if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters'])
1594d5703f5aSAndreas Boehler        {
1595d5703f5aSAndreas Boehler            $requirePostFilter = false;
1596d5703f5aSAndreas Boehler        }
1597d5703f5aSAndreas Boehler        // There was a time-range filter
1598d5703f5aSAndreas Boehler        if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range']))
1599d5703f5aSAndreas Boehler        {
1600d5703f5aSAndreas Boehler            $timeRange = $filters['comp-filters'][0]['time-range'];
1601d5703f5aSAndreas Boehler
1602d5703f5aSAndreas Boehler            // If start time OR the end time is not specified, we can do a
1603d5703f5aSAndreas Boehler            // 100% accurate mysql query.
1604d5703f5aSAndreas Boehler            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end']))
1605d5703f5aSAndreas Boehler            {
1606d5703f5aSAndreas Boehler                $requirePostFilter = false;
1607d5703f5aSAndreas Boehler            }
1608d5703f5aSAndreas Boehler        }
1609d5703f5aSAndreas Boehler
1610d5703f5aSAndreas Boehler    }
1611d5703f5aSAndreas Boehler
1612d5703f5aSAndreas Boehler    if ($requirePostFilter)
1613d5703f5aSAndreas Boehler    {
1614d5703f5aSAndreas Boehler        $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?";
1615d5703f5aSAndreas Boehler    }
1616d5703f5aSAndreas Boehler    else
1617d5703f5aSAndreas Boehler    {
1618d5703f5aSAndreas Boehler        $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1619d5703f5aSAndreas Boehler    }
1620d5703f5aSAndreas Boehler
1621d5703f5aSAndreas Boehler    $values = array(
1622d5703f5aSAndreas Boehler        $calendarId
1623d5703f5aSAndreas Boehler    );
1624d5703f5aSAndreas Boehler
1625d5703f5aSAndreas Boehler    if ($componentType)
1626d5703f5aSAndreas Boehler    {
1627d5703f5aSAndreas Boehler        $query .= " AND componenttype = ?";
1628d5703f5aSAndreas Boehler        $values[] = $componentType;
1629d5703f5aSAndreas Boehler    }
1630d5703f5aSAndreas Boehler
1631d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['start'])
1632d5703f5aSAndreas Boehler    {
1633d5703f5aSAndreas Boehler        $query .= " AND lastoccurence > ?";
1634d5703f5aSAndreas Boehler        $values[] = $timeRange['start']->getTimeStamp();
1635d5703f5aSAndreas Boehler    }
1636d5703f5aSAndreas Boehler    if ($timeRange && $timeRange['end'])
1637d5703f5aSAndreas Boehler    {
1638d5703f5aSAndreas Boehler        $query .= " AND firstoccurence < ?";
1639d5703f5aSAndreas Boehler        $values[] = $timeRange['end']->getTimeStamp();
1640d5703f5aSAndreas Boehler    }
1641d5703f5aSAndreas Boehler
1642d5703f5aSAndreas Boehler    $res = $this->sqlite->query($query, $values);
1643d5703f5aSAndreas Boehler    $arr = $this->sqlite->res2arr($res);
1644d5703f5aSAndreas Boehler
1645d5703f5aSAndreas Boehler    $result = array();
1646d5703f5aSAndreas Boehler    foreach($arr as $row)
1647d5703f5aSAndreas Boehler    {
1648d5703f5aSAndreas Boehler        if ($requirePostFilter)
1649d5703f5aSAndreas Boehler        {
1650d5703f5aSAndreas Boehler            if (!$this->validateFilterForObject($row, $filters))
1651d5703f5aSAndreas Boehler            {
1652d5703f5aSAndreas Boehler                continue;
1653d5703f5aSAndreas Boehler            }
1654d5703f5aSAndreas Boehler        }
1655d5703f5aSAndreas Boehler        $result[] = $row['uri'];
1656d5703f5aSAndreas Boehler
1657d5703f5aSAndreas Boehler    }
1658d5703f5aSAndreas Boehler
1659d5703f5aSAndreas Boehler    return $result;
1660d5703f5aSAndreas Boehler  }
1661d5703f5aSAndreas Boehler
1662d5703f5aSAndreas Boehler  /**
1663d5703f5aSAndreas Boehler   * This method validates if a filter (as passed to calendarQuery) matches
1664d5703f5aSAndreas Boehler   * the given object. Taken from Sabre's PDO backend
1665d5703f5aSAndreas Boehler   *
1666d5703f5aSAndreas Boehler   * @param array $object
1667d5703f5aSAndreas Boehler   * @param array $filters
1668d5703f5aSAndreas Boehler   * @return bool
1669d5703f5aSAndreas Boehler   */
1670d5703f5aSAndreas Boehler  protected function validateFilterForObject($object, $filters)
1671d5703f5aSAndreas Boehler  {
1672d5703f5aSAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1673d5703f5aSAndreas Boehler      // Unfortunately, setting the 'calendardata' here is optional. If
1674d5703f5aSAndreas Boehler      // it was excluded, we actually need another call to get this as
1675d5703f5aSAndreas Boehler      // well.
1676d5703f5aSAndreas Boehler      if (!isset($object['calendardata']))
1677d5703f5aSAndreas Boehler      {
1678d5703f5aSAndreas Boehler          $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']);
1679d5703f5aSAndreas Boehler      }
1680d5703f5aSAndreas Boehler
1681d5703f5aSAndreas Boehler      $vObject = \Sabre\VObject\Reader::read($object['calendardata']);
1682d5703f5aSAndreas Boehler      $validator = new \Sabre\CalDAV\CalendarQueryValidator();
1683d5703f5aSAndreas Boehler
1684d5703f5aSAndreas Boehler      return $validator->validate($vObject, $filters);
1685d5703f5aSAndreas Boehler
1686d5703f5aSAndreas Boehler  }
1687d5703f5aSAndreas Boehler
1688d5703f5aSAndreas Boehler  /**
1689d5703f5aSAndreas Boehler   * Retrieve changes for a given calendar based on the given syncToken.
1690d5703f5aSAndreas Boehler   *
1691d5703f5aSAndreas Boehler   * @param int $calid The calendar's ID
1692d5703f5aSAndreas Boehler   * @param int $syncToken The supplied sync token
1693d5703f5aSAndreas Boehler   * @param int $syncLevel The sync level
1694d5703f5aSAndreas Boehler   * @param int $limit The limit of changes
1695d5703f5aSAndreas Boehler   *
1696d5703f5aSAndreas Boehler   * @return array The result
1697d5703f5aSAndreas Boehler   */
1698d5703f5aSAndreas Boehler  public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null)
1699d5703f5aSAndreas Boehler  {
1700d5703f5aSAndreas Boehler      // Current synctoken
1701d5703f5aSAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
1702d5703f5aSAndreas Boehler
1703d5703f5aSAndreas Boehler      if ($currentToken === false) return null;
1704d5703f5aSAndreas Boehler
1705d5703f5aSAndreas Boehler      $result = array(
1706d5703f5aSAndreas Boehler          'syncToken' => $currentToken,
1707d5703f5aSAndreas Boehler          'added'     => array(),
1708d5703f5aSAndreas Boehler          'modified'  => array(),
1709d5703f5aSAndreas Boehler          'deleted'   => array(),
1710d5703f5aSAndreas Boehler      );
1711d5703f5aSAndreas Boehler
1712d5703f5aSAndreas Boehler      if ($syncToken)
1713d5703f5aSAndreas Boehler      {
1714d5703f5aSAndreas Boehler
1715d5703f5aSAndreas Boehler          $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
1716d5703f5aSAndreas Boehler          if ($limit > 0) $query .= " LIMIT " . (int)$limit;
1717d5703f5aSAndreas Boehler
1718d5703f5aSAndreas Boehler          // Fetching all changes
1719d5703f5aSAndreas Boehler          $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid);
1720d5703f5aSAndreas Boehler          if($res === false)
1721d5703f5aSAndreas Boehler              return null;
1722d5703f5aSAndreas Boehler
1723d5703f5aSAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1724d5703f5aSAndreas Boehler          $changes = array();
1725d5703f5aSAndreas Boehler
1726d5703f5aSAndreas Boehler          // This loop ensures that any duplicates are overwritten, only the
1727d5703f5aSAndreas Boehler          // last change on a node is relevant.
1728d5703f5aSAndreas Boehler          foreach($arr as $row)
1729d5703f5aSAndreas Boehler          {
1730d5703f5aSAndreas Boehler              $changes[$row['uri']] = $row['operation'];
1731d5703f5aSAndreas Boehler          }
1732d5703f5aSAndreas Boehler
1733d5703f5aSAndreas Boehler          foreach ($changes as $uri => $operation)
1734d5703f5aSAndreas Boehler          {
1735d5703f5aSAndreas Boehler              switch ($operation)
1736d5703f5aSAndreas Boehler              {
1737d5703f5aSAndreas Boehler                  case 1 :
1738d5703f5aSAndreas Boehler                      $result['added'][] = $uri;
1739d5703f5aSAndreas Boehler                      break;
1740d5703f5aSAndreas Boehler                  case 2 :
1741d5703f5aSAndreas Boehler                      $result['modified'][] = $uri;
1742d5703f5aSAndreas Boehler                      break;
1743d5703f5aSAndreas Boehler                  case 3 :
1744d5703f5aSAndreas Boehler                      $result['deleted'][] = $uri;
1745d5703f5aSAndreas Boehler                      break;
1746d5703f5aSAndreas Boehler              }
1747d5703f5aSAndreas Boehler
1748d5703f5aSAndreas Boehler          }
1749d5703f5aSAndreas Boehler      }
1750d5703f5aSAndreas Boehler      else
1751d5703f5aSAndreas Boehler      {
1752d5703f5aSAndreas Boehler          // No synctoken supplied, this is the initial sync.
1753d5703f5aSAndreas Boehler          $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1754d5703f5aSAndreas Boehler          $res = $this->sqlite->query($query);
1755d5703f5aSAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1756d5703f5aSAndreas Boehler
1757d5703f5aSAndreas Boehler          $result['added'] = $arr;
1758d5703f5aSAndreas Boehler      }
1759d5703f5aSAndreas Boehler      return $result;
1760d5703f5aSAndreas Boehler  }
1761d5703f5aSAndreas Boehler
1762a1a3b679SAndreas Boehler}
1763