xref: /plugin/davcal/helper.php (revision 65133ef9fb87962bd7be54969f74e52890dd428d)
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  /**
8380e1ddf7SAndreas Boehler   * Filter calendar pages and return only those where the current
8480e1ddf7SAndreas Boehler   * user has at least read permission.
8580e1ddf7SAndreas Boehler   *
8680e1ddf7SAndreas Boehler   * @param array $calendarPages Array with calendar pages to check
8780e1ddf7SAndreas Boehler   * @return array with filtered calendar pages
8880e1ddf7SAndreas Boehler   */
8980e1ddf7SAndreas Boehler  public function filterCalendarPagesByUserPermission($calendarPages)
9080e1ddf7SAndreas Boehler  {
9180e1ddf7SAndreas Boehler      $retList = array();
9280e1ddf7SAndreas Boehler      foreach($calendarPages as $page => $data)
9380e1ddf7SAndreas Boehler      {
940b805092SAndreas Boehler          // WebDAV Connections are always readable
950b805092SAndreas Boehler          if(strpos($page, 'webdav://') === 0)
960b805092SAndreas Boehler          {
970b805092SAndreas Boehler              $retList[$page] = $data;
980b805092SAndreas Boehler          }
990b805092SAndreas Boehler          elseif(auth_quickaclcheck($page) >= AUTH_READ)
10080e1ddf7SAndreas Boehler          {
10180e1ddf7SAndreas Boehler              $retList[$page] = $data;
10280e1ddf7SAndreas Boehler          }
10380e1ddf7SAndreas Boehler      }
10480e1ddf7SAndreas Boehler      return $retList;
10580e1ddf7SAndreas Boehler  }
10680e1ddf7SAndreas Boehler
10780e1ddf7SAndreas Boehler  /**
108185e2535SAndreas Boehler   * Get all calendar pages used by a given page
109185e2535SAndreas Boehler   * based on the stored metadata
110185e2535SAndreas Boehler   *
111185e2535SAndreas Boehler   * @param string $id optional The page id
112185e2535SAndreas Boehler   * @return mixed The pages as array or false
113185e2535SAndreas Boehler   */
114185e2535SAndreas Boehler  public function getCalendarPagesByMeta($id = null)
115185e2535SAndreas Boehler  {
116185e2535SAndreas Boehler      if(is_null($id))
117185e2535SAndreas Boehler      {
118185e2535SAndreas Boehler          global $ID;
119185e2535SAndreas Boehler          $id = $ID;
120185e2535SAndreas Boehler      }
121185e2535SAndreas Boehler
122185e2535SAndreas Boehler      $meta = $this->getCalendarMetaForPage($id);
1230b805092SAndreas Boehler
124185e2535SAndreas Boehler      if(isset($meta['id']))
125ed764890SAndreas Boehler      {
126ed764890SAndreas Boehler          // Filter the list of pages by permission
12780e1ddf7SAndreas Boehler          $pages = $this->filterCalendarPagesByUserPermission($meta['id']);
12880e1ddf7SAndreas Boehler          if(empty($pages))
129ed764890SAndreas Boehler            return false;
13080e1ddf7SAndreas Boehler          return $pages;
131ed764890SAndreas Boehler      }
132185e2535SAndreas Boehler      return false;
133185e2535SAndreas Boehler  }
134185e2535SAndreas Boehler
135185e2535SAndreas Boehler  /**
136185e2535SAndreas Boehler   * Get a list of calendar names/pages/ids/colors
137185e2535SAndreas Boehler   * for an array of page ids
138185e2535SAndreas Boehler   *
139185e2535SAndreas Boehler   * @param array $calendarPages The calendar pages to retrieve
140185e2535SAndreas Boehler   * @return array The list
141185e2535SAndreas Boehler   */
142185e2535SAndreas Boehler  public function getCalendarMapForIDs($calendarPages)
143185e2535SAndreas Boehler  {
144185e2535SAndreas Boehler      $data = array();
1454a2bf5eeSAndreas Boehler      foreach($calendarPages as $page => $color)
146185e2535SAndreas Boehler      {
1470b805092SAndreas Boehler            if(strpos($page, 'webdav://') === 0)
1480b805092SAndreas Boehler            {
1490b805092SAndreas Boehler                $wdc =& plugin_load('helper', 'webdavclient');
1500b805092SAndreas Boehler                if(is_null($wdc))
1510b805092SAndreas Boehler                    continue;
1520b805092SAndreas Boehler                $connectionId = str_replace('webdav://', '', $page);
1530b805092SAndreas Boehler                $settings = $wdc->getConnection($connectionId);
1540b805092SAndreas Boehler                $name = $settings['displayname'];
155809cb0faSAndreas Boehler                $write = $settings['write'];
1560b805092SAndreas Boehler                $calid = $connectionId;
1570b805092SAndreas Boehler            }
1580b805092SAndreas Boehler            else
1590b805092SAndreas Boehler            {
160185e2535SAndreas Boehler                $calid = $this->getCalendarIdForPage($page);
161185e2535SAndreas Boehler                if($calid !== false)
162185e2535SAndreas Boehler                {
163185e2535SAndreas Boehler                    $settings = $this->getCalendarSettings($calid);
164185e2535SAndreas Boehler                    $name = $settings['displayname'];
1650b805092SAndreas Boehler                    //$color = $settings['calendarcolor'];
166ed764890SAndreas Boehler                    $write = (auth_quickaclcheck($page) > AUTH_READ);
1670b805092SAndreas Boehler                }
1680b805092SAndreas Boehler                else
1690b805092SAndreas Boehler                {
1700b805092SAndreas Boehler                    continue;
1710b805092SAndreas Boehler                }
1720b805092SAndreas Boehler            }
173185e2535SAndreas Boehler            $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
174ed764890SAndreas Boehler                            'color' => $color, 'write' => $write);
175185e2535SAndreas Boehler      }
176185e2535SAndreas Boehler      return $data;
177185e2535SAndreas Boehler  }
178185e2535SAndreas Boehler
179185e2535SAndreas Boehler  /**
180185e2535SAndreas Boehler   * Get the saved calendar color for a given page.
181185e2535SAndreas Boehler   *
182185e2535SAndreas Boehler   * @param string $id optional The page ID
183185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
184185e2535SAndreas Boehler   */
185185e2535SAndreas Boehler  public function getCalendarColorForPage($id = null)
186185e2535SAndreas Boehler  {
187185e2535SAndreas Boehler      if(is_null($id))
188185e2535SAndreas Boehler      {
189185e2535SAndreas Boehler          global $ID;
190185e2535SAndreas Boehler          $id = $ID;
191185e2535SAndreas Boehler      }
192185e2535SAndreas Boehler
193185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
194185e2535SAndreas Boehler      if($calid === false)
195185e2535SAndreas Boehler        return false;
196185e2535SAndreas Boehler
197185e2535SAndreas Boehler      return $this->getCalendarColorForCalendar($calid);
198185e2535SAndreas Boehler  }
199185e2535SAndreas Boehler
200185e2535SAndreas Boehler  /**
201185e2535SAndreas Boehler   * Get the saved calendar color for a given calendar ID.
202185e2535SAndreas Boehler   *
203185e2535SAndreas Boehler   * @param string $id optional The calendar ID
204185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
205185e2535SAndreas Boehler   */
206185e2535SAndreas Boehler  public function getCalendarColorForCalendar($calid)
207185e2535SAndreas Boehler  {
208185e2535SAndreas Boehler      if(isset($this->cachedValues['calendarcolor'][$calid]))
209185e2535SAndreas Boehler        return $this->cachedValues['calendarcolor'][$calid];
210185e2535SAndreas Boehler
211185e2535SAndreas Boehler      $row = $this->getCalendarSettings($calid);
212185e2535SAndreas Boehler
213185e2535SAndreas Boehler      if(!isset($row['calendarcolor']))
214185e2535SAndreas Boehler        return false;
215185e2535SAndreas Boehler
216185e2535SAndreas Boehler      $color = $row['calendarcolor'];
217185e2535SAndreas Boehler      $this->cachedValues['calendarcolor'][$calid] = $color;
218185e2535SAndreas Boehler      return $color;
219185e2535SAndreas Boehler  }
220185e2535SAndreas Boehler
221185e2535SAndreas Boehler  /**
222e86c8dd3SAndreas Boehler   * Get the user's principal URL for iOS sync
223e86c8dd3SAndreas Boehler   * @param string $user the user name
224e86c8dd3SAndreas Boehler   * @return the URL to the principal sync
225e86c8dd3SAndreas Boehler   */
226e86c8dd3SAndreas Boehler  public function getPrincipalUrlForUser($user)
227e86c8dd3SAndreas Boehler  {
228e86c8dd3SAndreas Boehler      if(is_null($user))
229e86c8dd3SAndreas Boehler        return false;
230e86c8dd3SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user;
231e86c8dd3SAndreas Boehler      return $url;
232e86c8dd3SAndreas Boehler  }
233e86c8dd3SAndreas Boehler
234e86c8dd3SAndreas Boehler  /**
235185e2535SAndreas Boehler   * Set the calendar color for a given page.
236185e2535SAndreas Boehler   *
237185e2535SAndreas Boehler   * @param string $color The color definition
238185e2535SAndreas Boehler   * @param string $id optional The page ID
239185e2535SAndreas Boehler   * @return boolean True on success, otherwise false
240185e2535SAndreas Boehler   */
241185e2535SAndreas Boehler  public function setCalendarColorForPage($color, $id = null)
242185e2535SAndreas Boehler  {
243185e2535SAndreas Boehler      if(is_null($id))
244185e2535SAndreas Boehler      {
245185e2535SAndreas Boehler          global $ID;
246185e2535SAndreas Boehler          $id = $ID;
247185e2535SAndreas Boehler      }
248185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
249185e2535SAndreas Boehler      if($calid === false)
250185e2535SAndreas Boehler        return false;
251185e2535SAndreas Boehler
25251f4febbSAndreas Boehler      $query = "UPDATE calendars SET calendarcolor = ? ".
25351f4febbSAndreas Boehler               " WHERE id = ?";
25451f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $color, $calid);
255185e2535SAndreas Boehler      if($res !== false)
256185e2535SAndreas Boehler      {
257185e2535SAndreas Boehler        $this->cachedValues['calendarcolor'][$calid] = $color;
258185e2535SAndreas Boehler        return true;
259185e2535SAndreas Boehler      }
260185e2535SAndreas Boehler      return false;
261185e2535SAndreas Boehler  }
262185e2535SAndreas Boehler
263185e2535SAndreas Boehler  /**
264cb71a62aSAndreas Boehler   * Set the calendar name and description for a given page with a given
265cb71a62aSAndreas Boehler   * page id.
266cb71a62aSAndreas Boehler   * If the calendar doesn't exist, the calendar is created!
267cb71a62aSAndreas Boehler   *
268cb71a62aSAndreas Boehler   * @param string  $name The name of the new calendar
269cb71a62aSAndreas Boehler   * @param string  $description The description of the new calendar
270cb71a62aSAndreas Boehler   * @param string  $id (optional) The ID of the page
271cb71a62aSAndreas Boehler   * @param string  $userid The userid of the creating user
272cb71a62aSAndreas Boehler   *
273cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false.
274cb71a62aSAndreas Boehler   */
275a1a3b679SAndreas Boehler  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
276a1a3b679SAndreas Boehler  {
277a1a3b679SAndreas Boehler      if(is_null($id))
278a1a3b679SAndreas Boehler      {
279a1a3b679SAndreas Boehler          global $ID;
280a1a3b679SAndreas Boehler          $id = $ID;
281a1a3b679SAndreas Boehler      }
282a1a3b679SAndreas Boehler      if(is_null($userid))
28334a47953SAndreas Boehler      {
28434a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
28534a47953SAndreas Boehler        {
286a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
28734a47953SAndreas Boehler        }
28834a47953SAndreas Boehler        else
28934a47953SAndreas Boehler        {
29034a47953SAndreas Boehler          $userid = uniqid('davcal-');
29134a47953SAndreas Boehler        }
29234a47953SAndreas Boehler      }
293a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
294a1a3b679SAndreas Boehler      if($calid === false)
295a1a3b679SAndreas Boehler        return $this->createCalendarForPage($name, $description, $id, $userid);
296a1a3b679SAndreas Boehler
29751f4febbSAndreas Boehler      $query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?";
29851f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $name, $description, $calid);
299b269830cSAndreas Boehler      if($res !== false)
300b269830cSAndreas Boehler        return true;
301b269830cSAndreas Boehler      return false;
302a1a3b679SAndreas Boehler  }
303a1a3b679SAndreas Boehler
304cb71a62aSAndreas Boehler  /**
305*65133ef9SAndreas Boehler   * Update a calendar's displayname
306*65133ef9SAndreas Boehler   *
307*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
308*65133ef9SAndreas Boehler   * @param string $name The new calendar name
309*65133ef9SAndreas Boehler   *
310*65133ef9SAndreas Boehler   * @return boolean True on success, otherwise false
311*65133ef9SAndreas Boehler   */
312*65133ef9SAndreas Boehler  public function updateCalendarName($calid, $name)
313*65133ef9SAndreas Boehler  {
314*65133ef9SAndreas Boehler      $query = "UPDATE calendars SET displayname = ? WHERE id = ?";
315*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid, $name);
316*65133ef9SAndreas Boehler      if($res !== false)
317*65133ef9SAndreas Boehler      {
318*65133ef9SAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
319*65133ef9SAndreas Boehler        return true;
320*65133ef9SAndreas Boehler      }
321*65133ef9SAndreas Boehler      return false;
322*65133ef9SAndreas Boehler  }
323*65133ef9SAndreas Boehler
324*65133ef9SAndreas Boehler  /**
325*65133ef9SAndreas Boehler   * Update the calendar description
326*65133ef9SAndreas Boehler   *
327*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
328*65133ef9SAndreas Boehler   * @param string $description The new calendar's description
329*65133ef9SAndreas Boehler   *
330*65133ef9SAndreas Boehler   * @return boolean True on success, otherwise false
331*65133ef9SAndreas Boehler   */
332*65133ef9SAndreas Boehler  public function updateCalendarDescription($calid, $description)
333*65133ef9SAndreas Boehler  {
334*65133ef9SAndreas Boehler      $query = "UPDATE calendars SET description = ? WHERE id = ?";
335*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid, $description);
336*65133ef9SAndreas Boehler      if($res !== false)
337*65133ef9SAndreas Boehler      {
338*65133ef9SAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
339*65133ef9SAndreas Boehler        return true;
340*65133ef9SAndreas Boehler      }
341*65133ef9SAndreas Boehler      return false;
342*65133ef9SAndreas Boehler  }
343*65133ef9SAndreas Boehler
344*65133ef9SAndreas Boehler  /**
345*65133ef9SAndreas Boehler   * Update a calendar's timezone information
346*65133ef9SAndreas Boehler   *
347*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
348*65133ef9SAndreas Boehler   * @param string $timezone The new timezone to set
349*65133ef9SAndreas Boehler   *
350*65133ef9SAndreas Boehler   * @return boolean True on success, otherwise false
351*65133ef9SAndreas Boehler   */
352*65133ef9SAndreas Boehler  public function updateCalendarTimezone($calid, $timezone)
353*65133ef9SAndreas Boehler  {
354*65133ef9SAndreas Boehler      $query = "UPDATE calendars SET timezone = ? WHERE id = ?";
355*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid, $timezone);
356*65133ef9SAndreas Boehler      if($res !== false)
357*65133ef9SAndreas Boehler      {
358*65133ef9SAndreas Boehler        $this->updateSyncTokenLog($calid, '', 'modified');
359*65133ef9SAndreas Boehler        return true;
360*65133ef9SAndreas Boehler      }
361*65133ef9SAndreas Boehler      return false;
362*65133ef9SAndreas Boehler  }
363*65133ef9SAndreas Boehler
364*65133ef9SAndreas Boehler  /**
365cb71a62aSAndreas Boehler   * Save the personal settings to the SQLite database 'calendarsettings'.
366cb71a62aSAndreas Boehler   *
367cb71a62aSAndreas Boehler   * @param array  $settings The settings array to store
368cb71a62aSAndreas Boehler   * @param string $userid (optional) The userid to store
369cb71a62aSAndreas Boehler   *
370cb71a62aSAndreas Boehler   * @param boolean True on success, otherwise false
371cb71a62aSAndreas Boehler   */
372a495d34cSAndreas Boehler  public function savePersonalSettings($settings, $userid = null)
373a495d34cSAndreas Boehler  {
374a495d34cSAndreas Boehler      if(is_null($userid))
37534a47953SAndreas Boehler      {
37634a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
37734a47953SAndreas Boehler          {
378a495d34cSAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
37934a47953SAndreas Boehler          }
38034a47953SAndreas Boehler          else
38134a47953SAndreas Boehler          {
38234a47953SAndreas Boehler              return false;
38334a47953SAndreas Boehler          }
38434a47953SAndreas Boehler      }
385a495d34cSAndreas Boehler      $this->sqlite->query("BEGIN TRANSACTION");
386a495d34cSAndreas Boehler
38751f4febbSAndreas Boehler      $query = "DELETE FROM calendarsettings WHERE userid = ?";
38851f4febbSAndreas Boehler      $this->sqlite->query($query, $userid);
389bd883736SAndreas Boehler
390a495d34cSAndreas Boehler      foreach($settings as $key => $value)
391a495d34cSAndreas Boehler      {
39251f4febbSAndreas Boehler          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)";
39351f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $userid, $key, $value);
394a495d34cSAndreas Boehler          if($res === false)
395a495d34cSAndreas Boehler              return false;
396a495d34cSAndreas Boehler      }
397a495d34cSAndreas Boehler      $this->sqlite->query("COMMIT TRANSACTION");
398185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
399a495d34cSAndreas Boehler      return true;
400a495d34cSAndreas Boehler  }
401a495d34cSAndreas Boehler
402cb71a62aSAndreas Boehler  /**
403cb71a62aSAndreas Boehler   * Retrieve the settings array for a given user id.
404cb71a62aSAndreas Boehler   * Some sane defaults are returned, currently:
405cb71a62aSAndreas Boehler   *
406cb71a62aSAndreas Boehler   *    timezone    => local
407cb71a62aSAndreas Boehler   *    weeknumbers => 0
408cb71a62aSAndreas Boehler   *    workweek    => 0
409cb71a62aSAndreas Boehler   *
410cb71a62aSAndreas Boehler   * @param string $userid (optional) The user id to retrieve
411cb71a62aSAndreas Boehler   *
412cb71a62aSAndreas Boehler   * @return array The settings array
413cb71a62aSAndreas Boehler   */
414a495d34cSAndreas Boehler  public function getPersonalSettings($userid = null)
415a495d34cSAndreas Boehler  {
416bd883736SAndreas Boehler      // Some sane default settings
417bd883736SAndreas Boehler      $settings = array(
418fb813b30SAndreas Boehler        'timezone' => $this->getConf('timezone'),
419fb813b30SAndreas Boehler        'weeknumbers' => $this->getConf('weeknumbers'),
420fb813b30SAndreas Boehler        'workweek' => $this->getConf('workweek'),
4211d5bdcd0SAndreas Boehler        'monday' => $this->getConf('monday'),
4221d5bdcd0SAndreas Boehler        'timeformat' => $this->getConf('timeformat')
423bd883736SAndreas Boehler      );
42434a47953SAndreas Boehler      if(is_null($userid))
42534a47953SAndreas Boehler      {
42634a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
42734a47953SAndreas Boehler          {
42834a47953SAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
42934a47953SAndreas Boehler          }
43034a47953SAndreas Boehler          else
43134a47953SAndreas Boehler          {
43234a47953SAndreas Boehler            return $settings;
43334a47953SAndreas Boehler          }
43434a47953SAndreas Boehler      }
43534a47953SAndreas Boehler
43634a47953SAndreas Boehler      if(isset($this->cachedValues['settings'][$userid]))
43734a47953SAndreas Boehler        return $this->cachedValues['settings'][$userid];
43851f4febbSAndreas Boehler      $query = "SELECT key, value FROM calendarsettings WHERE userid = ?";
43951f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $userid);
440a495d34cSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
441a495d34cSAndreas Boehler      foreach($arr as $row)
442a495d34cSAndreas Boehler      {
443a495d34cSAndreas Boehler          $settings[$row['key']] = $row['value'];
444a495d34cSAndreas Boehler      }
445185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
446a495d34cSAndreas Boehler      return $settings;
447a495d34cSAndreas Boehler  }
448a495d34cSAndreas Boehler
449cb71a62aSAndreas Boehler  /**
450cb71a62aSAndreas Boehler   * Retrieve the calendar ID based on a page ID from the SQLite table
451cb71a62aSAndreas Boehler   * 'pagetocalendarmapping'.
452cb71a62aSAndreas Boehler   *
453cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to retrieve the corresponding calendar
454cb71a62aSAndreas Boehler   *
455cb71a62aSAndreas Boehler   * @return mixed the ID on success, otherwise false
456cb71a62aSAndreas Boehler   */
457a1a3b679SAndreas Boehler  public function getCalendarIdForPage($id = null)
458a1a3b679SAndreas Boehler  {
459a1a3b679SAndreas Boehler      if(is_null($id))
460a1a3b679SAndreas Boehler      {
461a1a3b679SAndreas Boehler          global $ID;
462a1a3b679SAndreas Boehler          $id = $ID;
463a1a3b679SAndreas Boehler      }
464a1a3b679SAndreas Boehler
465185e2535SAndreas Boehler      if(isset($this->cachedValues['calid'][$id]))
466185e2535SAndreas Boehler        return $this->cachedValues['calid'][$id];
467185e2535SAndreas Boehler
46851f4febbSAndreas Boehler      $query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?";
46951f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $id);
470a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
471a1a3b679SAndreas Boehler      if(isset($row['calid']))
472185e2535SAndreas Boehler      {
473185e2535SAndreas Boehler        $calid = $row['calid'];
474185e2535SAndreas Boehler        $this->cachedValues['calid'] = $calid;
475185e2535SAndreas Boehler        return $calid;
476185e2535SAndreas Boehler      }
477a1a3b679SAndreas Boehler      return false;
478a1a3b679SAndreas Boehler  }
479a1a3b679SAndreas Boehler
480cb71a62aSAndreas Boehler  /**
481cb71a62aSAndreas Boehler   * Retrieve the complete calendar id to page mapping.
482cb71a62aSAndreas Boehler   * This is necessary to be able to retrieve a list of
483cb71a62aSAndreas Boehler   * calendars for a given user and check the access rights.
484cb71a62aSAndreas Boehler   *
485cb71a62aSAndreas Boehler   * @return array The mapping array
486cb71a62aSAndreas Boehler   */
487a1a3b679SAndreas Boehler  public function getCalendarIdToPageMapping()
488a1a3b679SAndreas Boehler  {
489a1a3b679SAndreas Boehler      $query = "SELECT calid, page FROM pagetocalendarmapping";
490a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
491a1a3b679SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
492a1a3b679SAndreas Boehler      return $arr;
493a1a3b679SAndreas Boehler  }
494a1a3b679SAndreas Boehler
495cb71a62aSAndreas Boehler  /**
496cb71a62aSAndreas Boehler   * Retrieve all calendar IDs a given user has access to.
497cb71a62aSAndreas Boehler   * The user is specified by the principalUri, so the
498cb71a62aSAndreas Boehler   * user name is actually split from the URI component.
499cb71a62aSAndreas Boehler   *
500cb71a62aSAndreas Boehler   * Access rights are checked against DokuWiki's ACL
501cb71a62aSAndreas Boehler   * and applied accordingly.
502cb71a62aSAndreas Boehler   *
503cb71a62aSAndreas Boehler   * @param string $principalUri The principal URI to work on
504cb71a62aSAndreas Boehler   *
505cb71a62aSAndreas Boehler   * @return array An associative array of calendar IDs
506cb71a62aSAndreas Boehler   */
507a1a3b679SAndreas Boehler  public function getCalendarIdsForUser($principalUri)
508a1a3b679SAndreas Boehler  {
50934a47953SAndreas Boehler      global $auth;
510a1a3b679SAndreas Boehler      $user = explode('/', $principalUri);
511a1a3b679SAndreas Boehler      $user = end($user);
512a1a3b679SAndreas Boehler      $mapping = $this->getCalendarIdToPageMapping();
513a1a3b679SAndreas Boehler      $calids = array();
51434a47953SAndreas Boehler      $ud = $auth->getUserData($user);
51534a47953SAndreas Boehler      $groups = $ud['grps'];
516a1a3b679SAndreas Boehler      foreach($mapping as $row)
517a1a3b679SAndreas Boehler      {
518a1a3b679SAndreas Boehler          $id = $row['calid'];
519a1a3b679SAndreas Boehler          $page = $row['page'];
52034a47953SAndreas Boehler          $acl = auth_aclcheck($page, $user, $groups);
521a1a3b679SAndreas Boehler          if($acl >= AUTH_READ)
522a1a3b679SAndreas Boehler          {
523a1a3b679SAndreas Boehler              $write = $acl > AUTH_READ;
524a1a3b679SAndreas Boehler              $calids[$id] = array('readonly' => !$write);
525a1a3b679SAndreas Boehler          }
526a1a3b679SAndreas Boehler      }
527a1a3b679SAndreas Boehler      return $calids;
528a1a3b679SAndreas Boehler  }
529a1a3b679SAndreas Boehler
530cb71a62aSAndreas Boehler  /**
531cb71a62aSAndreas Boehler   * Create a new calendar for a given page ID and set name and description
532cb71a62aSAndreas Boehler   * accordingly. Also update the pagetocalendarmapping table on success.
533cb71a62aSAndreas Boehler   *
534cb71a62aSAndreas Boehler   * @param string $name The calendar's name
535cb71a62aSAndreas Boehler   * @param string $description The calendar's description
536cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to work on
537cb71a62aSAndreas Boehler   * @param string $userid (optional) The user ID that created the calendar
538cb71a62aSAndreas Boehler   *
539cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
540cb71a62aSAndreas Boehler   */
541a1a3b679SAndreas Boehler  public function createCalendarForPage($name, $description, $id = null, $userid = null)
542a1a3b679SAndreas Boehler  {
543a1a3b679SAndreas Boehler      if(is_null($id))
544a1a3b679SAndreas Boehler      {
545a1a3b679SAndreas Boehler          global $ID;
546a1a3b679SAndreas Boehler          $id = $ID;
547a1a3b679SAndreas Boehler      }
548a1a3b679SAndreas Boehler      if(is_null($userid))
54934a47953SAndreas Boehler      {
55034a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
55134a47953SAndreas Boehler        {
552a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
55334a47953SAndreas Boehler        }
55434a47953SAndreas Boehler        else
55534a47953SAndreas Boehler        {
55634a47953SAndreas Boehler          $userid = uniqid('davcal-');
55734a47953SAndreas Boehler        }
55834a47953SAndreas Boehler      }
559a1a3b679SAndreas Boehler      $values = array('principals/'.$userid,
560a1a3b679SAndreas Boehler                      $name,
561a1a3b679SAndreas Boehler                      str_replace(array('/', ' ', ':'), '_', $id),
562a1a3b679SAndreas Boehler                      $description,
563a1a3b679SAndreas Boehler                      'VEVENT,VTODO',
56455a741c0SAndreas Boehler                      0,
56555a741c0SAndreas Boehler                      1);
56651f4febbSAndreas Boehler      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ".
56751f4febbSAndreas Boehler               "VALUES (?, ?, ?, ?, ?, ?, ?)";
56851f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]);
56955a741c0SAndreas Boehler      if($res === false)
57055a741c0SAndreas Boehler        return false;
571cb71a62aSAndreas Boehler
572cb71a62aSAndreas Boehler      // Get the new calendar ID
57351f4febbSAndreas Boehler      $query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ".
57451f4febbSAndreas Boehler               "uri = ? AND description = ?";
57551f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $values[0], $values[1], $values[2], $values[3]);
576a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
577cb71a62aSAndreas Boehler
578cb71a62aSAndreas Boehler      // Update the pagetocalendarmapping table with the new calendar ID
579a1a3b679SAndreas Boehler      if(isset($row['id']))
580a1a3b679SAndreas Boehler      {
58151f4febbSAndreas Boehler          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)";
58251f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $id, $row['id']);
58355a741c0SAndreas Boehler          return ($res !== false);
584a1a3b679SAndreas Boehler      }
585a1a3b679SAndreas Boehler
586a1a3b679SAndreas Boehler      return false;
587a1a3b679SAndreas Boehler  }
588a1a3b679SAndreas Boehler
589cb71a62aSAndreas Boehler  /**
590*65133ef9SAndreas Boehler   * Add a new calendar entry to the given calendar. Calendar data is
591*65133ef9SAndreas Boehler   * specified as ICS file, thus it needs to be parsed first.
592*65133ef9SAndreas Boehler   *
593*65133ef9SAndreas Boehler   * This is mainly needed for the sync support.
594*65133ef9SAndreas Boehler   *
595*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
596*65133ef9SAndreas Boehler   * @param string $uri The new object URI
597*65133ef9SAndreas Boehler   * @param string $ics The ICS file
598*65133ef9SAndreas Boehler   *
599*65133ef9SAndreas Boehler   * @return mixed The etag.
600*65133ef9SAndreas Boehler   */
601*65133ef9SAndreas Boehler  public function addCalendarEntryToCalendarByICS($calid, $uri, $ics)
602*65133ef9SAndreas Boehler  {
603*65133ef9SAndreas Boehler    $extraData = $this->getDenormalizedData($ics);
604*65133ef9SAndreas Boehler
605*65133ef9SAndreas Boehler    $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)";
606*65133ef9SAndreas Boehler    $res = $this->sqlite->query($query,
607*65133ef9SAndreas Boehler            $calid,
608*65133ef9SAndreas Boehler            $uri,
609*65133ef9SAndreas Boehler            $ics,
610*65133ef9SAndreas Boehler            time(),
611*65133ef9SAndreas Boehler            $extraData['etag'],
612*65133ef9SAndreas Boehler            $extraData['size'],
613*65133ef9SAndreas Boehler            $extraData['componentType'],
614*65133ef9SAndreas Boehler            $extraData['firstOccurence'],
615*65133ef9SAndreas Boehler            $extraData['lastOccurence'],
616*65133ef9SAndreas Boehler            $extraData['uid']);
617*65133ef9SAndreas Boehler            // If successfully, update the sync token database
618*65133ef9SAndreas Boehler    if($res !== false)
619*65133ef9SAndreas Boehler    {
620*65133ef9SAndreas Boehler        $this->updateSyncTokenLog($calid, $uri, 'added');
621*65133ef9SAndreas Boehler    }
622*65133ef9SAndreas Boehler    return $extraData['etag'];
623*65133ef9SAndreas Boehler  }
624*65133ef9SAndreas Boehler
625*65133ef9SAndreas Boehler  /**
626*65133ef9SAndreas Boehler   * Edit a calendar entry by providing a new ICS file. This is mainly
627*65133ef9SAndreas Boehler   * needed for the sync support.
628*65133ef9SAndreas Boehler   *
629*65133ef9SAndreas Boehler   * @param int $calid The calendar's IS
630*65133ef9SAndreas Boehler   * @param string $uri The object's URI to modify
631*65133ef9SAndreas Boehler   * @param string $ics The new object's ICS file
632*65133ef9SAndreas Boehler   */
633*65133ef9SAndreas Boehler  public function editCalendarEntryToCalendarByICS($calid, $uri, $ics)
634*65133ef9SAndreas Boehler  {
635*65133ef9SAndreas Boehler      $extraData = $this->getDenormalizedData($ics);
636*65133ef9SAndreas Boehler
637*65133ef9SAndreas Boehler      $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?";
638*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query,
639*65133ef9SAndreas Boehler        $ics,
640*65133ef9SAndreas Boehler        time(),
641*65133ef9SAndreas Boehler        $extraData['etag'],
642*65133ef9SAndreas Boehler        $extraData['size'],
643*65133ef9SAndreas Boehler        $extraData['componentType'],
644*65133ef9SAndreas Boehler        $extraData['firstOccurence'],
645*65133ef9SAndreas Boehler        $extraData['lastOccurence'],
646*65133ef9SAndreas Boehler        $extraData['uid'],
647*65133ef9SAndreas Boehler        $calid,
648*65133ef9SAndreas Boehler        $uri
649*65133ef9SAndreas Boehler      );
650*65133ef9SAndreas Boehler      if($res !== false)
651*65133ef9SAndreas Boehler      {
652*65133ef9SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'modified');
653*65133ef9SAndreas Boehler      }
654*65133ef9SAndreas Boehler      return $extraData['etag'];
655*65133ef9SAndreas Boehler  }
656*65133ef9SAndreas Boehler
657*65133ef9SAndreas Boehler  /**
658cb71a62aSAndreas Boehler   * Add a new iCal entry for a given page, i.e. a given calendar.
659cb71a62aSAndreas Boehler   *
660cb71a62aSAndreas Boehler   * The parameter array needs to contain
661cb71a62aSAndreas Boehler   *   detectedtz       => The timezone as detected by the browser
66282a48dfbSAndreas Boehler   *   currenttz        => The timezone in use by the calendar
663cb71a62aSAndreas Boehler   *   eventfrom        => The event's start date
664cb71a62aSAndreas Boehler   *   eventfromtime    => The event's start time
665cb71a62aSAndreas Boehler   *   eventto          => The event's end date
666cb71a62aSAndreas Boehler   *   eventtotime      => The event's end time
667cb71a62aSAndreas Boehler   *   eventname        => The event's name
668cb71a62aSAndreas Boehler   *   eventdescription => The event's description
669cb71a62aSAndreas Boehler   *
670cb71a62aSAndreas Boehler   * @param string $id The page ID to work on
671cb71a62aSAndreas Boehler   * @param string $user The user who created the calendar
672cb71a62aSAndreas Boehler   * @param string $params A parameter array with values to create
673cb71a62aSAndreas Boehler   *
674cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
675cb71a62aSAndreas Boehler   */
676a1a3b679SAndreas Boehler  public function addCalendarEntryToCalendarForPage($id, $user, $params)
677a1a3b679SAndreas Boehler  {
67882a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
67982a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
68082a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
681a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
682bd883736SAndreas Boehler      else
683bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
684cb71a62aSAndreas Boehler
685cb71a62aSAndreas Boehler      // Retrieve dates from settings
686b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
687b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
688b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
689b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
690cb71a62aSAndreas Boehler
691cb71a62aSAndreas Boehler      // Load SabreDAV
6929bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
693a1a3b679SAndreas Boehler      $vcalendar = new \Sabre\VObject\Component\VCalendar();
694cb71a62aSAndreas Boehler
695cb71a62aSAndreas Boehler      // Add VCalendar, UID and Event Name
696a1a3b679SAndreas Boehler      $event = $vcalendar->add('VEVENT');
697b269830cSAndreas Boehler      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
698b269830cSAndreas Boehler      $event->add('UID', $uuid);
699a1a3b679SAndreas Boehler      $event->summary = $params['eventname'];
700cb71a62aSAndreas Boehler
701cb71a62aSAndreas Boehler      // Add a description if requested
7020eebc909SAndreas Boehler      $description = $params['eventdescription'];
7030eebc909SAndreas Boehler      if($description !== '')
7040eebc909SAndreas Boehler        $event->add('DESCRIPTION', $description);
705cb71a62aSAndreas Boehler
7064ecb526cSAndreas Boehler      // Add attachments
7074ecb526cSAndreas Boehler      $attachments = $params['attachments'];
70882a48dfbSAndreas Boehler      if(!is_null($attachments))
7094ecb526cSAndreas Boehler        foreach($attachments as $attachment)
7104ecb526cSAndreas Boehler          $event->add('ATTACH', $attachment);
7114ecb526cSAndreas Boehler
712cb71a62aSAndreas Boehler      // Create a timestamp for last modified, created and dtstamp values in UTC
713b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
714b269830cSAndreas Boehler      $event->add('DTSTAMP', $dtStamp);
715b269830cSAndreas Boehler      $event->add('CREATED', $dtStamp);
716b269830cSAndreas Boehler      $event->add('LAST-MODIFIED', $dtStamp);
717cb71a62aSAndreas Boehler
718cb71a62aSAndreas Boehler      // Adjust the start date, based on the given timezone information
719b269830cSAndreas Boehler      $dtStart = new \DateTime();
720a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
721b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
722cb71a62aSAndreas Boehler
723cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
724b269830cSAndreas Boehler      if($params['allday'] != '1')
725b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
726cb71a62aSAndreas Boehler
727cb71a62aSAndreas Boehler      // Adjust the end date, based on the given timezone information
728b269830cSAndreas Boehler      $dtEnd = new \DateTime();
729a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
730b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
731cb71a62aSAndreas Boehler
732cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
733b269830cSAndreas Boehler      if($params['allday'] != '1')
734b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
735cb71a62aSAndreas Boehler
736b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
737b269830cSAndreas Boehler      if($params['allday'] == '1')
738b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
739cb71a62aSAndreas Boehler
740cb71a62aSAndreas Boehler      // Really add Start and End events
741b269830cSAndreas Boehler      $dtStartEv = $event->add('DTSTART', $dtStart);
742b269830cSAndreas Boehler      $dtEndEv = $event->add('DTEND', $dtEnd);
743cb71a62aSAndreas Boehler
744cb71a62aSAndreas Boehler      // Adjust the DATE format for allday events
745b269830cSAndreas Boehler      if($params['allday'] == '1')
746b269830cSAndreas Boehler      {
747b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
748b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
749b269830cSAndreas Boehler      }
750cb71a62aSAndreas Boehler
751809cb0faSAndreas Boehler      $eventStr = $vcalendar->serialize();
752809cb0faSAndreas Boehler
753809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
754809cb0faSAndreas Boehler      {
755809cb0faSAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
756809cb0faSAndreas Boehler          if(is_null($wdc))
757809cb0faSAndreas Boehler            return false;
758809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
759809cb0faSAndreas Boehler          return $wdc->addCalendarEntry($connectionId, $eventStr);
760809cb0faSAndreas Boehler      }
761809cb0faSAndreas Boehler      else
762809cb0faSAndreas Boehler      {
763cb71a62aSAndreas Boehler          // Actually add the values to the database
764a1a3b679SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
765a1a3b679SAndreas Boehler          $uri = uniqid('dokuwiki-').'.ics';
766a1a3b679SAndreas Boehler          $now = new DateTime();
767a1a3b679SAndreas Boehler
76851f4febbSAndreas Boehler          $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
76951f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT',
77051f4febbSAndreas Boehler                                      $event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(),
77151f4febbSAndreas Boehler                                      strlen($eventStr), md5($eventStr), $uuid);
772cb71a62aSAndreas Boehler
773cb71a62aSAndreas Boehler          // If successfully, update the sync token database
77455a741c0SAndreas Boehler          if($res !== false)
77555a741c0SAndreas Boehler          {
77655a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'added');
777a1a3b679SAndreas Boehler              return true;
778a1a3b679SAndreas Boehler          }
779809cb0faSAndreas Boehler      }
78055a741c0SAndreas Boehler      return false;
78155a741c0SAndreas Boehler  }
782a1a3b679SAndreas Boehler
783cb71a62aSAndreas Boehler  /**
784cb71a62aSAndreas Boehler   * Retrieve the calendar settings of a given calendar id
785cb71a62aSAndreas Boehler   *
786cb71a62aSAndreas Boehler   * @param string $calid The calendar ID
787cb71a62aSAndreas Boehler   *
788cb71a62aSAndreas Boehler   * @return array The calendar settings array
789cb71a62aSAndreas Boehler   */
790b269830cSAndreas Boehler  public function getCalendarSettings($calid)
791b269830cSAndreas Boehler  {
792*65133ef9SAndreas Boehler      $query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id= ? ";
79351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
794b269830cSAndreas Boehler      $row = $this->sqlite->res2row($res);
795b269830cSAndreas Boehler      return $row;
796b269830cSAndreas Boehler  }
797b269830cSAndreas Boehler
798cb71a62aSAndreas Boehler  /**
799cb71a62aSAndreas Boehler   * Retrieve all events that are within a given date range,
800cb71a62aSAndreas Boehler   * based on the timezone setting.
801cb71a62aSAndreas Boehler   *
802cb71a62aSAndreas Boehler   * There is also support for retrieving recurring events,
803cb71a62aSAndreas Boehler   * using Sabre's VObject Iterator. Recurring events are represented
804cb71a62aSAndreas Boehler   * as individual calendar entries with the same UID.
805cb71a62aSAndreas Boehler   *
806cb71a62aSAndreas Boehler   * @param string $id The page ID to work with
807cb71a62aSAndreas Boehler   * @param string $user The user ID to work with
808cb71a62aSAndreas Boehler   * @param string $startDate The start date as a string
809cb71a62aSAndreas Boehler   * @param string $endDate The end date as a string
8104a2bf5eeSAndreas Boehler   * @param string $color (optional) The calendar's color
811cb71a62aSAndreas Boehler   *
812cb71a62aSAndreas Boehler   * @return array An array containing the calendar entries.
813cb71a62aSAndreas Boehler   */
8144a2bf5eeSAndreas Boehler  public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null)
815a1a3b679SAndreas Boehler  {
81682a48dfbSAndreas Boehler      if($timezone !== '' && $timezone !== 'local')
81782a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($timezone);
818bd883736SAndreas Boehler      else
819bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
820a1a3b679SAndreas Boehler      $data = array();
821cb71a62aSAndreas Boehler
822a469597cSAndreas Boehler      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?";
823a469597cSAndreas Boehler      $startTs = null;
824a469597cSAndreas Boehler      $endTs = null;
825a469597cSAndreas Boehler      if($startDate !== null)
826a469597cSAndreas Boehler      {
827a1a3b679SAndreas Boehler        $startTs = new \DateTime($startDate);
828a469597cSAndreas Boehler        $query .= " AND lastoccurence > ".$this->sqlite->quote_string($startTs->getTimestamp());
829a469597cSAndreas Boehler      }
830a469597cSAndreas Boehler      if($endDate !== null)
831a469597cSAndreas Boehler      {
832a1a3b679SAndreas Boehler        $endTs = new \DateTime($endDate);
833a469597cSAndreas Boehler        $query .= " AND firstoccurence < ".$this->sqlite->quote_string($endTs->getTimestamp());
834a469597cSAndreas Boehler      }
835cb71a62aSAndreas Boehler
8360b805092SAndreas Boehler      // Load SabreDAV
8370b805092SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
8380b805092SAndreas Boehler
8390b805092SAndreas Boehler      if(strpos($id, 'webdav://') === 0)
8400b805092SAndreas Boehler      {
8410b805092SAndreas Boehler          $wdc =& plugin_load('helper', 'webdavclient');
8420b805092SAndreas Boehler          if(is_null($wdc))
8430b805092SAndreas Boehler            return $data;
8440b805092SAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
8450b805092SAndreas Boehler          $arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate);
8460b805092SAndreas Boehler      }
8470b805092SAndreas Boehler      else
8480b805092SAndreas Boehler      {
8490b805092SAndreas Boehler          $calid = $this->getCalendarIdForPage($id);
8500b805092SAndreas Boehler          if(is_null($color))
8510b805092SAndreas Boehler            $color = $this->getCalendarColorForCalendar($calid);
8520b805092SAndreas Boehler
853cb71a62aSAndreas Boehler          // Retrieve matching calendar objects
854a469597cSAndreas Boehler          $res = $this->sqlite->query($query, $calid);
855a1a3b679SAndreas Boehler          $arr = $this->sqlite->res2arr($res);
8560b805092SAndreas Boehler      }
857cb71a62aSAndreas Boehler
858cb71a62aSAndreas Boehler      // Parse individual calendar entries
859a1a3b679SAndreas Boehler      foreach($arr as $row)
860a1a3b679SAndreas Boehler      {
861a1a3b679SAndreas Boehler          if(isset($row['calendardata']))
862a1a3b679SAndreas Boehler          {
863b269830cSAndreas Boehler              $entry = array();
864a1a3b679SAndreas Boehler              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
865ebc4eb57SAndreas Boehler              $recurrence = $vcal->VEVENT->RRULE;
866cb71a62aSAndreas Boehler              // If it is a recurring event, pass it through Sabre's EventIterator
867ebc4eb57SAndreas Boehler              if($recurrence != null)
868ebc4eb57SAndreas Boehler              {
869ebc4eb57SAndreas Boehler                  $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
870ebc4eb57SAndreas Boehler                  $rEvents->rewind();
871e9b7d302SAndreas Boehler                  while($rEvents->valid())
872ebc4eb57SAndreas Boehler                  {
873ebc4eb57SAndreas Boehler                      $event = $rEvents->getEventObject();
874cb71a62aSAndreas Boehler                      // If we are after the given time range, exit
875a469597cSAndreas Boehler                      if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()))
876e9b7d302SAndreas Boehler                          break;
877cb71a62aSAndreas Boehler
878cb71a62aSAndreas Boehler                      // If we are before the given time range, continue
879a469597cSAndreas Boehler                      if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()))
880ebc4eb57SAndreas Boehler                      {
881ebc4eb57SAndreas Boehler                          $rEvents->next();
882ebc4eb57SAndreas Boehler                          continue;
883ebc4eb57SAndreas Boehler                      }
884cb71a62aSAndreas Boehler
885cb71a62aSAndreas Boehler                      // If we are within the given time range, parse the event
886185e2535SAndreas Boehler                      $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
887ebc4eb57SAndreas Boehler                      $rEvents->next();
888ebc4eb57SAndreas Boehler                  }
889ebc4eb57SAndreas Boehler              }
890ebc4eb57SAndreas Boehler              else
891185e2535SAndreas Boehler                $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
892ebc4eb57SAndreas Boehler          }
893ebc4eb57SAndreas Boehler      }
894ebc4eb57SAndreas Boehler      return $data;
895ebc4eb57SAndreas Boehler  }
896ebc4eb57SAndreas Boehler
897cb71a62aSAndreas Boehler  /**
898cb71a62aSAndreas Boehler   * Helper function that parses the iCal data of a VEVENT to a calendar entry.
899cb71a62aSAndreas Boehler   *
900cb71a62aSAndreas Boehler   * @param \Sabre\VObject\VEvent $event The event to parse
901cb71a62aSAndreas Boehler   * @param \DateTimeZone $timezone The timezone object
902cb71a62aSAndreas Boehler   * @param string $uid The entry's UID
9033c86dda8SAndreas Boehler   * @param boolean $recurring (optional) Set to true to define a recurring event
904cb71a62aSAndreas Boehler   *
905cb71a62aSAndreas Boehler   * @return array The parse calendar entry
906cb71a62aSAndreas Boehler   */
907185e2535SAndreas Boehler  private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
908ebc4eb57SAndreas Boehler  {
909ebc4eb57SAndreas Boehler      $entry = array();
910ebc4eb57SAndreas Boehler      $start = $event->DTSTART;
911cb71a62aSAndreas Boehler      // Parse only if the start date/time is present
912b269830cSAndreas Boehler      if($start !== null)
913b269830cSAndreas Boehler      {
914b269830cSAndreas Boehler        $dtStart = $start->getDateTime();
915b269830cSAndreas Boehler        $dtStart->setTimezone($timezone);
916bf0ad2b4SAndreas Boehler
917bf0ad2b4SAndreas Boehler        // moment.js doesn't like times be given even if
918bf0ad2b4SAndreas Boehler        // allDay is set to true
919bf0ad2b4SAndreas Boehler        // This should fix T23
920b269830cSAndreas Boehler        if($start['VALUE'] == 'DATE')
921bf0ad2b4SAndreas Boehler        {
922b269830cSAndreas Boehler          $entry['allDay'] = true;
923bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format("Y-m-d");
924bf0ad2b4SAndreas Boehler        }
925b269830cSAndreas Boehler        else
926bf0ad2b4SAndreas Boehler        {
927b269830cSAndreas Boehler          $entry['allDay'] = false;
928bf0ad2b4SAndreas Boehler          $entry['start'] = $dtStart->format(\DateTime::ATOM);
929bf0ad2b4SAndreas Boehler        }
930b269830cSAndreas Boehler      }
931ebc4eb57SAndreas Boehler      $end = $event->DTEND;
932bf0ad2b4SAndreas Boehler      // Parse only if the end date/time is present
933b269830cSAndreas Boehler      if($end !== null)
934b269830cSAndreas Boehler      {
935b269830cSAndreas Boehler        $dtEnd = $end->getDateTime();
936b269830cSAndreas Boehler        $dtEnd->setTimezone($timezone);
937bf0ad2b4SAndreas Boehler        if($end['VALUE'] == 'DATE')
938bf0ad2b4SAndreas Boehler          $entry['end'] = $dtEnd->format("Y-m-d");
939bf0ad2b4SAndreas Boehler        else
940b269830cSAndreas Boehler          $entry['end'] = $dtEnd->format(\DateTime::ATOM);
941b269830cSAndreas Boehler      }
942ebc4eb57SAndreas Boehler      $description = $event->DESCRIPTION;
9430eebc909SAndreas Boehler      if($description !== null)
9440eebc909SAndreas Boehler        $entry['description'] = (string)$description;
9450eebc909SAndreas Boehler      else
9460eebc909SAndreas Boehler        $entry['description'] = '';
9474ecb526cSAndreas Boehler      $attachments = $event->ATTACH;
9484ecb526cSAndreas Boehler      if($attachments !== null)
9494ecb526cSAndreas Boehler      {
9504ecb526cSAndreas Boehler        $entry['attachments'] = array();
9514ecb526cSAndreas Boehler        foreach($attachments as $attachment)
9524ecb526cSAndreas Boehler          $entry['attachments'][] = (string)$attachment;
9534ecb526cSAndreas Boehler      }
954ebc4eb57SAndreas Boehler      $entry['title'] = (string)$event->summary;
955ebc4eb57SAndreas Boehler      $entry['id'] = $uid;
956185e2535SAndreas Boehler      $entry['page'] = $page;
957185e2535SAndreas Boehler      $entry['color'] = $color;
9583c86dda8SAndreas Boehler      $entry['recurring'] = $recurring;
959185e2535SAndreas Boehler
960ebc4eb57SAndreas Boehler      return $entry;
961a1a3b679SAndreas Boehler  }
962a1a3b679SAndreas Boehler
963cb71a62aSAndreas Boehler  /**
964cb71a62aSAndreas Boehler   * Retrieve an event by its UID
965cb71a62aSAndreas Boehler   *
966cb71a62aSAndreas Boehler   * @param string $uid The event's UID
967cb71a62aSAndreas Boehler   *
968cb71a62aSAndreas Boehler   * @return mixed The table row with the given event
969cb71a62aSAndreas Boehler   */
970a1a3b679SAndreas Boehler  public function getEventWithUid($uid)
971a1a3b679SAndreas Boehler  {
97251f4febbSAndreas Boehler      $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?";
97351f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
974a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
975a1a3b679SAndreas Boehler      return $row;
976a1a3b679SAndreas Boehler  }
977a1a3b679SAndreas Boehler
978cb71a62aSAndreas Boehler  /**
979*65133ef9SAndreas Boehler   * Retrieve information of a calendar's object, not including the actual
980*65133ef9SAndreas Boehler   * calendar data! This is mainly neede for the sync support.
981*65133ef9SAndreas Boehler   *
982*65133ef9SAndreas Boehler   * @param int $calid The calendar ID
983*65133ef9SAndreas Boehler   *
984*65133ef9SAndreas Boehler   * @return mixed The result
985*65133ef9SAndreas Boehler   */
986*65133ef9SAndreas Boehler  public function getCalendarObjects($calid)
987*65133ef9SAndreas Boehler  {
988*65133ef9SAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?";
989*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid);
990*65133ef9SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
991*65133ef9SAndreas Boehler      return $arr;
992*65133ef9SAndreas Boehler  }
993*65133ef9SAndreas Boehler
994*65133ef9SAndreas Boehler  /**
995*65133ef9SAndreas Boehler   * Retrieve a single calendar object by calendar ID and URI
996*65133ef9SAndreas Boehler   *
997*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
998*65133ef9SAndreas Boehler   * @param string $uri The object's URI
999*65133ef9SAndreas Boehler   *
1000*65133ef9SAndreas Boehler   * @return mixed The result
1001*65133ef9SAndreas Boehler   */
1002*65133ef9SAndreas Boehler  public function getCalendarObjectByUri($calid, $uri)
1003*65133ef9SAndreas Boehler  {
1004*65133ef9SAndreas Boehler      $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1005*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1006*65133ef9SAndreas Boehler      $row = $this->sqlite->res2row($res);
1007*65133ef9SAndreas Boehler      return $row;
1008*65133ef9SAndreas Boehler  }
1009*65133ef9SAndreas Boehler
1010*65133ef9SAndreas Boehler  /**
1011*65133ef9SAndreas Boehler   * Retrieve several calendar objects by specifying an array of URIs.
1012*65133ef9SAndreas Boehler   * This is mainly neede for sync.
1013*65133ef9SAndreas Boehler   *
1014*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
1015*65133ef9SAndreas Boehler   * @param array $uris An array of URIs
1016*65133ef9SAndreas Boehler   *
1017*65133ef9SAndreas Boehler   * @return mixed The result
1018*65133ef9SAndreas Boehler   */
1019*65133ef9SAndreas Boehler  public function getMultipleCalendarObjectsByUri($calid, $uris)
1020*65133ef9SAndreas Boehler  {
1021*65133ef9SAndreas Boehler        $query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN (";
1022*65133ef9SAndreas Boehler        // Inserting a whole bunch of question marks
1023*65133ef9SAndreas Boehler        $query .= implode(',', array_fill(0, count($uris), '?'));
1024*65133ef9SAndreas Boehler        $query .= ')';
1025*65133ef9SAndreas Boehler        $vals = array_merge(array($calid), $uris);
1026*65133ef9SAndreas Boehler
1027*65133ef9SAndreas Boehler        $res = $this->sqlite->query($query, $vals);
1028*65133ef9SAndreas Boehler        $arr = $this->sqlite->res2arr($res);
1029*65133ef9SAndreas Boehler        return $arr;
1030*65133ef9SAndreas Boehler  }
1031*65133ef9SAndreas Boehler
1032*65133ef9SAndreas Boehler  /**
1033cb71a62aSAndreas Boehler   * Retrieve all calendar events for a given calendar ID
1034cb71a62aSAndreas Boehler   *
1035cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1036cb71a62aSAndreas Boehler   *
1037cb71a62aSAndreas Boehler   * @return array An array containing all calendar data
1038cb71a62aSAndreas Boehler   */
1039f69bb449SAndreas Boehler  public function getAllCalendarEvents($calid)
1040f69bb449SAndreas Boehler  {
10417e0b8590SAndreas Boehler      $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?";
104251f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1043f69bb449SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
1044f69bb449SAndreas Boehler      return $arr;
1045f69bb449SAndreas Boehler  }
1046f69bb449SAndreas Boehler
1047cb71a62aSAndreas Boehler  /**
1048cb71a62aSAndreas Boehler   * Edit a calendar entry for a page, given by its parameters.
1049cb71a62aSAndreas Boehler   * The params array has the same format as @see addCalendarEntryForPage
1050cb71a62aSAndreas Boehler   *
1051cb71a62aSAndreas Boehler   * @param string $id The page's ID to work on
1052cb71a62aSAndreas Boehler   * @param string $user The user's ID to work on
1053cb71a62aSAndreas Boehler   * @param array $params The parameter array for the edited calendar event
1054cb71a62aSAndreas Boehler   *
1055cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1056cb71a62aSAndreas Boehler   */
1057a1a3b679SAndreas Boehler  public function editCalendarEntryForPage($id, $user, $params)
1058a1a3b679SAndreas Boehler  {
105982a48dfbSAndreas Boehler      if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
106082a48dfbSAndreas Boehler          $timezone = new \DateTimeZone($params['currenttz']);
106182a48dfbSAndreas Boehler      elseif($params['currenttz'] === 'local')
1062a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
1063bd883736SAndreas Boehler      else
1064bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
1065cb71a62aSAndreas Boehler
1066cb71a62aSAndreas Boehler      // Parse dates
1067b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
1068b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
1069b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
1070b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
1071cb71a62aSAndreas Boehler
1072cb71a62aSAndreas Boehler      // Retrieve the existing event based on the UID
107355a741c0SAndreas Boehler      $uid = $params['uid'];
1074809cb0faSAndreas Boehler
1075809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1076809cb0faSAndreas Boehler      {
1077809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1078809cb0faSAndreas Boehler        if(is_null($wdc))
1079809cb0faSAndreas Boehler          return false;
1080809cb0faSAndreas Boehler        $event = $wdc->getCalendarEntryByUid($uid);
1081809cb0faSAndreas Boehler      }
1082809cb0faSAndreas Boehler      else
1083809cb0faSAndreas Boehler      {
108455a741c0SAndreas Boehler        $event = $this->getEventWithUid($uid);
1085809cb0faSAndreas Boehler      }
1086cb71a62aSAndreas Boehler
1087cb71a62aSAndreas Boehler      // Load SabreDAV
10889bef4ad8SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1089a1a3b679SAndreas Boehler      if(!isset($event['calendardata']))
1090a1a3b679SAndreas Boehler        return false;
109155a741c0SAndreas Boehler      $uri = $event['uri'];
109255a741c0SAndreas Boehler      $calid = $event['calendarid'];
1093cb71a62aSAndreas Boehler
1094cb71a62aSAndreas Boehler      // Parse the existing event
1095a1a3b679SAndreas Boehler      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
1096b269830cSAndreas Boehler      $vevent = $vcal->VEVENT;
1097cb71a62aSAndreas Boehler
1098cb71a62aSAndreas Boehler      // Set the new event values
1099b269830cSAndreas Boehler      $vevent->summary = $params['eventname'];
1100b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
11010eebc909SAndreas Boehler      $description = $params['eventdescription'];
1102cb71a62aSAndreas Boehler
1103cb71a62aSAndreas Boehler      // Remove existing timestamps to overwrite them
11040eebc909SAndreas Boehler      $vevent->remove('DESCRIPTION');
1105b269830cSAndreas Boehler      $vevent->remove('DTSTAMP');
1106b269830cSAndreas Boehler      $vevent->remove('LAST-MODIFIED');
11074ecb526cSAndreas Boehler      $vevent->remove('ATTACH');
1108cb71a62aSAndreas Boehler
1109cb71a62aSAndreas Boehler      // Add new time stamps and description
1110b269830cSAndreas Boehler      $vevent->add('DTSTAMP', $dtStamp);
1111b269830cSAndreas Boehler      $vevent->add('LAST-MODIFIED', $dtStamp);
11120eebc909SAndreas Boehler      if($description !== '')
11130eebc909SAndreas Boehler        $vevent->add('DESCRIPTION', $description);
1114cb71a62aSAndreas Boehler
11154ecb526cSAndreas Boehler      // Add attachments
11164ecb526cSAndreas Boehler      $attachments = $params['attachments'];
111782a48dfbSAndreas Boehler      if(!is_null($attachments))
11184ecb526cSAndreas Boehler        foreach($attachments as $attachment)
11194ecb526cSAndreas Boehler          $vevent->add('ATTACH', $attachment);
11204ecb526cSAndreas Boehler
1121cb71a62aSAndreas Boehler      // Setup DTSTART
1122b269830cSAndreas Boehler      $dtStart = new \DateTime();
1123a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
1124b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
1125b269830cSAndreas Boehler      if($params['allday'] != '1')
1126b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
1127cb71a62aSAndreas Boehler
11284ecb526cSAndreas Boehler      // Setup DTEND
1129b269830cSAndreas Boehler      $dtEnd = new \DateTime();
1130a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
1131b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
1132b269830cSAndreas Boehler      if($params['allday'] != '1')
1133b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
1134cb71a62aSAndreas Boehler
1135b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
1136b269830cSAndreas Boehler      if($params['allday'] == '1')
1137b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
1138b269830cSAndreas Boehler      $vevent->remove('DTSTART');
1139b269830cSAndreas Boehler      $vevent->remove('DTEND');
1140b269830cSAndreas Boehler      $dtStartEv = $vevent->add('DTSTART', $dtStart);
1141b269830cSAndreas Boehler      $dtEndEv = $vevent->add('DTEND', $dtEnd);
1142cb71a62aSAndreas Boehler
1143cb71a62aSAndreas Boehler      // Remove the time for allday events
1144b269830cSAndreas Boehler      if($params['allday'] == '1')
1145b269830cSAndreas Boehler      {
1146b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
1147b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
1148b269830cSAndreas Boehler      }
1149a1a3b679SAndreas Boehler      $eventStr = $vcal->serialize();
1150809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1151809cb0faSAndreas Boehler      {
1152809cb0faSAndreas Boehler          $connectionId = str_replace('webdav://', '', $id);
1153809cb0faSAndreas Boehler          return $wdc->editCalendarEntry($connectionId, $uid, $eventStr);
1154809cb0faSAndreas Boehler      }
1155809cb0faSAndreas Boehler      else
1156809cb0faSAndreas Boehler      {
1157809cb0faSAndreas Boehler          $now = new DateTime();
1158cb71a62aSAndreas Boehler          // Actually write to the database
115951f4febbSAndreas Boehler          $query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ".
116051f4febbSAndreas Boehler                   "firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?";
116151f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(),
116251f4febbSAndreas Boehler                                      $dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid);
116355a741c0SAndreas Boehler          if($res !== false)
116455a741c0SAndreas Boehler          {
116555a741c0SAndreas Boehler              $this->updateSyncTokenLog($calid, $uri, 'modified');
1166a1a3b679SAndreas Boehler              return true;
1167a1a3b679SAndreas Boehler          }
1168809cb0faSAndreas Boehler      }
116955a741c0SAndreas Boehler      return false;
117055a741c0SAndreas Boehler  }
1171a1a3b679SAndreas Boehler
1172cb71a62aSAndreas Boehler  /**
1173*65133ef9SAndreas Boehler   * Delete an event from a calendar by calendar ID and URI
1174*65133ef9SAndreas Boehler   *
1175*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
1176*65133ef9SAndreas Boehler   * @param string $uri The object's URI
1177*65133ef9SAndreas Boehler   *
1178*65133ef9SAndreas Boehler   * @return true
1179*65133ef9SAndreas Boehler   */
1180*65133ef9SAndreas Boehler  public function deleteCalendarEntryForCalendarByUri($calid, $uri)
1181*65133ef9SAndreas Boehler  {
1182*65133ef9SAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?";
1183*65133ef9SAndreas Boehler      $res = $this->sqlite->query($query, $calid, $uri);
1184*65133ef9SAndreas Boehler      if($res !== false)
1185*65133ef9SAndreas Boehler      {
1186*65133ef9SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
1187*65133ef9SAndreas Boehler      }
1188*65133ef9SAndreas Boehler      return true;
1189*65133ef9SAndreas Boehler  }
1190*65133ef9SAndreas Boehler
1191*65133ef9SAndreas Boehler  /**
1192cb71a62aSAndreas Boehler   * Delete a calendar entry for a given page. Actually, the event is removed
1193cb71a62aSAndreas Boehler   * based on the entry's UID, so that page ID is no used.
1194cb71a62aSAndreas Boehler   *
1195cb71a62aSAndreas Boehler   * @param string $id The page's ID (unused)
1196cb71a62aSAndreas Boehler   * @param array $params The parameter array to work with
1197cb71a62aSAndreas Boehler   *
1198cb71a62aSAndreas Boehler   * @return boolean True
1199cb71a62aSAndreas Boehler   */
1200a1a3b679SAndreas Boehler  public function deleteCalendarEntryForPage($id, $params)
1201a1a3b679SAndreas Boehler  {
1202a1a3b679SAndreas Boehler      $uid = $params['uid'];
1203809cb0faSAndreas Boehler      if(strpos($id, 'webdav://') === 0)
1204809cb0faSAndreas Boehler      {
1205809cb0faSAndreas Boehler        $wdc =& plugin_load('helper', 'webdavclient');
1206809cb0faSAndreas Boehler        if(is_null($wdc))
1207809cb0faSAndreas Boehler          return false;
1208809cb0faSAndreas Boehler        $connectionId = str_replace('webdav://', '', $id);
1209809cb0faSAndreas Boehler        $result = $wdc->deleteCalendarEntry($connectionId, $uid);
1210809cb0faSAndreas Boehler        return $result;
1211809cb0faSAndreas Boehler      }
121255a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
12132c14b82bSAndreas Boehler      $calid = $event['calendarid'];
121455a741c0SAndreas Boehler      $uri = $event['uri'];
121551f4febbSAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE uid = ?";
121651f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uid);
121755a741c0SAndreas Boehler      if($res !== false)
121855a741c0SAndreas Boehler      {
121955a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
122055a741c0SAndreas Boehler      }
1221a1a3b679SAndreas Boehler      return true;
1222a1a3b679SAndreas Boehler  }
1223a1a3b679SAndreas Boehler
1224cb71a62aSAndreas Boehler  /**
1225cb71a62aSAndreas Boehler   * Retrieve the current sync token for a calendar
1226cb71a62aSAndreas Boehler   *
1227cb71a62aSAndreas Boehler   * @param string $calid The calendar id
1228cb71a62aSAndreas Boehler   *
1229cb71a62aSAndreas Boehler   * @return mixed The synctoken or false
1230cb71a62aSAndreas Boehler   */
123155a741c0SAndreas Boehler  public function getSyncTokenForCalendar($calid)
123255a741c0SAndreas Boehler  {
1233b269830cSAndreas Boehler      $row = $this->getCalendarSettings($calid);
123455a741c0SAndreas Boehler      if(isset($row['synctoken']))
123555a741c0SAndreas Boehler          return $row['synctoken'];
123655a741c0SAndreas Boehler      return false;
123755a741c0SAndreas Boehler  }
123855a741c0SAndreas Boehler
1239cb71a62aSAndreas Boehler  /**
1240cb71a62aSAndreas Boehler   * Helper function to convert the operation name to
1241cb71a62aSAndreas Boehler   * an operation code as stored in the database
1242cb71a62aSAndreas Boehler   *
1243cb71a62aSAndreas Boehler   * @param string $operationName The operation name
1244cb71a62aSAndreas Boehler   *
1245cb71a62aSAndreas Boehler   * @return mixed The operation code or false
1246cb71a62aSAndreas Boehler   */
124755a741c0SAndreas Boehler  public function operationNameToOperation($operationName)
124855a741c0SAndreas Boehler  {
124955a741c0SAndreas Boehler      switch($operationName)
125055a741c0SAndreas Boehler      {
125155a741c0SAndreas Boehler          case 'added':
125255a741c0SAndreas Boehler              return 1;
125355a741c0SAndreas Boehler          break;
125455a741c0SAndreas Boehler          case 'modified':
125555a741c0SAndreas Boehler              return 2;
125655a741c0SAndreas Boehler          break;
125755a741c0SAndreas Boehler          case 'deleted':
125855a741c0SAndreas Boehler              return 3;
125955a741c0SAndreas Boehler          break;
126055a741c0SAndreas Boehler      }
126155a741c0SAndreas Boehler      return false;
126255a741c0SAndreas Boehler  }
126355a741c0SAndreas Boehler
1264cb71a62aSAndreas Boehler  /**
1265cb71a62aSAndreas Boehler   * Update the sync token log based on the calendar id and the
1266cb71a62aSAndreas Boehler   * operation that was performed.
1267cb71a62aSAndreas Boehler   *
1268cb71a62aSAndreas Boehler   * @param string $calid The calendar ID that was modified
1269cb71a62aSAndreas Boehler   * @param string $uri The calendar URI that was modified
1270cb71a62aSAndreas Boehler   * @param string $operation The operation that was performed
1271cb71a62aSAndreas Boehler   *
1272cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
1273cb71a62aSAndreas Boehler   */
127455a741c0SAndreas Boehler  private function updateSyncTokenLog($calid, $uri, $operation)
127555a741c0SAndreas Boehler  {
127655a741c0SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
127755a741c0SAndreas Boehler      $operationCode = $this->operationNameToOperation($operation);
127855a741c0SAndreas Boehler      if(($operationCode === false) || ($currentToken === false))
127955a741c0SAndreas Boehler          return false;
128055a741c0SAndreas Boehler      $values = array($uri,
128155a741c0SAndreas Boehler                      $currentToken,
128255a741c0SAndreas Boehler                      $calid,
128355a741c0SAndreas Boehler                      $operationCode
128455a741c0SAndreas Boehler      );
128551f4febbSAndreas Boehler      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)";
128651f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $uri, $currentToken, $calid, $operationCode);
128755a741c0SAndreas Boehler      if($res === false)
128855a741c0SAndreas Boehler        return false;
128955a741c0SAndreas Boehler      $currentToken++;
129051f4febbSAndreas Boehler      $query = "UPDATE calendars SET synctoken = ? WHERE id = ?";
129151f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $currentToken, $calid);
129255a741c0SAndreas Boehler      return ($res !== false);
129355a741c0SAndreas Boehler  }
129455a741c0SAndreas Boehler
1295cb71a62aSAndreas Boehler  /**
1296cb71a62aSAndreas Boehler   * Return the sync URL for a given Page, i.e. a calendar
1297cb71a62aSAndreas Boehler   *
1298cb71a62aSAndreas Boehler   * @param string $id The page's ID
1299cb71a62aSAndreas Boehler   * @param string $user (optional) The user's ID
1300cb71a62aSAndreas Boehler   *
1301cb71a62aSAndreas Boehler   * @return mixed The sync url or false
1302cb71a62aSAndreas Boehler   */
1303b269830cSAndreas Boehler  public function getSyncUrlForPage($id, $user = null)
1304b269830cSAndreas Boehler  {
130534a47953SAndreas Boehler      if(is_null($userid))
130634a47953SAndreas Boehler      {
130734a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
130834a47953SAndreas Boehler        {
130934a47953SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
131034a47953SAndreas Boehler        }
131134a47953SAndreas Boehler        else
131234a47953SAndreas Boehler        {
131334a47953SAndreas Boehler          return false;
131434a47953SAndreas Boehler        }
131534a47953SAndreas Boehler      }
1316b269830cSAndreas Boehler
1317b269830cSAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1318b269830cSAndreas Boehler      if($calid === false)
1319b269830cSAndreas Boehler        return false;
1320b269830cSAndreas Boehler
1321b269830cSAndreas Boehler      $calsettings = $this->getCalendarSettings($calid);
1322b269830cSAndreas Boehler      if(!isset($calsettings['uri']))
1323b269830cSAndreas Boehler        return false;
1324b269830cSAndreas Boehler
1325b269830cSAndreas Boehler      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
1326b269830cSAndreas Boehler      return $syncurl;
1327b269830cSAndreas Boehler  }
1328b269830cSAndreas Boehler
1329cb71a62aSAndreas Boehler  /**
1330cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given page
1331cb71a62aSAndreas Boehler   *
1332cb71a62aSAndreas Boehler   * @param string $id the page ID
1333cb71a62aSAndreas Boehler   *
1334cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1335cb71a62aSAndreas Boehler   */
1336f69bb449SAndreas Boehler  public function getPrivateURLForPage($id)
1337f69bb449SAndreas Boehler  {
1338f69bb449SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1339f69bb449SAndreas Boehler      if($calid === false)
1340f69bb449SAndreas Boehler        return false;
1341f69bb449SAndreas Boehler
1342f69bb449SAndreas Boehler      return $this->getPrivateURLForCalendar($calid);
1343f69bb449SAndreas Boehler  }
1344f69bb449SAndreas Boehler
1345cb71a62aSAndreas Boehler  /**
1346cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given calendar ID
1347cb71a62aSAndreas Boehler   *
1348cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1349cb71a62aSAndreas Boehler   *
1350cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1351cb71a62aSAndreas Boehler   */
1352f69bb449SAndreas Boehler  public function getPrivateURLForCalendar($calid)
1353f69bb449SAndreas Boehler  {
1354185e2535SAndreas Boehler      if(isset($this->cachedValues['privateurl'][$calid]))
1355185e2535SAndreas Boehler        return $this->cachedValues['privateurl'][$calid];
135651f4febbSAndreas Boehler      $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?";
135751f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $calid);
1358f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1359f69bb449SAndreas Boehler      if(!isset($row['url']))
1360f69bb449SAndreas Boehler      {
1361f69bb449SAndreas Boehler          $url = uniqid("dokuwiki-").".ics";
136251f4febbSAndreas Boehler          $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)";
136351f4febbSAndreas Boehler          $res = $this->sqlite->query($query, $url, $calid);
1364f69bb449SAndreas Boehler          if($res === false)
1365f69bb449SAndreas Boehler            return false;
1366f69bb449SAndreas Boehler      }
1367f69bb449SAndreas Boehler      else
1368f69bb449SAndreas Boehler      {
1369f69bb449SAndreas Boehler          $url = $row['url'];
1370f69bb449SAndreas Boehler      }
1371185e2535SAndreas Boehler
1372185e2535SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url;
1373185e2535SAndreas Boehler      $this->cachedValues['privateurl'][$calid] = $url;
1374185e2535SAndreas Boehler      return $url;
1375f69bb449SAndreas Boehler  }
1376f69bb449SAndreas Boehler
1377cb71a62aSAndreas Boehler  /**
1378cb71a62aSAndreas Boehler   * Retrieve the calendar ID for a given private calendar URL
1379cb71a62aSAndreas Boehler   *
1380cb71a62aSAndreas Boehler   * @param string $url The private URL
1381cb71a62aSAndreas Boehler   *
1382cb71a62aSAndreas Boehler   * @return mixed The calendar ID or false
1383cb71a62aSAndreas Boehler   */
1384f69bb449SAndreas Boehler  public function getCalendarForPrivateURL($url)
1385f69bb449SAndreas Boehler  {
138651f4febbSAndreas Boehler      $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?";
138751f4febbSAndreas Boehler      $res = $this->sqlite->query($query, $url);
1388f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1389f69bb449SAndreas Boehler      if(!isset($row['calid']))
1390f69bb449SAndreas Boehler        return false;
1391f69bb449SAndreas Boehler      return $row['calid'];
1392f69bb449SAndreas Boehler  }
1393f69bb449SAndreas Boehler
1394cb71a62aSAndreas Boehler  /**
1395cb71a62aSAndreas Boehler   * Return a given calendar as ICS feed, i.e. all events in one ICS file.
1396cb71a62aSAndreas Boehler   *
13977e0b8590SAndreas Boehler   * @param string $calid The calendar ID to retrieve
1398cb71a62aSAndreas Boehler   *
1399cb71a62aSAndreas Boehler   * @return mixed The calendar events as string or false
1400cb71a62aSAndreas Boehler   */
1401f69bb449SAndreas Boehler  public function getCalendarAsICSFeed($calid)
1402f69bb449SAndreas Boehler  {
1403f69bb449SAndreas Boehler      $calSettings = $this->getCalendarSettings($calid);
1404f69bb449SAndreas Boehler      if($calSettings === false)
1405f69bb449SAndreas Boehler        return false;
1406f69bb449SAndreas Boehler      $events = $this->getAllCalendarEvents($calid);
1407f69bb449SAndreas Boehler      if($events === false)
1408f69bb449SAndreas Boehler        return false;
1409f69bb449SAndreas Boehler
14107e0b8590SAndreas Boehler      // Load SabreDAV
14117e0b8590SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
14127e0b8590SAndreas Boehler      $out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:";
14137e0b8590SAndreas Boehler      $out .= $calSettings['displayname']."\r\n";
1414f69bb449SAndreas Boehler      foreach($events as $event)
1415f69bb449SAndreas Boehler      {
14167e0b8590SAndreas Boehler          $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
14177e0b8590SAndreas Boehler          $evt = $vcal->VEVENT;
14187e0b8590SAndreas Boehler          $out .= $evt->serialize();
1419f69bb449SAndreas Boehler      }
14207e0b8590SAndreas Boehler      $out .= "END:VCALENDAR\r\n";
1421f69bb449SAndreas Boehler      return $out;
1422f69bb449SAndreas Boehler  }
1423f69bb449SAndreas Boehler
14247c7c6b0bSAndreas Boehler  /**
14257c7c6b0bSAndreas Boehler   * Retrieve a configuration option for the plugin
14267c7c6b0bSAndreas Boehler   *
14277c7c6b0bSAndreas Boehler   * @param string $key The key to query
142821d04f73SAndreas Boehler   * @return mixed The option set, null if not found
14297c7c6b0bSAndreas Boehler   */
14307c7c6b0bSAndreas Boehler  public function getConfig($key)
14317c7c6b0bSAndreas Boehler  {
14327c7c6b0bSAndreas Boehler      return $this->getConf($key);
14337c7c6b0bSAndreas Boehler  }
14347c7c6b0bSAndreas Boehler
1435*65133ef9SAndreas Boehler  /**
1436*65133ef9SAndreas Boehler   * Parses some information from calendar objects, used for optimized
1437*65133ef9SAndreas Boehler   * calendar-queries. Taken nearly unmodified from Sabre's PDO backend
1438*65133ef9SAndreas Boehler   *
1439*65133ef9SAndreas Boehler   * Returns an array with the following keys:
1440*65133ef9SAndreas Boehler   *   * etag - An md5 checksum of the object without the quotes.
1441*65133ef9SAndreas Boehler   *   * size - Size of the object in bytes
1442*65133ef9SAndreas Boehler   *   * componentType - VEVENT, VTODO or VJOURNAL
1443*65133ef9SAndreas Boehler   *   * firstOccurence
1444*65133ef9SAndreas Boehler   *   * lastOccurence
1445*65133ef9SAndreas Boehler   *   * uid - value of the UID property
1446*65133ef9SAndreas Boehler   *
1447*65133ef9SAndreas Boehler   * @param string $calendarData
1448*65133ef9SAndreas Boehler   * @return array
1449*65133ef9SAndreas Boehler   */
1450*65133ef9SAndreas Boehler  protected function getDenormalizedData($calendarData)
1451*65133ef9SAndreas Boehler  {
1452*65133ef9SAndreas Boehler    require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1453*65133ef9SAndreas Boehler
1454*65133ef9SAndreas Boehler    $vObject = \Sabre\VObject\Reader::read($calendarData);
1455*65133ef9SAndreas Boehler    $componentType = null;
1456*65133ef9SAndreas Boehler    $component = null;
1457*65133ef9SAndreas Boehler    $firstOccurence = null;
1458*65133ef9SAndreas Boehler    $lastOccurence = null;
1459*65133ef9SAndreas Boehler    $uid = null;
1460*65133ef9SAndreas Boehler    foreach ($vObject->getComponents() as $component)
1461*65133ef9SAndreas Boehler    {
1462*65133ef9SAndreas Boehler        if ($component->name !== 'VTIMEZONE')
1463*65133ef9SAndreas Boehler        {
1464*65133ef9SAndreas Boehler            $componentType = $component->name;
1465*65133ef9SAndreas Boehler            $uid = (string)$component->UID;
1466*65133ef9SAndreas Boehler            break;
1467*65133ef9SAndreas Boehler        }
1468*65133ef9SAndreas Boehler    }
1469*65133ef9SAndreas Boehler    if (!$componentType)
1470*65133ef9SAndreas Boehler    {
1471*65133ef9SAndreas Boehler        return false;
1472*65133ef9SAndreas Boehler    }
1473*65133ef9SAndreas Boehler    if ($componentType === 'VEVENT')
1474*65133ef9SAndreas Boehler    {
1475*65133ef9SAndreas Boehler        $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
1476*65133ef9SAndreas Boehler        // Finding the last occurence is a bit harder
1477*65133ef9SAndreas Boehler        if (!isset($component->RRULE))
1478*65133ef9SAndreas Boehler        {
1479*65133ef9SAndreas Boehler            if (isset($component->DTEND))
1480*65133ef9SAndreas Boehler            {
1481*65133ef9SAndreas Boehler                $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
1482*65133ef9SAndreas Boehler            }
1483*65133ef9SAndreas Boehler            elseif (isset($component->DURATION))
1484*65133ef9SAndreas Boehler            {
1485*65133ef9SAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1486*65133ef9SAndreas Boehler                $endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
1487*65133ef9SAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1488*65133ef9SAndreas Boehler            }
1489*65133ef9SAndreas Boehler            elseif (!$component->DTSTART->hasTime())
1490*65133ef9SAndreas Boehler            {
1491*65133ef9SAndreas Boehler                $endDate = clone $component->DTSTART->getDateTime();
1492*65133ef9SAndreas Boehler                $endDate->modify('+1 day');
1493*65133ef9SAndreas Boehler                $lastOccurence = $endDate->getTimeStamp();
1494*65133ef9SAndreas Boehler            }
1495*65133ef9SAndreas Boehler            else
1496*65133ef9SAndreas Boehler            {
1497*65133ef9SAndreas Boehler                $lastOccurence = $firstOccurence;
1498*65133ef9SAndreas Boehler            }
1499*65133ef9SAndreas Boehler        }
1500*65133ef9SAndreas Boehler        else
1501*65133ef9SAndreas Boehler        {
1502*65133ef9SAndreas Boehler            $it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
1503*65133ef9SAndreas Boehler            $maxDate = new \DateTime('2038-01-01');
1504*65133ef9SAndreas Boehler            if ($it->isInfinite())
1505*65133ef9SAndreas Boehler            {
1506*65133ef9SAndreas Boehler                $lastOccurence = $maxDate->getTimeStamp();
1507*65133ef9SAndreas Boehler            }
1508*65133ef9SAndreas Boehler            else
1509*65133ef9SAndreas Boehler            {
1510*65133ef9SAndreas Boehler                $end = $it->getDtEnd();
1511*65133ef9SAndreas Boehler                while ($it->valid() && $end < $maxDate)
1512*65133ef9SAndreas Boehler                {
1513*65133ef9SAndreas Boehler                    $end = $it->getDtEnd();
1514*65133ef9SAndreas Boehler                    $it->next();
1515*65133ef9SAndreas Boehler                }
1516*65133ef9SAndreas Boehler                $lastOccurence = $end->getTimeStamp();
1517*65133ef9SAndreas Boehler            }
1518*65133ef9SAndreas Boehler        }
1519*65133ef9SAndreas Boehler    }
1520*65133ef9SAndreas Boehler
1521*65133ef9SAndreas Boehler    return array(
1522*65133ef9SAndreas Boehler        'etag'           => md5($calendarData),
1523*65133ef9SAndreas Boehler        'size'           => strlen($calendarData),
1524*65133ef9SAndreas Boehler        'componentType'  => $componentType,
1525*65133ef9SAndreas Boehler        'firstOccurence' => $firstOccurence,
1526*65133ef9SAndreas Boehler        'lastOccurence'  => $lastOccurence,
1527*65133ef9SAndreas Boehler        'uid'            => $uid,
1528*65133ef9SAndreas Boehler    );
1529*65133ef9SAndreas Boehler
1530*65133ef9SAndreas Boehler  }
1531*65133ef9SAndreas Boehler
1532*65133ef9SAndreas Boehler  /**
1533*65133ef9SAndreas Boehler   * Query a calendar by ID and taking several filters into account.
1534*65133ef9SAndreas Boehler   * This is heavily based on Sabre's PDO backend.
1535*65133ef9SAndreas Boehler   *
1536*65133ef9SAndreas Boehler   * @param int $calendarId The calendar's ID
1537*65133ef9SAndreas Boehler   * @param array $filters The filter array to apply
1538*65133ef9SAndreas Boehler   *
1539*65133ef9SAndreas Boehler   * @return mixed The result
1540*65133ef9SAndreas Boehler   */
1541*65133ef9SAndreas Boehler  public function calendarQuery($calendarId, $filters)
1542*65133ef9SAndreas Boehler  {
1543*65133ef9SAndreas Boehler    $componentType = null;
1544*65133ef9SAndreas Boehler    $requirePostFilter = true;
1545*65133ef9SAndreas Boehler    $timeRange = null;
1546*65133ef9SAndreas Boehler
1547*65133ef9SAndreas Boehler    // if no filters were specified, we don't need to filter after a query
1548*65133ef9SAndreas Boehler    if (!$filters['prop-filters'] && !$filters['comp-filters'])
1549*65133ef9SAndreas Boehler    {
1550*65133ef9SAndreas Boehler        $requirePostFilter = false;
1551*65133ef9SAndreas Boehler    }
1552*65133ef9SAndreas Boehler
1553*65133ef9SAndreas Boehler    // Figuring out if there's a component filter
1554*65133ef9SAndreas Boehler    if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined'])
1555*65133ef9SAndreas Boehler    {
1556*65133ef9SAndreas Boehler        $componentType = $filters['comp-filters'][0]['name'];
1557*65133ef9SAndreas Boehler
1558*65133ef9SAndreas Boehler        // Checking if we need post-filters
1559*65133ef9SAndreas Boehler        if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters'])
1560*65133ef9SAndreas Boehler        {
1561*65133ef9SAndreas Boehler            $requirePostFilter = false;
1562*65133ef9SAndreas Boehler        }
1563*65133ef9SAndreas Boehler        // There was a time-range filter
1564*65133ef9SAndreas Boehler        if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range']))
1565*65133ef9SAndreas Boehler        {
1566*65133ef9SAndreas Boehler            $timeRange = $filters['comp-filters'][0]['time-range'];
1567*65133ef9SAndreas Boehler
1568*65133ef9SAndreas Boehler            // If start time OR the end time is not specified, we can do a
1569*65133ef9SAndreas Boehler            // 100% accurate mysql query.
1570*65133ef9SAndreas Boehler            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end']))
1571*65133ef9SAndreas Boehler            {
1572*65133ef9SAndreas Boehler                $requirePostFilter = false;
1573*65133ef9SAndreas Boehler            }
1574*65133ef9SAndreas Boehler        }
1575*65133ef9SAndreas Boehler
1576*65133ef9SAndreas Boehler    }
1577*65133ef9SAndreas Boehler
1578*65133ef9SAndreas Boehler    if ($requirePostFilter)
1579*65133ef9SAndreas Boehler    {
1580*65133ef9SAndreas Boehler        $query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?";
1581*65133ef9SAndreas Boehler    }
1582*65133ef9SAndreas Boehler    else
1583*65133ef9SAndreas Boehler    {
1584*65133ef9SAndreas Boehler        $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1585*65133ef9SAndreas Boehler    }
1586*65133ef9SAndreas Boehler
1587*65133ef9SAndreas Boehler    $values = array(
1588*65133ef9SAndreas Boehler        $calendarId
1589*65133ef9SAndreas Boehler    );
1590*65133ef9SAndreas Boehler
1591*65133ef9SAndreas Boehler    if ($componentType)
1592*65133ef9SAndreas Boehler    {
1593*65133ef9SAndreas Boehler        $query .= " AND componenttype = ?";
1594*65133ef9SAndreas Boehler        $values[] = $componentType;
1595*65133ef9SAndreas Boehler    }
1596*65133ef9SAndreas Boehler
1597*65133ef9SAndreas Boehler    if ($timeRange && $timeRange['start'])
1598*65133ef9SAndreas Boehler    {
1599*65133ef9SAndreas Boehler        $query .= " AND lastoccurence > ?";
1600*65133ef9SAndreas Boehler        $values[] = $timeRange['start']->getTimeStamp();
1601*65133ef9SAndreas Boehler    }
1602*65133ef9SAndreas Boehler    if ($timeRange && $timeRange['end'])
1603*65133ef9SAndreas Boehler    {
1604*65133ef9SAndreas Boehler        $query .= " AND firstoccurence < ?";
1605*65133ef9SAndreas Boehler        $values[] = $timeRange['end']->getTimeStamp();
1606*65133ef9SAndreas Boehler    }
1607*65133ef9SAndreas Boehler
1608*65133ef9SAndreas Boehler    $res = $this->sqlite->query($query, $values);
1609*65133ef9SAndreas Boehler    $arr = $this->sqlite->res2arr($res);
1610*65133ef9SAndreas Boehler
1611*65133ef9SAndreas Boehler    $result = array();
1612*65133ef9SAndreas Boehler    foreach($arr as $row)
1613*65133ef9SAndreas Boehler    {
1614*65133ef9SAndreas Boehler        if ($requirePostFilter)
1615*65133ef9SAndreas Boehler        {
1616*65133ef9SAndreas Boehler            if (!$this->validateFilterForObject($row, $filters))
1617*65133ef9SAndreas Boehler            {
1618*65133ef9SAndreas Boehler                continue;
1619*65133ef9SAndreas Boehler            }
1620*65133ef9SAndreas Boehler        }
1621*65133ef9SAndreas Boehler        $result[] = $row['uri'];
1622*65133ef9SAndreas Boehler
1623*65133ef9SAndreas Boehler    }
1624*65133ef9SAndreas Boehler
1625*65133ef9SAndreas Boehler    return $result;
1626*65133ef9SAndreas Boehler  }
1627*65133ef9SAndreas Boehler
1628*65133ef9SAndreas Boehler  /**
1629*65133ef9SAndreas Boehler   * This method validates if a filter (as passed to calendarQuery) matches
1630*65133ef9SAndreas Boehler   * the given object. Taken from Sabre's PDO backend
1631*65133ef9SAndreas Boehler   *
1632*65133ef9SAndreas Boehler   * @param array $object
1633*65133ef9SAndreas Boehler   * @param array $filters
1634*65133ef9SAndreas Boehler   * @return bool
1635*65133ef9SAndreas Boehler   */
1636*65133ef9SAndreas Boehler  protected function validateFilterForObject($object, $filters)
1637*65133ef9SAndreas Boehler  {
1638*65133ef9SAndreas Boehler      require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
1639*65133ef9SAndreas Boehler      // Unfortunately, setting the 'calendardata' here is optional. If
1640*65133ef9SAndreas Boehler      // it was excluded, we actually need another call to get this as
1641*65133ef9SAndreas Boehler      // well.
1642*65133ef9SAndreas Boehler      if (!isset($object['calendardata']))
1643*65133ef9SAndreas Boehler      {
1644*65133ef9SAndreas Boehler          $object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']);
1645*65133ef9SAndreas Boehler      }
1646*65133ef9SAndreas Boehler
1647*65133ef9SAndreas Boehler      $vObject = \Sabre\VObject\Reader::read($object['calendardata']);
1648*65133ef9SAndreas Boehler      $validator = new \Sabre\CalDAV\CalendarQueryValidator();
1649*65133ef9SAndreas Boehler
1650*65133ef9SAndreas Boehler      return $validator->validate($vObject, $filters);
1651*65133ef9SAndreas Boehler
1652*65133ef9SAndreas Boehler  }
1653*65133ef9SAndreas Boehler
1654*65133ef9SAndreas Boehler  /**
1655*65133ef9SAndreas Boehler   * Retrieve changes for a given calendar based on the given syncToken.
1656*65133ef9SAndreas Boehler   *
1657*65133ef9SAndreas Boehler   * @param int $calid The calendar's ID
1658*65133ef9SAndreas Boehler   * @param int $syncToken The supplied sync token
1659*65133ef9SAndreas Boehler   * @param int $syncLevel The sync level
1660*65133ef9SAndreas Boehler   * @param int $limit The limit of changes
1661*65133ef9SAndreas Boehler   *
1662*65133ef9SAndreas Boehler   * @return array The result
1663*65133ef9SAndreas Boehler   */
1664*65133ef9SAndreas Boehler  public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null)
1665*65133ef9SAndreas Boehler  {
1666*65133ef9SAndreas Boehler      // Current synctoken
1667*65133ef9SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
1668*65133ef9SAndreas Boehler
1669*65133ef9SAndreas Boehler      if ($currentToken === false) return null;
1670*65133ef9SAndreas Boehler
1671*65133ef9SAndreas Boehler      $result = array(
1672*65133ef9SAndreas Boehler          'syncToken' => $currentToken,
1673*65133ef9SAndreas Boehler          'added'     => array(),
1674*65133ef9SAndreas Boehler          'modified'  => array(),
1675*65133ef9SAndreas Boehler          'deleted'   => array(),
1676*65133ef9SAndreas Boehler      );
1677*65133ef9SAndreas Boehler
1678*65133ef9SAndreas Boehler      if ($syncToken)
1679*65133ef9SAndreas Boehler      {
1680*65133ef9SAndreas Boehler
1681*65133ef9SAndreas Boehler          $query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
1682*65133ef9SAndreas Boehler          if ($limit > 0) $query .= " LIMIT " . (int)$limit;
1683*65133ef9SAndreas Boehler
1684*65133ef9SAndreas Boehler          // Fetching all changes
1685*65133ef9SAndreas Boehler          $res = $this->sqlite->query($query, $syncToken, $currentToken, $calid);
1686*65133ef9SAndreas Boehler          if($res === false)
1687*65133ef9SAndreas Boehler              return null;
1688*65133ef9SAndreas Boehler
1689*65133ef9SAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1690*65133ef9SAndreas Boehler          $changes = array();
1691*65133ef9SAndreas Boehler
1692*65133ef9SAndreas Boehler          // This loop ensures that any duplicates are overwritten, only the
1693*65133ef9SAndreas Boehler          // last change on a node is relevant.
1694*65133ef9SAndreas Boehler          foreach($arr as $row)
1695*65133ef9SAndreas Boehler          {
1696*65133ef9SAndreas Boehler              $changes[$row['uri']] = $row['operation'];
1697*65133ef9SAndreas Boehler          }
1698*65133ef9SAndreas Boehler
1699*65133ef9SAndreas Boehler          foreach ($changes as $uri => $operation)
1700*65133ef9SAndreas Boehler          {
1701*65133ef9SAndreas Boehler              switch ($operation)
1702*65133ef9SAndreas Boehler              {
1703*65133ef9SAndreas Boehler                  case 1 :
1704*65133ef9SAndreas Boehler                      $result['added'][] = $uri;
1705*65133ef9SAndreas Boehler                      break;
1706*65133ef9SAndreas Boehler                  case 2 :
1707*65133ef9SAndreas Boehler                      $result['modified'][] = $uri;
1708*65133ef9SAndreas Boehler                      break;
1709*65133ef9SAndreas Boehler                  case 3 :
1710*65133ef9SAndreas Boehler                      $result['deleted'][] = $uri;
1711*65133ef9SAndreas Boehler                      break;
1712*65133ef9SAndreas Boehler              }
1713*65133ef9SAndreas Boehler
1714*65133ef9SAndreas Boehler          }
1715*65133ef9SAndreas Boehler      }
1716*65133ef9SAndreas Boehler      else
1717*65133ef9SAndreas Boehler      {
1718*65133ef9SAndreas Boehler          // No synctoken supplied, this is the initial sync.
1719*65133ef9SAndreas Boehler          $query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
1720*65133ef9SAndreas Boehler          $res = $this->sqlite->query($query);
1721*65133ef9SAndreas Boehler          $arr = $this->sqlite->res2arr($res);
1722*65133ef9SAndreas Boehler
1723*65133ef9SAndreas Boehler          $result['added'] = $arr;
1724*65133ef9SAndreas Boehler      }
1725*65133ef9SAndreas Boehler      return $result;
1726*65133ef9SAndreas Boehler  }
1727*65133ef9SAndreas Boehler
1728a1a3b679SAndreas Boehler}
1729