xref: /plugin/davcal/helper.php (revision 7c7c6b0b06a11b5b23dcc73946b36f5a291336c5)
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');
21a1a3b679SAndreas Boehler    if(!$this->sqlite)
22a1a3b679SAndreas Boehler    {
23a1a3b679SAndreas Boehler        msg('This plugin requires the sqlite plugin. Please install it.');
24a1a3b679SAndreas Boehler        return;
25a1a3b679SAndreas Boehler    }
26a1a3b679SAndreas Boehler
27a1a3b679SAndreas Boehler    if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
28a1a3b679SAndreas Boehler    {
29a1a3b679SAndreas Boehler        return;
30a1a3b679SAndreas Boehler    }
31a1a3b679SAndreas Boehler  }
32a1a3b679SAndreas Boehler
33cb71a62aSAndreas Boehler  /**
34185e2535SAndreas Boehler   * Retrieve meta data for a given page
35185e2535SAndreas Boehler   *
36185e2535SAndreas Boehler   * @param string $id optional The page ID
37185e2535SAndreas Boehler   * @return array The metadata
38185e2535SAndreas Boehler   */
39185e2535SAndreas Boehler  private function getMeta($id = null) {
40185e2535SAndreas Boehler    global $ID;
41185e2535SAndreas Boehler    global $INFO;
42185e2535SAndreas Boehler
43185e2535SAndreas Boehler    if ($id === null) $id = $ID;
44185e2535SAndreas Boehler
45185e2535SAndreas Boehler    if($ID === $id && $INFO['meta']) {
46185e2535SAndreas Boehler        $meta = $INFO['meta'];
47185e2535SAndreas Boehler    } else {
48185e2535SAndreas Boehler        $meta = p_get_metadata($id);
49185e2535SAndreas Boehler    }
50185e2535SAndreas Boehler
51185e2535SAndreas Boehler    return $meta;
52185e2535SAndreas Boehler  }
53185e2535SAndreas Boehler
54185e2535SAndreas Boehler  /**
55185e2535SAndreas Boehler   * Retrieve the meta data for a given page
56185e2535SAndreas Boehler   *
57185e2535SAndreas Boehler   * @param string $id optional The page ID
58185e2535SAndreas Boehler   * @return array with meta data
59185e2535SAndreas Boehler   */
60185e2535SAndreas Boehler  public function getCalendarMetaForPage($id = null)
61185e2535SAndreas Boehler  {
62185e2535SAndreas Boehler      if(is_null($id))
63185e2535SAndreas Boehler      {
64185e2535SAndreas Boehler          global $ID;
65185e2535SAndreas Boehler          $id = $ID;
66185e2535SAndreas Boehler      }
67185e2535SAndreas Boehler
68185e2535SAndreas Boehler      $meta = $this->getMeta($id);
69185e2535SAndreas Boehler      if(isset($meta['plugin_davcal']))
70185e2535SAndreas Boehler        return $meta['plugin_davcal'];
71185e2535SAndreas Boehler      else
72185e2535SAndreas Boehler        return array();
73185e2535SAndreas Boehler  }
74185e2535SAndreas Boehler
75185e2535SAndreas Boehler  /**
76185e2535SAndreas Boehler   * Get all calendar pages used by a given page
77185e2535SAndreas Boehler   * based on the stored metadata
78185e2535SAndreas Boehler   *
79185e2535SAndreas Boehler   * @param string $id optional The page id
80185e2535SAndreas Boehler   * @return mixed The pages as array or false
81185e2535SAndreas Boehler   */
82185e2535SAndreas Boehler  public function getCalendarPagesByMeta($id = null)
83185e2535SAndreas Boehler  {
84185e2535SAndreas Boehler      if(is_null($id))
85185e2535SAndreas Boehler      {
86185e2535SAndreas Boehler          global $ID;
87185e2535SAndreas Boehler          $id = $ID;
88185e2535SAndreas Boehler      }
89185e2535SAndreas Boehler
90185e2535SAndreas Boehler      $meta = $this->getCalendarMetaForPage($id);
91185e2535SAndreas Boehler      if(isset($meta['id']))
92185e2535SAndreas Boehler      {
93185e2535SAndreas Boehler          return array_keys($meta['id']);
94185e2535SAndreas Boehler      }
95185e2535SAndreas Boehler      return false;
96185e2535SAndreas Boehler  }
97185e2535SAndreas Boehler
98185e2535SAndreas Boehler  /**
99185e2535SAndreas Boehler   * Get a list of calendar names/pages/ids/colors
100185e2535SAndreas Boehler   * for an array of page ids
101185e2535SAndreas Boehler   *
102185e2535SAndreas Boehler   * @param array $calendarPages The calendar pages to retrieve
103185e2535SAndreas Boehler   * @return array The list
104185e2535SAndreas Boehler   */
105185e2535SAndreas Boehler  public function getCalendarMapForIDs($calendarPages)
106185e2535SAndreas Boehler  {
107185e2535SAndreas Boehler      $data = array();
108185e2535SAndreas Boehler      foreach($calendarPages as $page)
109185e2535SAndreas Boehler      {
110185e2535SAndreas Boehler          $calid = $this->getCalendarIdForPage($page);
111185e2535SAndreas Boehler          if($calid !== false)
112185e2535SAndreas Boehler          {
113185e2535SAndreas Boehler            $settings = $this->getCalendarSettings($calid);
114185e2535SAndreas Boehler            $name = $settings['displayname'];
115185e2535SAndreas Boehler            $color = $settings['calendarcolor'];
116185e2535SAndreas Boehler            $data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
117185e2535SAndreas Boehler                            'color' => $color);
118185e2535SAndreas Boehler          }
119185e2535SAndreas Boehler      }
120185e2535SAndreas Boehler      return $data;
121185e2535SAndreas Boehler  }
122185e2535SAndreas Boehler
123185e2535SAndreas Boehler  /**
124185e2535SAndreas Boehler   * Get the saved calendar color for a given page.
125185e2535SAndreas Boehler   *
126185e2535SAndreas Boehler   * @param string $id optional The page ID
127185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
128185e2535SAndreas Boehler   */
129185e2535SAndreas Boehler  public function getCalendarColorForPage($id = null)
130185e2535SAndreas Boehler  {
131185e2535SAndreas Boehler      if(is_null($id))
132185e2535SAndreas Boehler      {
133185e2535SAndreas Boehler          global $ID;
134185e2535SAndreas Boehler          $id = $ID;
135185e2535SAndreas Boehler      }
136185e2535SAndreas Boehler
137185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
138185e2535SAndreas Boehler      if($calid === false)
139185e2535SAndreas Boehler        return false;
140185e2535SAndreas Boehler
141185e2535SAndreas Boehler      return $this->getCalendarColorForCalendar($calid);
142185e2535SAndreas Boehler  }
143185e2535SAndreas Boehler
144185e2535SAndreas Boehler  /**
145185e2535SAndreas Boehler   * Get the saved calendar color for a given calendar ID.
146185e2535SAndreas Boehler   *
147185e2535SAndreas Boehler   * @param string $id optional The calendar ID
148185e2535SAndreas Boehler   * @return mixed The color on success, otherwise false
149185e2535SAndreas Boehler   */
150185e2535SAndreas Boehler  public function getCalendarColorForCalendar($calid)
151185e2535SAndreas Boehler  {
152185e2535SAndreas Boehler      if(isset($this->cachedValues['calendarcolor'][$calid]))
153185e2535SAndreas Boehler        return $this->cachedValues['calendarcolor'][$calid];
154185e2535SAndreas Boehler
155185e2535SAndreas Boehler      $row = $this->getCalendarSettings($calid);
156185e2535SAndreas Boehler
157185e2535SAndreas Boehler      if(!isset($row['calendarcolor']))
158185e2535SAndreas Boehler        return false;
159185e2535SAndreas Boehler
160185e2535SAndreas Boehler      $color = $row['calendarcolor'];
161185e2535SAndreas Boehler      $this->cachedValues['calendarcolor'][$calid] = $color;
162185e2535SAndreas Boehler      return $color;
163185e2535SAndreas Boehler  }
164185e2535SAndreas Boehler
165185e2535SAndreas Boehler  /**
166185e2535SAndreas Boehler   * Set the calendar color for a given page.
167185e2535SAndreas Boehler   *
168185e2535SAndreas Boehler   * @param string $color The color definition
169185e2535SAndreas Boehler   * @param string $id optional The page ID
170185e2535SAndreas Boehler   * @return boolean True on success, otherwise false
171185e2535SAndreas Boehler   */
172185e2535SAndreas Boehler  public function setCalendarColorForPage($color, $id = null)
173185e2535SAndreas Boehler  {
174185e2535SAndreas Boehler      if(is_null($id))
175185e2535SAndreas Boehler      {
176185e2535SAndreas Boehler          global $ID;
177185e2535SAndreas Boehler          $id = $ID;
178185e2535SAndreas Boehler      }
179185e2535SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
180185e2535SAndreas Boehler      if($calid === false)
181185e2535SAndreas Boehler        return false;
182185e2535SAndreas Boehler
183185e2535SAndreas Boehler      $query = "UPDATE calendars SET calendarcolor=".$this->sqlite->quote_string($color).
184185e2535SAndreas Boehler               " WHERE id=".$this->sqlite->quote_string($calid);
185185e2535SAndreas Boehler      $res = $this->sqlite->query($query);
186185e2535SAndreas Boehler      if($res !== false)
187185e2535SAndreas Boehler      {
188185e2535SAndreas Boehler        $this->cachedValues['calendarcolor'][$calid] = $color;
189185e2535SAndreas Boehler        return true;
190185e2535SAndreas Boehler      }
191185e2535SAndreas Boehler      return false;
192185e2535SAndreas Boehler  }
193185e2535SAndreas Boehler
194185e2535SAndreas Boehler  /**
195cb71a62aSAndreas Boehler   * Set the calendar name and description for a given page with a given
196cb71a62aSAndreas Boehler   * page id.
197cb71a62aSAndreas Boehler   * If the calendar doesn't exist, the calendar is created!
198cb71a62aSAndreas Boehler   *
199cb71a62aSAndreas Boehler   * @param string  $name The name of the new calendar
200cb71a62aSAndreas Boehler   * @param string  $description The description of the new calendar
201cb71a62aSAndreas Boehler   * @param string  $id (optional) The ID of the page
202cb71a62aSAndreas Boehler   * @param string  $userid The userid of the creating user
203cb71a62aSAndreas Boehler   *
204cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false.
205cb71a62aSAndreas Boehler   */
206a1a3b679SAndreas Boehler  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
207a1a3b679SAndreas Boehler  {
208a1a3b679SAndreas Boehler      if(is_null($id))
209a1a3b679SAndreas Boehler      {
210a1a3b679SAndreas Boehler          global $ID;
211a1a3b679SAndreas Boehler          $id = $ID;
212a1a3b679SAndreas Boehler      }
213a1a3b679SAndreas Boehler      if(is_null($userid))
21434a47953SAndreas Boehler      {
21534a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
21634a47953SAndreas Boehler        {
217a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
21834a47953SAndreas Boehler        }
21934a47953SAndreas Boehler        else
22034a47953SAndreas Boehler        {
22134a47953SAndreas Boehler          $userid = uniqid('davcal-');
22234a47953SAndreas Boehler        }
22334a47953SAndreas Boehler      }
224a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
225a1a3b679SAndreas Boehler      if($calid === false)
226a1a3b679SAndreas Boehler        return $this->createCalendarForPage($name, $description, $id, $userid);
227a1a3b679SAndreas Boehler
228b269830cSAndreas Boehler      $query = "UPDATE calendars SET displayname=".$this->sqlite->quote_string($name).", ".
229b269830cSAndreas Boehler               "description=".$this->sqlite->quote_string($description)." WHERE ".
230b269830cSAndreas Boehler               "id=".$this->sqlite->quote_string($calid);
231b269830cSAndreas Boehler      $res = $this->sqlite->query($query);
232b269830cSAndreas Boehler      if($res !== false)
233b269830cSAndreas Boehler        return true;
234b269830cSAndreas Boehler      return false;
235a1a3b679SAndreas Boehler  }
236a1a3b679SAndreas Boehler
237cb71a62aSAndreas Boehler  /**
238cb71a62aSAndreas Boehler   * Save the personal settings to the SQLite database 'calendarsettings'.
239cb71a62aSAndreas Boehler   *
240cb71a62aSAndreas Boehler   * @param array  $settings The settings array to store
241cb71a62aSAndreas Boehler   * @param string $userid (optional) The userid to store
242cb71a62aSAndreas Boehler   *
243cb71a62aSAndreas Boehler   * @param boolean True on success, otherwise false
244cb71a62aSAndreas Boehler   */
245a495d34cSAndreas Boehler  public function savePersonalSettings($settings, $userid = null)
246a495d34cSAndreas Boehler  {
247a495d34cSAndreas Boehler      if(is_null($userid))
24834a47953SAndreas Boehler      {
24934a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
25034a47953SAndreas Boehler          {
251a495d34cSAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
25234a47953SAndreas Boehler          }
25334a47953SAndreas Boehler          else
25434a47953SAndreas Boehler          {
25534a47953SAndreas Boehler              return false;
25634a47953SAndreas Boehler          }
25734a47953SAndreas Boehler      }
258a495d34cSAndreas Boehler      $this->sqlite->query("BEGIN TRANSACTION");
259a495d34cSAndreas Boehler
260bd883736SAndreas Boehler      $query = "DELETE FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid);
261bd883736SAndreas Boehler      $this->sqlite->query($query);
262bd883736SAndreas Boehler
263a495d34cSAndreas Boehler      foreach($settings as $key => $value)
264a495d34cSAndreas Boehler      {
265bd883736SAndreas Boehler          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (".
266a495d34cSAndreas Boehler                   $this->sqlite->quote_string($userid).", ".
267a495d34cSAndreas Boehler                   $this->sqlite->quote_string($key).", ".
268a495d34cSAndreas Boehler                   $this->sqlite->quote_string($value).")";
269a495d34cSAndreas Boehler          $res = $this->sqlite->query($query);
270a495d34cSAndreas Boehler          if($res === false)
271a495d34cSAndreas Boehler              return false;
272a495d34cSAndreas Boehler      }
273a495d34cSAndreas Boehler      $this->sqlite->query("COMMIT TRANSACTION");
274185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
275a495d34cSAndreas Boehler      return true;
276a495d34cSAndreas Boehler  }
277a495d34cSAndreas Boehler
278cb71a62aSAndreas Boehler  /**
279cb71a62aSAndreas Boehler   * Retrieve the settings array for a given user id.
280cb71a62aSAndreas Boehler   * Some sane defaults are returned, currently:
281cb71a62aSAndreas Boehler   *
282cb71a62aSAndreas Boehler   *    timezone    => local
283cb71a62aSAndreas Boehler   *    weeknumbers => 0
284cb71a62aSAndreas Boehler   *    workweek    => 0
285cb71a62aSAndreas Boehler   *
286cb71a62aSAndreas Boehler   * @param string $userid (optional) The user id to retrieve
287cb71a62aSAndreas Boehler   *
288cb71a62aSAndreas Boehler   * @return array The settings array
289cb71a62aSAndreas Boehler   */
290a495d34cSAndreas Boehler  public function getPersonalSettings($userid = null)
291a495d34cSAndreas Boehler  {
292bd883736SAndreas Boehler      // Some sane default settings
293bd883736SAndreas Boehler      $settings = array(
294fb813b30SAndreas Boehler        'timezone' => $this->getConf('timezone'),
295fb813b30SAndreas Boehler        'weeknumbers' => $this->getConf('weeknumbers'),
296fb813b30SAndreas Boehler        'workweek' => $this->getConf('workweek'),
297fb813b30SAndreas Boehler        'monday' => $this->getConf('monday')
298bd883736SAndreas Boehler      );
29934a47953SAndreas Boehler      if(is_null($userid))
30034a47953SAndreas Boehler      {
30134a47953SAndreas Boehler          if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
30234a47953SAndreas Boehler          {
30334a47953SAndreas Boehler            $userid = $_SERVER['REMOTE_USER'];
30434a47953SAndreas Boehler          }
30534a47953SAndreas Boehler          else
30634a47953SAndreas Boehler          {
30734a47953SAndreas Boehler            return $settings;
30834a47953SAndreas Boehler          }
30934a47953SAndreas Boehler      }
31034a47953SAndreas Boehler
31134a47953SAndreas Boehler      if(isset($this->cachedValues['settings'][$userid]))
31234a47953SAndreas Boehler        return $this->cachedValues['settings'][$userid];
313a495d34cSAndreas Boehler      $query = "SELECT key, value FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid);
314a495d34cSAndreas Boehler      $res = $this->sqlite->query($query);
315a495d34cSAndreas Boehler      $arr = $this->sqlite->res2arr($res);
316a495d34cSAndreas Boehler      foreach($arr as $row)
317a495d34cSAndreas Boehler      {
318a495d34cSAndreas Boehler          $settings[$row['key']] = $row['value'];
319a495d34cSAndreas Boehler      }
320185e2535SAndreas Boehler      $this->cachedValues['settings'][$userid] = $settings;
321a495d34cSAndreas Boehler      return $settings;
322a495d34cSAndreas Boehler  }
323a495d34cSAndreas Boehler
324cb71a62aSAndreas Boehler  /**
325cb71a62aSAndreas Boehler   * Retrieve the calendar ID based on a page ID from the SQLite table
326cb71a62aSAndreas Boehler   * 'pagetocalendarmapping'.
327cb71a62aSAndreas Boehler   *
328cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to retrieve the corresponding calendar
329cb71a62aSAndreas Boehler   *
330cb71a62aSAndreas Boehler   * @return mixed the ID on success, otherwise false
331cb71a62aSAndreas Boehler   */
332a1a3b679SAndreas Boehler  public function getCalendarIdForPage($id = null)
333a1a3b679SAndreas Boehler  {
334a1a3b679SAndreas Boehler      if(is_null($id))
335a1a3b679SAndreas Boehler      {
336a1a3b679SAndreas Boehler          global $ID;
337a1a3b679SAndreas Boehler          $id = $ID;
338a1a3b679SAndreas Boehler      }
339a1a3b679SAndreas Boehler
340185e2535SAndreas Boehler      if(isset($this->cachedValues['calid'][$id]))
341185e2535SAndreas Boehler        return $this->cachedValues['calid'][$id];
342185e2535SAndreas Boehler
343a1a3b679SAndreas Boehler      $query = "SELECT calid FROM pagetocalendarmapping WHERE page=".$this->sqlite->quote_string($id);
344a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
345a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
346a1a3b679SAndreas Boehler      if(isset($row['calid']))
347185e2535SAndreas Boehler      {
348185e2535SAndreas Boehler        $calid = $row['calid'];
349185e2535SAndreas Boehler        $this->cachedValues['calid'] = $calid;
350185e2535SAndreas Boehler        return $calid;
351185e2535SAndreas Boehler      }
352a1a3b679SAndreas Boehler      return false;
353a1a3b679SAndreas Boehler  }
354a1a3b679SAndreas Boehler
355cb71a62aSAndreas Boehler  /**
356cb71a62aSAndreas Boehler   * Retrieve the complete calendar id to page mapping.
357cb71a62aSAndreas Boehler   * This is necessary to be able to retrieve a list of
358cb71a62aSAndreas Boehler   * calendars for a given user and check the access rights.
359cb71a62aSAndreas Boehler   *
360cb71a62aSAndreas Boehler   * @return array The mapping array
361cb71a62aSAndreas Boehler   */
362a1a3b679SAndreas Boehler  public function getCalendarIdToPageMapping()
363a1a3b679SAndreas Boehler  {
364a1a3b679SAndreas Boehler      $query = "SELECT calid, page FROM pagetocalendarmapping";
365a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
366a1a3b679SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
367a1a3b679SAndreas Boehler      return $arr;
368a1a3b679SAndreas Boehler  }
369a1a3b679SAndreas Boehler
370cb71a62aSAndreas Boehler  /**
371cb71a62aSAndreas Boehler   * Retrieve all calendar IDs a given user has access to.
372cb71a62aSAndreas Boehler   * The user is specified by the principalUri, so the
373cb71a62aSAndreas Boehler   * user name is actually split from the URI component.
374cb71a62aSAndreas Boehler   *
375cb71a62aSAndreas Boehler   * Access rights are checked against DokuWiki's ACL
376cb71a62aSAndreas Boehler   * and applied accordingly.
377cb71a62aSAndreas Boehler   *
378cb71a62aSAndreas Boehler   * @param string $principalUri The principal URI to work on
379cb71a62aSAndreas Boehler   *
380cb71a62aSAndreas Boehler   * @return array An associative array of calendar IDs
381cb71a62aSAndreas Boehler   */
382a1a3b679SAndreas Boehler  public function getCalendarIdsForUser($principalUri)
383a1a3b679SAndreas Boehler  {
38434a47953SAndreas Boehler      global $auth;
385a1a3b679SAndreas Boehler      $user = explode('/', $principalUri);
386a1a3b679SAndreas Boehler      $user = end($user);
387a1a3b679SAndreas Boehler      $mapping = $this->getCalendarIdToPageMapping();
388a1a3b679SAndreas Boehler      $calids = array();
38934a47953SAndreas Boehler      $ud = $auth->getUserData($user);
39034a47953SAndreas Boehler      $groups = $ud['grps'];
391a1a3b679SAndreas Boehler      foreach($mapping as $row)
392a1a3b679SAndreas Boehler      {
393a1a3b679SAndreas Boehler          $id = $row['calid'];
394a1a3b679SAndreas Boehler          $page = $row['page'];
39534a47953SAndreas Boehler          $acl = auth_aclcheck($page, $user, $groups);
396a1a3b679SAndreas Boehler          if($acl >= AUTH_READ)
397a1a3b679SAndreas Boehler          {
398a1a3b679SAndreas Boehler              $write = $acl > AUTH_READ;
399a1a3b679SAndreas Boehler              $calids[$id] = array('readonly' => !$write);
400a1a3b679SAndreas Boehler          }
401a1a3b679SAndreas Boehler      }
402a1a3b679SAndreas Boehler      return $calids;
403a1a3b679SAndreas Boehler  }
404a1a3b679SAndreas Boehler
405cb71a62aSAndreas Boehler  /**
406cb71a62aSAndreas Boehler   * Create a new calendar for a given page ID and set name and description
407cb71a62aSAndreas Boehler   * accordingly. Also update the pagetocalendarmapping table on success.
408cb71a62aSAndreas Boehler   *
409cb71a62aSAndreas Boehler   * @param string $name The calendar's name
410cb71a62aSAndreas Boehler   * @param string $description The calendar's description
411cb71a62aSAndreas Boehler   * @param string $id (optional) The page ID to work on
412cb71a62aSAndreas Boehler   * @param string $userid (optional) The user ID that created the calendar
413cb71a62aSAndreas Boehler   *
414cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
415cb71a62aSAndreas Boehler   */
416a1a3b679SAndreas Boehler  public function createCalendarForPage($name, $description, $id = null, $userid = null)
417a1a3b679SAndreas Boehler  {
418a1a3b679SAndreas Boehler      if(is_null($id))
419a1a3b679SAndreas Boehler      {
420a1a3b679SAndreas Boehler          global $ID;
421a1a3b679SAndreas Boehler          $id = $ID;
422a1a3b679SAndreas Boehler      }
423a1a3b679SAndreas Boehler      if(is_null($userid))
42434a47953SAndreas Boehler      {
42534a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
42634a47953SAndreas Boehler        {
427a1a3b679SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
42834a47953SAndreas Boehler        }
42934a47953SAndreas Boehler        else
43034a47953SAndreas Boehler        {
43134a47953SAndreas Boehler          $userid = uniqid('davcal-');
43234a47953SAndreas Boehler        }
43334a47953SAndreas Boehler      }
434a1a3b679SAndreas Boehler      $values = array('principals/'.$userid,
435a1a3b679SAndreas Boehler                      $name,
436a1a3b679SAndreas Boehler                      str_replace(array('/', ' ', ':'), '_', $id),
437a1a3b679SAndreas Boehler                      $description,
438a1a3b679SAndreas Boehler                      'VEVENT,VTODO',
43955a741c0SAndreas Boehler                      0,
44055a741c0SAndreas Boehler                      1);
44155a741c0SAndreas Boehler      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) VALUES (".$this->sqlite->quote_and_join($values, ',').");";
442a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
44355a741c0SAndreas Boehler      if($res === false)
44455a741c0SAndreas Boehler        return false;
445cb71a62aSAndreas Boehler
446cb71a62aSAndreas Boehler      // Get the new calendar ID
447a1a3b679SAndreas Boehler      $query = "SELECT id FROM calendars WHERE principaluri=".$this->sqlite->quote_string($values[0])." AND ".
448a1a3b679SAndreas Boehler               "displayname=".$this->sqlite->quote_string($values[1])." AND ".
449a1a3b679SAndreas Boehler               "uri=".$this->sqlite->quote_string($values[2])." AND ".
450a1a3b679SAndreas Boehler               "description=".$this->sqlite->quote_string($values[3]);
451a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
452a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
453cb71a62aSAndreas Boehler
454cb71a62aSAndreas Boehler      // Update the pagetocalendarmapping table with the new calendar ID
455a1a3b679SAndreas Boehler      if(isset($row['id']))
456a1a3b679SAndreas Boehler      {
457a1a3b679SAndreas Boehler          $values = array($id, $row['id']);
458a1a3b679SAndreas Boehler          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (".$this->sqlite->quote_and_join($values, ',').")";
459a1a3b679SAndreas Boehler          $res = $this->sqlite->query($query);
46055a741c0SAndreas Boehler          return ($res !== false);
461a1a3b679SAndreas Boehler      }
462a1a3b679SAndreas Boehler
463a1a3b679SAndreas Boehler      return false;
464a1a3b679SAndreas Boehler  }
465a1a3b679SAndreas Boehler
466cb71a62aSAndreas Boehler  /**
467cb71a62aSAndreas Boehler   * Add a new iCal entry for a given page, i.e. a given calendar.
468cb71a62aSAndreas Boehler   *
469cb71a62aSAndreas Boehler   * The parameter array needs to contain
470cb71a62aSAndreas Boehler   *   timezone         => The timezone of the entries
471cb71a62aSAndreas Boehler   *   detectedtz       => The timezone as detected by the browser
472cb71a62aSAndreas Boehler   *   eventfrom        => The event's start date
473cb71a62aSAndreas Boehler   *   eventfromtime    => The event's start time
474cb71a62aSAndreas Boehler   *   eventto          => The event's end date
475cb71a62aSAndreas Boehler   *   eventtotime      => The event's end time
476cb71a62aSAndreas Boehler   *   eventname        => The event's name
477cb71a62aSAndreas Boehler   *   eventdescription => The event's description
478cb71a62aSAndreas Boehler   *
479cb71a62aSAndreas Boehler   * @param string $id The page ID to work on
480cb71a62aSAndreas Boehler   * @param string $user The user who created the calendar
481cb71a62aSAndreas Boehler   * @param string $params A parameter array with values to create
482cb71a62aSAndreas Boehler   *
483cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
484cb71a62aSAndreas Boehler   */
485a1a3b679SAndreas Boehler  public function addCalendarEntryToCalendarForPage($id, $user, $params)
486a1a3b679SAndreas Boehler  {
487bd883736SAndreas Boehler      $settings = $this->getPersonalSettings($user);
488bd883736SAndreas Boehler      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
489bd883736SAndreas Boehler          $timezone = new \DateTimeZone($settings['timezone']);
490a25c89eaSAndreas Boehler      elseif($settings['timezone'] === 'local')
491a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
492bd883736SAndreas Boehler      else
493bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
494cb71a62aSAndreas Boehler
495cb71a62aSAndreas Boehler      // Retrieve dates from settings
496b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
497b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
498b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
499b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
500cb71a62aSAndreas Boehler
501cb71a62aSAndreas Boehler      // Load SabreDAV
502a1a3b679SAndreas Boehler      require_once('vendor/autoload.php');
503a1a3b679SAndreas Boehler      $vcalendar = new \Sabre\VObject\Component\VCalendar();
504cb71a62aSAndreas Boehler
505cb71a62aSAndreas Boehler      // Add VCalendar, UID and Event Name
506a1a3b679SAndreas Boehler      $event = $vcalendar->add('VEVENT');
507b269830cSAndreas Boehler      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
508b269830cSAndreas Boehler      $event->add('UID', $uuid);
509a1a3b679SAndreas Boehler      $event->summary = $params['eventname'];
510cb71a62aSAndreas Boehler
511cb71a62aSAndreas Boehler      // Add a description if requested
5120eebc909SAndreas Boehler      $description = $params['eventdescription'];
5130eebc909SAndreas Boehler      if($description !== '')
5140eebc909SAndreas Boehler        $event->add('DESCRIPTION', $description);
515cb71a62aSAndreas Boehler
516cb71a62aSAndreas Boehler      // Create a timestamp for last modified, created and dtstamp values in UTC
517b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
518b269830cSAndreas Boehler      $event->add('DTSTAMP', $dtStamp);
519b269830cSAndreas Boehler      $event->add('CREATED', $dtStamp);
520b269830cSAndreas Boehler      $event->add('LAST-MODIFIED', $dtStamp);
521cb71a62aSAndreas Boehler
522cb71a62aSAndreas Boehler      // Adjust the start date, based on the given timezone information
523b269830cSAndreas Boehler      $dtStart = new \DateTime();
524a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
525b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
526cb71a62aSAndreas Boehler
527cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
528b269830cSAndreas Boehler      if($params['allday'] != '1')
529b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
530cb71a62aSAndreas Boehler
531cb71a62aSAndreas Boehler      // Adjust the end date, based on the given timezone information
532b269830cSAndreas Boehler      $dtEnd = new \DateTime();
533a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
534b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
535cb71a62aSAndreas Boehler
536cb71a62aSAndreas Boehler      // Only add the time values if it's not an allday event
537b269830cSAndreas Boehler      if($params['allday'] != '1')
538b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
539cb71a62aSAndreas Boehler
540b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
541b269830cSAndreas Boehler      if($params['allday'] == '1')
542b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
543cb71a62aSAndreas Boehler
544cb71a62aSAndreas Boehler      // Really add Start and End events
545b269830cSAndreas Boehler      $dtStartEv = $event->add('DTSTART', $dtStart);
546b269830cSAndreas Boehler      $dtEndEv = $event->add('DTEND', $dtEnd);
547cb71a62aSAndreas Boehler
548cb71a62aSAndreas Boehler      // Adjust the DATE format for allday events
549b269830cSAndreas Boehler      if($params['allday'] == '1')
550b269830cSAndreas Boehler      {
551b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
552b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
553b269830cSAndreas Boehler      }
554cb71a62aSAndreas Boehler
555cb71a62aSAndreas Boehler      // Actually add the values to the database
556a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
557a1a3b679SAndreas Boehler      $uri = uniqid('dokuwiki-').'.ics';
558a1a3b679SAndreas Boehler      $now = new DateTime();
559a1a3b679SAndreas Boehler      $eventStr = $vcalendar->serialize();
560a1a3b679SAndreas Boehler
561a1a3b679SAndreas Boehler      $values = array($calid,
562a1a3b679SAndreas Boehler                      $uri,
563a1a3b679SAndreas Boehler                      $eventStr,
564a1a3b679SAndreas Boehler                      $now->getTimestamp(),
565a1a3b679SAndreas Boehler                      'VEVENT',
566a1a3b679SAndreas Boehler                      $event->DTSTART->getDateTime()->getTimeStamp(),
567a1a3b679SAndreas Boehler                      $event->DTEND->getDateTime()->getTimeStamp(),
568a1a3b679SAndreas Boehler                      strlen($eventStr),
569a1a3b679SAndreas Boehler                      md5($eventStr),
570cb71a62aSAndreas Boehler                      $uuid
571a1a3b679SAndreas Boehler      );
572a1a3b679SAndreas Boehler
573a1a3b679SAndreas Boehler      $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (".$this->sqlite->quote_and_join($values, ',').")";
574a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
575cb71a62aSAndreas Boehler
576cb71a62aSAndreas Boehler      // If successfully, update the sync token database
57755a741c0SAndreas Boehler      if($res !== false)
57855a741c0SAndreas Boehler      {
57955a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'added');
580a1a3b679SAndreas Boehler          return true;
581a1a3b679SAndreas Boehler      }
58255a741c0SAndreas Boehler      return false;
58355a741c0SAndreas Boehler  }
584a1a3b679SAndreas Boehler
585cb71a62aSAndreas Boehler  /**
586cb71a62aSAndreas Boehler   * Retrieve the calendar settings of a given calendar id
587cb71a62aSAndreas Boehler   *
588cb71a62aSAndreas Boehler   * @param string $calid The calendar ID
589cb71a62aSAndreas Boehler   *
590cb71a62aSAndreas Boehler   * @return array The calendar settings array
591cb71a62aSAndreas Boehler   */
592b269830cSAndreas Boehler  public function getCalendarSettings($calid)
593b269830cSAndreas Boehler  {
594185e2535SAndreas Boehler      $query = "SELECT principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id=".$this->sqlite->quote_string($calid);
595b269830cSAndreas Boehler      $res = $this->sqlite->query($query);
596b269830cSAndreas Boehler      $row = $this->sqlite->res2row($res);
597b269830cSAndreas Boehler      return $row;
598b269830cSAndreas Boehler  }
599b269830cSAndreas Boehler
600cb71a62aSAndreas Boehler  /**
601cb71a62aSAndreas Boehler   * Retrieve all events that are within a given date range,
602cb71a62aSAndreas Boehler   * based on the timezone setting.
603cb71a62aSAndreas Boehler   *
604cb71a62aSAndreas Boehler   * There is also support for retrieving recurring events,
605cb71a62aSAndreas Boehler   * using Sabre's VObject Iterator. Recurring events are represented
606cb71a62aSAndreas Boehler   * as individual calendar entries with the same UID.
607cb71a62aSAndreas Boehler   *
608cb71a62aSAndreas Boehler   * @param string $id The page ID to work with
609cb71a62aSAndreas Boehler   * @param string $user The user ID to work with
610cb71a62aSAndreas Boehler   * @param string $startDate The start date as a string
611cb71a62aSAndreas Boehler   * @param string $endDate The end date as a string
612cb71a62aSAndreas Boehler   *
613cb71a62aSAndreas Boehler   * @return array An array containing the calendar entries.
614cb71a62aSAndreas Boehler   */
615a1a3b679SAndreas Boehler  public function getEventsWithinDateRange($id, $user, $startDate, $endDate)
616a1a3b679SAndreas Boehler  {
617bd883736SAndreas Boehler      $settings = $this->getPersonalSettings($user);
618bd883736SAndreas Boehler      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
619bd883736SAndreas Boehler          $timezone = new \DateTimeZone($settings['timezone']);
620bd883736SAndreas Boehler      else
621bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
622a1a3b679SAndreas Boehler      $data = array();
623cb71a62aSAndreas Boehler
624cb71a62aSAndreas Boehler      // Load SabreDAV
625a1a3b679SAndreas Boehler      require_once('vendor/autoload.php');
626a1a3b679SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
627185e2535SAndreas Boehler      $color = $this->getCalendarColorForCalendar($calid);
628a1a3b679SAndreas Boehler      $startTs = new \DateTime($startDate);
629a1a3b679SAndreas Boehler      $endTs = new \DateTime($endDate);
630cb71a62aSAndreas Boehler
631cb71a62aSAndreas Boehler      // Retrieve matching calendar objects
632ebc4eb57SAndreas Boehler      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid=".
633ebc4eb57SAndreas Boehler                $this->sqlite->quote_string($calid)." AND firstoccurence < ".
634ebc4eb57SAndreas Boehler                $this->sqlite->quote_string($endTs->getTimestamp())." AND lastoccurence > ".
635ebc4eb57SAndreas Boehler                $this->sqlite->quote_string($startTs->getTimestamp());
636a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
637a1a3b679SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
638cb71a62aSAndreas Boehler
639cb71a62aSAndreas Boehler      // Parse individual calendar entries
640a1a3b679SAndreas Boehler      foreach($arr as $row)
641a1a3b679SAndreas Boehler      {
642a1a3b679SAndreas Boehler          if(isset($row['calendardata']))
643a1a3b679SAndreas Boehler          {
644b269830cSAndreas Boehler              $entry = array();
645a1a3b679SAndreas Boehler              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
646ebc4eb57SAndreas Boehler              $recurrence = $vcal->VEVENT->RRULE;
647cb71a62aSAndreas Boehler              // If it is a recurring event, pass it through Sabre's EventIterator
648ebc4eb57SAndreas Boehler              if($recurrence != null)
649ebc4eb57SAndreas Boehler              {
650ebc4eb57SAndreas Boehler                  $rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
651ebc4eb57SAndreas Boehler                  $rEvents->rewind();
652ebc4eb57SAndreas Boehler                  $done = false;
653ebc4eb57SAndreas Boehler                  while($rEvents->valid() && !$done)
654ebc4eb57SAndreas Boehler                  {
655ebc4eb57SAndreas Boehler                      $event = $rEvents->getEventObject();
656cb71a62aSAndreas Boehler                      // If we are after the given time range, exit
657ebc4eb57SAndreas Boehler                      if(($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()) &&
658ebc4eb57SAndreas Boehler                         ($rEvents->getDtEnd()->getTimestamp() > $endTs->getTimestamp()))
659ebc4eb57SAndreas Boehler                        $done = true;
660cb71a62aSAndreas Boehler
661cb71a62aSAndreas Boehler                      // If we are before the given time range, continue
662ebc4eb57SAndreas Boehler                      if($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp())
663ebc4eb57SAndreas Boehler                      {
664ebc4eb57SAndreas Boehler                          $rEvents->next();
665ebc4eb57SAndreas Boehler                          continue;
666ebc4eb57SAndreas Boehler                      }
667cb71a62aSAndreas Boehler
668cb71a62aSAndreas Boehler                      // If we are within the given time range, parse the event
669185e2535SAndreas Boehler                      $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
670ebc4eb57SAndreas Boehler                      $rEvents->next();
671ebc4eb57SAndreas Boehler                  }
672ebc4eb57SAndreas Boehler              }
673ebc4eb57SAndreas Boehler              else
674185e2535SAndreas Boehler                $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
675ebc4eb57SAndreas Boehler          }
676ebc4eb57SAndreas Boehler      }
677ebc4eb57SAndreas Boehler      return $data;
678ebc4eb57SAndreas Boehler  }
679ebc4eb57SAndreas Boehler
680cb71a62aSAndreas Boehler  /**
681cb71a62aSAndreas Boehler   * Helper function that parses the iCal data of a VEVENT to a calendar entry.
682cb71a62aSAndreas Boehler   *
683cb71a62aSAndreas Boehler   * @param \Sabre\VObject\VEvent $event The event to parse
684cb71a62aSAndreas Boehler   * @param \DateTimeZone $timezone The timezone object
685cb71a62aSAndreas Boehler   * @param string $uid The entry's UID
6863c86dda8SAndreas Boehler   * @param boolean $recurring (optional) Set to true to define a recurring event
687cb71a62aSAndreas Boehler   *
688cb71a62aSAndreas Boehler   * @return array The parse calendar entry
689cb71a62aSAndreas Boehler   */
690185e2535SAndreas Boehler  private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
691ebc4eb57SAndreas Boehler  {
692ebc4eb57SAndreas Boehler      $entry = array();
693ebc4eb57SAndreas Boehler      $start = $event->DTSTART;
694cb71a62aSAndreas Boehler      // Parse only if the start date/time is present
695b269830cSAndreas Boehler      if($start !== null)
696b269830cSAndreas Boehler      {
697b269830cSAndreas Boehler        $dtStart = $start->getDateTime();
698b269830cSAndreas Boehler        $dtStart->setTimezone($timezone);
699b269830cSAndreas Boehler        $entry['start'] = $dtStart->format(\DateTime::ATOM);
700b269830cSAndreas Boehler        if($start['VALUE'] == 'DATE')
701b269830cSAndreas Boehler          $entry['allDay'] = true;
702b269830cSAndreas Boehler        else
703b269830cSAndreas Boehler          $entry['allDay'] = false;
704b269830cSAndreas Boehler      }
705ebc4eb57SAndreas Boehler      $end = $event->DTEND;
706cb71a62aSAndreas Boehler      // Parse onlyl if the end date/time is present
707b269830cSAndreas Boehler      if($end !== null)
708b269830cSAndreas Boehler      {
709b269830cSAndreas Boehler        $dtEnd = $end->getDateTime();
710b269830cSAndreas Boehler        $dtEnd->setTimezone($timezone);
711b269830cSAndreas Boehler        $entry['end'] = $dtEnd->format(\DateTime::ATOM);
712b269830cSAndreas Boehler      }
713ebc4eb57SAndreas Boehler      $description = $event->DESCRIPTION;
7140eebc909SAndreas Boehler      if($description !== null)
7150eebc909SAndreas Boehler        $entry['description'] = (string)$description;
7160eebc909SAndreas Boehler      else
7170eebc909SAndreas Boehler        $entry['description'] = '';
718ebc4eb57SAndreas Boehler      $entry['title'] = (string)$event->summary;
719ebc4eb57SAndreas Boehler      $entry['id'] = $uid;
720185e2535SAndreas Boehler      $entry['page'] = $page;
721185e2535SAndreas Boehler      $entry['color'] = $color;
7223c86dda8SAndreas Boehler      $entry['recurring'] = $recurring;
723185e2535SAndreas Boehler
724ebc4eb57SAndreas Boehler      return $entry;
725a1a3b679SAndreas Boehler  }
726a1a3b679SAndreas Boehler
727cb71a62aSAndreas Boehler  /**
728cb71a62aSAndreas Boehler   * Retrieve an event by its UID
729cb71a62aSAndreas Boehler   *
730cb71a62aSAndreas Boehler   * @param string $uid The event's UID
731cb71a62aSAndreas Boehler   *
732cb71a62aSAndreas Boehler   * @return mixed The table row with the given event
733cb71a62aSAndreas Boehler   */
734a1a3b679SAndreas Boehler  public function getEventWithUid($uid)
735a1a3b679SAndreas Boehler  {
73655a741c0SAndreas Boehler      $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid=".
737a1a3b679SAndreas Boehler                $this->sqlite->quote_string($uid);
738a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
739a1a3b679SAndreas Boehler      $row = $this->sqlite->res2row($res);
740a1a3b679SAndreas Boehler      return $row;
741a1a3b679SAndreas Boehler  }
742a1a3b679SAndreas Boehler
743cb71a62aSAndreas Boehler  /**
744cb71a62aSAndreas Boehler   * Retrieve all calendar events for a given calendar ID
745cb71a62aSAndreas Boehler   *
746cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
747cb71a62aSAndreas Boehler   *
748cb71a62aSAndreas Boehler   * @return array An array containing all calendar data
749cb71a62aSAndreas Boehler   */
750f69bb449SAndreas Boehler  public function getAllCalendarEvents($calid)
751f69bb449SAndreas Boehler  {
752f69bb449SAndreas Boehler      $query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid=".
753f69bb449SAndreas Boehler               $this->sqlite->quote_string($calid);
754f69bb449SAndreas Boehler      $res = $this->sqlite->query($query);
755f69bb449SAndreas Boehler      $arr = $this->sqlite->res2arr($res);
756f69bb449SAndreas Boehler      return $arr;
757f69bb449SAndreas Boehler  }
758f69bb449SAndreas Boehler
759cb71a62aSAndreas Boehler  /**
760cb71a62aSAndreas Boehler   * Edit a calendar entry for a page, given by its parameters.
761cb71a62aSAndreas Boehler   * The params array has the same format as @see addCalendarEntryForPage
762cb71a62aSAndreas Boehler   *
763cb71a62aSAndreas Boehler   * @param string $id The page's ID to work on
764cb71a62aSAndreas Boehler   * @param string $user The user's ID to work on
765cb71a62aSAndreas Boehler   * @param array $params The parameter array for the edited calendar event
766cb71a62aSAndreas Boehler   *
767cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
768cb71a62aSAndreas Boehler   */
769a1a3b679SAndreas Boehler  public function editCalendarEntryForPage($id, $user, $params)
770a1a3b679SAndreas Boehler  {
771bd883736SAndreas Boehler      $settings = $this->getPersonalSettings($user);
772bd883736SAndreas Boehler      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
773bd883736SAndreas Boehler          $timezone = new \DateTimeZone($settings['timezone']);
774a25c89eaSAndreas Boehler      elseif($settings['timezone'] === 'local')
775a25c89eaSAndreas Boehler          $timezone = new \DateTimeZone($params['detectedtz']);
776bd883736SAndreas Boehler      else
777bd883736SAndreas Boehler          $timezone = new \DateTimeZone('UTC');
778cb71a62aSAndreas Boehler
779cb71a62aSAndreas Boehler      // Parse dates
780b269830cSAndreas Boehler      $startDate = explode('-', $params['eventfrom']);
781b269830cSAndreas Boehler      $startTime = explode(':', $params['eventfromtime']);
782b269830cSAndreas Boehler      $endDate = explode('-', $params['eventto']);
783b269830cSAndreas Boehler      $endTime = explode(':', $params['eventtotime']);
784cb71a62aSAndreas Boehler
785cb71a62aSAndreas Boehler      // Retrieve the existing event based on the UID
78655a741c0SAndreas Boehler      $uid = $params['uid'];
78755a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
788cb71a62aSAndreas Boehler
789cb71a62aSAndreas Boehler      // Load SabreDAV
790a1a3b679SAndreas Boehler      require_once('vendor/autoload.php');
791a1a3b679SAndreas Boehler      if(!isset($event['calendardata']))
792a1a3b679SAndreas Boehler        return false;
79355a741c0SAndreas Boehler      $uri = $event['uri'];
79455a741c0SAndreas Boehler      $calid = $event['calendarid'];
795cb71a62aSAndreas Boehler
796cb71a62aSAndreas Boehler      // Parse the existing event
797a1a3b679SAndreas Boehler      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
798b269830cSAndreas Boehler      $vevent = $vcal->VEVENT;
799cb71a62aSAndreas Boehler
800cb71a62aSAndreas Boehler      // Set the new event values
801b269830cSAndreas Boehler      $vevent->summary = $params['eventname'];
802b269830cSAndreas Boehler      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
8030eebc909SAndreas Boehler      $description = $params['eventdescription'];
804cb71a62aSAndreas Boehler
805cb71a62aSAndreas Boehler      // Remove existing timestamps to overwrite them
8060eebc909SAndreas Boehler      $vevent->remove('DESCRIPTION');
807b269830cSAndreas Boehler      $vevent->remove('DTSTAMP');
808b269830cSAndreas Boehler      $vevent->remove('LAST-MODIFIED');
809cb71a62aSAndreas Boehler
810cb71a62aSAndreas Boehler      // Add new time stamps and description
811b269830cSAndreas Boehler      $vevent->add('DTSTAMP', $dtStamp);
812b269830cSAndreas Boehler      $vevent->add('LAST-MODIFIED', $dtStamp);
8130eebc909SAndreas Boehler      if($description !== '')
8140eebc909SAndreas Boehler        $vevent->add('DESCRIPTION', $description);
815cb71a62aSAndreas Boehler
816cb71a62aSAndreas Boehler      // Setup DTSTART
817b269830cSAndreas Boehler      $dtStart = new \DateTime();
818a25c89eaSAndreas Boehler      $dtStart->setTimezone($timezone);
819b269830cSAndreas Boehler      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
820b269830cSAndreas Boehler      if($params['allday'] != '1')
821b269830cSAndreas Boehler        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
822cb71a62aSAndreas Boehler
823cb71a62aSAndreas Boehler      // Setup DETEND
824b269830cSAndreas Boehler      $dtEnd = new \DateTime();
825a25c89eaSAndreas Boehler      $dtEnd->setTimezone($timezone);
826b269830cSAndreas Boehler      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
827b269830cSAndreas Boehler      if($params['allday'] != '1')
828b269830cSAndreas Boehler        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
829cb71a62aSAndreas Boehler
830b269830cSAndreas Boehler      // According to the VCal spec, we need to add a whole day here
831b269830cSAndreas Boehler      if($params['allday'] == '1')
832b269830cSAndreas Boehler          $dtEnd->add(new \DateInterval('P1D'));
833b269830cSAndreas Boehler      $vevent->remove('DTSTART');
834b269830cSAndreas Boehler      $vevent->remove('DTEND');
835b269830cSAndreas Boehler      $dtStartEv = $vevent->add('DTSTART', $dtStart);
836b269830cSAndreas Boehler      $dtEndEv = $vevent->add('DTEND', $dtEnd);
837cb71a62aSAndreas Boehler
838cb71a62aSAndreas Boehler      // Remove the time for allday events
839b269830cSAndreas Boehler      if($params['allday'] == '1')
840b269830cSAndreas Boehler      {
841b269830cSAndreas Boehler          $dtStartEv['VALUE'] = 'DATE';
842b269830cSAndreas Boehler          $dtEndEv['VALUE'] = 'DATE';
843b269830cSAndreas Boehler      }
844a1a3b679SAndreas Boehler      $now = new DateTime();
845a1a3b679SAndreas Boehler      $eventStr = $vcal->serialize();
846a1a3b679SAndreas Boehler
847cb71a62aSAndreas Boehler      // Actually write to the database
848a1a3b679SAndreas Boehler      $query = "UPDATE calendarobjects SET calendardata=".$this->sqlite->quote_string($eventStr).
849a1a3b679SAndreas Boehler               ", lastmodified=".$this->sqlite->quote_string($now->getTimestamp()).
850a1a3b679SAndreas Boehler               ", firstoccurence=".$this->sqlite->quote_string($dtStart->getTimestamp()).
851a1a3b679SAndreas Boehler               ", lastoccurence=".$this->sqlite->quote_string($dtEnd->getTimestamp()).
852a1a3b679SAndreas Boehler               ", size=".strlen($eventStr).
853a1a3b679SAndreas Boehler               ", etag=".$this->sqlite->quote_string(md5($eventStr)).
85455a741c0SAndreas Boehler               " WHERE uid=".$this->sqlite->quote_string($uid);
855a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
85655a741c0SAndreas Boehler      if($res !== false)
85755a741c0SAndreas Boehler      {
85855a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'modified');
859a1a3b679SAndreas Boehler          return true;
860a1a3b679SAndreas Boehler      }
86155a741c0SAndreas Boehler      return false;
86255a741c0SAndreas Boehler  }
863a1a3b679SAndreas Boehler
864cb71a62aSAndreas Boehler  /**
865cb71a62aSAndreas Boehler   * Delete a calendar entry for a given page. Actually, the event is removed
866cb71a62aSAndreas Boehler   * based on the entry's UID, so that page ID is no used.
867cb71a62aSAndreas Boehler   *
868cb71a62aSAndreas Boehler   * @param string $id The page's ID (unused)
869cb71a62aSAndreas Boehler   * @param array $params The parameter array to work with
870cb71a62aSAndreas Boehler   *
871cb71a62aSAndreas Boehler   * @return boolean True
872cb71a62aSAndreas Boehler   */
873a1a3b679SAndreas Boehler  public function deleteCalendarEntryForPage($id, $params)
874a1a3b679SAndreas Boehler  {
875a1a3b679SAndreas Boehler      $uid = $params['uid'];
87655a741c0SAndreas Boehler      $event = $this->getEventWithUid($uid);
8772c14b82bSAndreas Boehler      $calid = $event['calendarid'];
87855a741c0SAndreas Boehler      $uri = $event['uri'];
879a1a3b679SAndreas Boehler      $query = "DELETE FROM calendarobjects WHERE uid=".$this->sqlite->quote_string($uid);
880a1a3b679SAndreas Boehler      $res = $this->sqlite->query($query);
88155a741c0SAndreas Boehler      if($res !== false)
88255a741c0SAndreas Boehler      {
88355a741c0SAndreas Boehler          $this->updateSyncTokenLog($calid, $uri, 'deleted');
88455a741c0SAndreas Boehler      }
885a1a3b679SAndreas Boehler      return true;
886a1a3b679SAndreas Boehler  }
887a1a3b679SAndreas Boehler
888cb71a62aSAndreas Boehler  /**
889cb71a62aSAndreas Boehler   * Retrieve the current sync token for a calendar
890cb71a62aSAndreas Boehler   *
891cb71a62aSAndreas Boehler   * @param string $calid The calendar id
892cb71a62aSAndreas Boehler   *
893cb71a62aSAndreas Boehler   * @return mixed The synctoken or false
894cb71a62aSAndreas Boehler   */
89555a741c0SAndreas Boehler  public function getSyncTokenForCalendar($calid)
89655a741c0SAndreas Boehler  {
897b269830cSAndreas Boehler      $row = $this->getCalendarSettings($calid);
89855a741c0SAndreas Boehler      if(isset($row['synctoken']))
89955a741c0SAndreas Boehler          return $row['synctoken'];
90055a741c0SAndreas Boehler      return false;
90155a741c0SAndreas Boehler  }
90255a741c0SAndreas Boehler
903cb71a62aSAndreas Boehler  /**
904cb71a62aSAndreas Boehler   * Helper function to convert the operation name to
905cb71a62aSAndreas Boehler   * an operation code as stored in the database
906cb71a62aSAndreas Boehler   *
907cb71a62aSAndreas Boehler   * @param string $operationName The operation name
908cb71a62aSAndreas Boehler   *
909cb71a62aSAndreas Boehler   * @return mixed The operation code or false
910cb71a62aSAndreas Boehler   */
91155a741c0SAndreas Boehler  public function operationNameToOperation($operationName)
91255a741c0SAndreas Boehler  {
91355a741c0SAndreas Boehler      switch($operationName)
91455a741c0SAndreas Boehler      {
91555a741c0SAndreas Boehler          case 'added':
91655a741c0SAndreas Boehler              return 1;
91755a741c0SAndreas Boehler          break;
91855a741c0SAndreas Boehler          case 'modified':
91955a741c0SAndreas Boehler              return 2;
92055a741c0SAndreas Boehler          break;
92155a741c0SAndreas Boehler          case 'deleted':
92255a741c0SAndreas Boehler              return 3;
92355a741c0SAndreas Boehler          break;
92455a741c0SAndreas Boehler      }
92555a741c0SAndreas Boehler      return false;
92655a741c0SAndreas Boehler  }
92755a741c0SAndreas Boehler
928cb71a62aSAndreas Boehler  /**
929cb71a62aSAndreas Boehler   * Update the sync token log based on the calendar id and the
930cb71a62aSAndreas Boehler   * operation that was performed.
931cb71a62aSAndreas Boehler   *
932cb71a62aSAndreas Boehler   * @param string $calid The calendar ID that was modified
933cb71a62aSAndreas Boehler   * @param string $uri The calendar URI that was modified
934cb71a62aSAndreas Boehler   * @param string $operation The operation that was performed
935cb71a62aSAndreas Boehler   *
936cb71a62aSAndreas Boehler   * @return boolean True on success, otherwise false
937cb71a62aSAndreas Boehler   */
93855a741c0SAndreas Boehler  private function updateSyncTokenLog($calid, $uri, $operation)
93955a741c0SAndreas Boehler  {
94055a741c0SAndreas Boehler      $currentToken = $this->getSyncTokenForCalendar($calid);
94155a741c0SAndreas Boehler      $operationCode = $this->operationNameToOperation($operation);
94255a741c0SAndreas Boehler      if(($operationCode === false) || ($currentToken === false))
94355a741c0SAndreas Boehler          return false;
94455a741c0SAndreas Boehler      $values = array($uri,
94555a741c0SAndreas Boehler                      $currentToken,
94655a741c0SAndreas Boehler                      $calid,
94755a741c0SAndreas Boehler                      $operationCode
94855a741c0SAndreas Boehler      );
94955a741c0SAndreas Boehler      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(".
95055a741c0SAndreas Boehler               $this->sqlite->quote_and_join($values, ',').")";
95155a741c0SAndreas Boehler      $res = $this->sqlite->query($query);
95255a741c0SAndreas Boehler      if($res === false)
95355a741c0SAndreas Boehler        return false;
95455a741c0SAndreas Boehler      $currentToken++;
95555a741c0SAndreas Boehler      $query = "UPDATE calendars SET synctoken=".$this->sqlite->quote_string($currentToken)." WHERE id=".
95655a741c0SAndreas Boehler               $this->sqlite->quote_string($calid);
95755a741c0SAndreas Boehler      $res = $this->sqlite->query($query);
95855a741c0SAndreas Boehler      return ($res !== false);
95955a741c0SAndreas Boehler  }
96055a741c0SAndreas Boehler
961cb71a62aSAndreas Boehler  /**
962cb71a62aSAndreas Boehler   * Return the sync URL for a given Page, i.e. a calendar
963cb71a62aSAndreas Boehler   *
964cb71a62aSAndreas Boehler   * @param string $id The page's ID
965cb71a62aSAndreas Boehler   * @param string $user (optional) The user's ID
966cb71a62aSAndreas Boehler   *
967cb71a62aSAndreas Boehler   * @return mixed The sync url or false
968cb71a62aSAndreas Boehler   */
969b269830cSAndreas Boehler  public function getSyncUrlForPage($id, $user = null)
970b269830cSAndreas Boehler  {
97134a47953SAndreas Boehler      if(is_null($userid))
97234a47953SAndreas Boehler      {
97334a47953SAndreas Boehler        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
97434a47953SAndreas Boehler        {
97534a47953SAndreas Boehler          $userid = $_SERVER['REMOTE_USER'];
97634a47953SAndreas Boehler        }
97734a47953SAndreas Boehler        else
97834a47953SAndreas Boehler        {
97934a47953SAndreas Boehler          return false;
98034a47953SAndreas Boehler        }
98134a47953SAndreas Boehler      }
982b269830cSAndreas Boehler
983b269830cSAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
984b269830cSAndreas Boehler      if($calid === false)
985b269830cSAndreas Boehler        return false;
986b269830cSAndreas Boehler
987b269830cSAndreas Boehler      $calsettings = $this->getCalendarSettings($calid);
988b269830cSAndreas Boehler      if(!isset($calsettings['uri']))
989b269830cSAndreas Boehler        return false;
990b269830cSAndreas Boehler
991b269830cSAndreas Boehler      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
992b269830cSAndreas Boehler      return $syncurl;
993b269830cSAndreas Boehler  }
994b269830cSAndreas Boehler
995cb71a62aSAndreas Boehler  /**
996cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given page
997cb71a62aSAndreas Boehler   *
998cb71a62aSAndreas Boehler   * @param string $id the page ID
999cb71a62aSAndreas Boehler   *
1000cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1001cb71a62aSAndreas Boehler   */
1002f69bb449SAndreas Boehler  public function getPrivateURLForPage($id)
1003f69bb449SAndreas Boehler  {
1004f69bb449SAndreas Boehler      $calid = $this->getCalendarIdForPage($id);
1005f69bb449SAndreas Boehler      if($calid === false)
1006f69bb449SAndreas Boehler        return false;
1007f69bb449SAndreas Boehler
1008f69bb449SAndreas Boehler      return $this->getPrivateURLForCalendar($calid);
1009f69bb449SAndreas Boehler  }
1010f69bb449SAndreas Boehler
1011cb71a62aSAndreas Boehler  /**
1012cb71a62aSAndreas Boehler   * Return the private calendar's URL for a given calendar ID
1013cb71a62aSAndreas Boehler   *
1014cb71a62aSAndreas Boehler   * @param string $calid The calendar's ID
1015cb71a62aSAndreas Boehler   *
1016cb71a62aSAndreas Boehler   * @return mixed The private URL or false
1017cb71a62aSAndreas Boehler   */
1018f69bb449SAndreas Boehler  public function getPrivateURLForCalendar($calid)
1019f69bb449SAndreas Boehler  {
1020185e2535SAndreas Boehler      if(isset($this->cachedValues['privateurl'][$calid]))
1021185e2535SAndreas Boehler        return $this->cachedValues['privateurl'][$calid];
1022f69bb449SAndreas Boehler      $query = "SELECT url FROM calendartoprivateurlmapping WHERE calid=".$this->sqlite->quote_string($calid);
1023f69bb449SAndreas Boehler      $res = $this->sqlite->query($query);
1024f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1025f69bb449SAndreas Boehler      if(!isset($row['url']))
1026f69bb449SAndreas Boehler      {
1027f69bb449SAndreas Boehler          $url = uniqid("dokuwiki-").".ics";
1028f69bb449SAndreas Boehler          $values = array(
1029f69bb449SAndreas Boehler                $url,
1030f69bb449SAndreas Boehler                $calid
1031f69bb449SAndreas Boehler          );
1032f69bb449SAndreas Boehler          $query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(".
1033f69bb449SAndreas Boehler                $this->sqlite->quote_and_join($values, ", ").")";
1034f69bb449SAndreas Boehler          $res = $this->sqlite->query($query);
1035f69bb449SAndreas Boehler          if($res === false)
1036f69bb449SAndreas Boehler            return false;
1037f69bb449SAndreas Boehler      }
1038f69bb449SAndreas Boehler      else
1039f69bb449SAndreas Boehler      {
1040f69bb449SAndreas Boehler          $url = $row['url'];
1041f69bb449SAndreas Boehler      }
1042185e2535SAndreas Boehler
1043185e2535SAndreas Boehler      $url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url;
1044185e2535SAndreas Boehler      $this->cachedValues['privateurl'][$calid] = $url;
1045185e2535SAndreas Boehler      return $url;
1046f69bb449SAndreas Boehler  }
1047f69bb449SAndreas Boehler
1048cb71a62aSAndreas Boehler  /**
1049cb71a62aSAndreas Boehler   * Retrieve the calendar ID for a given private calendar URL
1050cb71a62aSAndreas Boehler   *
1051cb71a62aSAndreas Boehler   * @param string $url The private URL
1052cb71a62aSAndreas Boehler   *
1053cb71a62aSAndreas Boehler   * @return mixed The calendar ID or false
1054cb71a62aSAndreas Boehler   */
1055f69bb449SAndreas Boehler  public function getCalendarForPrivateURL($url)
1056f69bb449SAndreas Boehler  {
1057f69bb449SAndreas Boehler      $query = "SELECT calid FROM calendartoprivateurlmapping WHERE url=".$this->sqlite->quote_string($url);
1058f69bb449SAndreas Boehler      $res = $this->sqlite->query($query);
1059f69bb449SAndreas Boehler      $row = $this->sqlite->res2row($res);
1060f69bb449SAndreas Boehler      if(!isset($row['calid']))
1061f69bb449SAndreas Boehler        return false;
1062f69bb449SAndreas Boehler      return $row['calid'];
1063f69bb449SAndreas Boehler  }
1064f69bb449SAndreas Boehler
1065cb71a62aSAndreas Boehler  /**
1066cb71a62aSAndreas Boehler   * Return a given calendar as ICS feed, i.e. all events in one ICS file.
1067cb71a62aSAndreas Boehler   *
1068cb71a62aSAndreas Boehler   * @param string $caldi The calendar ID to retrieve
1069cb71a62aSAndreas Boehler   *
1070cb71a62aSAndreas Boehler   * @return mixed The calendar events as string or false
1071cb71a62aSAndreas Boehler   */
1072f69bb449SAndreas Boehler  public function getCalendarAsICSFeed($calid)
1073f69bb449SAndreas Boehler  {
1074f69bb449SAndreas Boehler      $calSettings = $this->getCalendarSettings($calid);
1075f69bb449SAndreas Boehler      if($calSettings === false)
1076f69bb449SAndreas Boehler        return false;
1077f69bb449SAndreas Boehler      $events = $this->getAllCalendarEvents($calid);
1078f69bb449SAndreas Boehler      if($events === false)
1079f69bb449SAndreas Boehler        return false;
1080f69bb449SAndreas Boehler
1081f69bb449SAndreas Boehler      $out = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\nCALSCALE:GREGORIAN\nX-WR-CALNAME:";
1082f69bb449SAndreas Boehler      $out .= $calSettings['displayname']."\n";
1083f69bb449SAndreas Boehler      foreach($events as $event)
1084f69bb449SAndreas Boehler      {
1085f69bb449SAndreas Boehler          $out .= rtrim($event['calendardata']);
1086f69bb449SAndreas Boehler          $out .= "\n";
1087f69bb449SAndreas Boehler      }
1088f69bb449SAndreas Boehler      $out .= "END:VCALENDAR\n";
1089f69bb449SAndreas Boehler      return $out;
1090f69bb449SAndreas Boehler  }
1091f69bb449SAndreas Boehler
1092*7c7c6b0bSAndreas Boehler  /**
1093*7c7c6b0bSAndreas Boehler   * Retrieve a configuration option for the plugin
1094*7c7c6b0bSAndreas Boehler   *
1095*7c7c6b0bSAndreas Boehler   * @param string $key The key to query
1096*7c7c6b0bSAndreas Boehler   * @return mised The option set, null if not found
1097*7c7c6b0bSAndreas Boehler   */
1098*7c7c6b0bSAndreas Boehler  public function getConfig($key)
1099*7c7c6b0bSAndreas Boehler  {
1100*7c7c6b0bSAndreas Boehler      return $this->getConf($key);
1101*7c7c6b0bSAndreas Boehler  }
1102*7c7c6b0bSAndreas Boehler
1103a1a3b679SAndreas Boehler}
1104