xref: /plugin/davcal/helper.php (revision 0eebc909f8774f895527f8fa7688b4cafd81a498)
1<?php
2/**
3  * Helper Class for the tagrevisions plugin
4  * This helper does the actual work.
5  *
6  * Configurable in DokuWiki's configuration
7  */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class helper_plugin_davcal extends DokuWiki_Plugin {
13
14  protected $sqlite = null;
15
16  /**
17    * Constructor to load the configuration
18    */
19  public function helper_plugin_davcal() {
20    $this->sqlite =& plugin_load('helper', 'sqlite');
21    if(!$this->sqlite)
22    {
23        msg('This plugin requires the sqlite plugin. Please install it.');
24        return;
25    }
26
27    if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
28    {
29        return;
30    }
31  }
32
33  public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
34  {
35      if(is_null($id))
36      {
37          global $ID;
38          $id = $ID;
39      }
40      if(is_null($userid))
41        $userid = $_SERVER['REMOTE_USER'];
42      $calid = $this->getCalendarIdForPage($id);
43      if($calid === false)
44        return $this->createCalendarForPage($name, $description, $id, $userid);
45
46      $query = "UPDATE calendars SET displayname=".$this->sqlite->quote_string($name).", ".
47               "description=".$this->sqlite->quote_string($description)." WHERE ".
48               "id=".$this->sqlite->quote_string($calid);
49      $res = $this->sqlite->query($query);
50      if($res !== false)
51        return true;
52      return false;
53  }
54
55  public function savePersonalSettings($settings, $userid = null)
56  {
57      if(is_null($userid))
58          $userid = $_SERVER['REMOTE_USER'];
59      $this->sqlite->query("BEGIN TRANSACTION");
60
61      $query = "DELETE FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid);
62      $this->sqlite->query($query);
63
64      foreach($settings as $key => $value)
65      {
66          $query = "INSERT INTO calendarsettings (userid, key, value) VALUES (".
67                   $this->sqlite->quote_string($userid).", ".
68                   $this->sqlite->quote_string($key).", ".
69                   $this->sqlite->quote_string($value).")";
70          $res = $this->sqlite->query($query);
71          if($res === false)
72              return false;
73      }
74      $this->sqlite->query("COMMIT TRANSACTION");
75      return true;
76  }
77
78  public function getPersonalSettings($userid = null)
79  {
80      if(is_null($userid))
81        $userid = $_SERVER['REMOTE_USER'];
82      // Some sane default settings
83      $settings = array(
84        'timezone' => 'local',
85        'weeknumbers' => '0',
86        'workweek' => '0'
87      );
88      $query = "SELECT key, value FROM calendarsettings WHERE userid=".$this->sqlite->quote_string($userid);
89      $res = $this->sqlite->query($query);
90      $arr = $this->sqlite->res2arr($res);
91      foreach($arr as $row)
92      {
93          $settings[$row['key']] = $row['value'];
94      }
95      return $settings;
96  }
97
98  public function getCalendarIdForPage($id = null)
99  {
100      if(is_null($id))
101      {
102          global $ID;
103          $id = $ID;
104      }
105
106      $query = "SELECT calid FROM pagetocalendarmapping WHERE page=".$this->sqlite->quote_string($id);
107      $res = $this->sqlite->query($query);
108      $row = $this->sqlite->res2row($res);
109      if(isset($row['calid']))
110        return $row['calid'];
111      else
112        return false;
113  }
114
115  public function getCalendarIdToPageMapping()
116  {
117      $query = "SELECT calid, page FROM pagetocalendarmapping";
118      $res = $this->sqlite->query($query);
119      $arr = $this->sqlite->res2arr($res);
120      return $arr;
121  }
122
123  public function getCalendarIdsForUser($principalUri)
124  {
125      $user = explode('/', $principalUri);
126      $user = end($user);
127      $mapping = $this->getCalendarIdToPageMapping();
128      $calids = array();
129      foreach($mapping as $row)
130      {
131          $id = $row['calid'];
132          $page = $row['page'];
133          $acl = auth_quickaclcheck($page);
134          if($acl >= AUTH_READ)
135          {
136              $write = $acl > AUTH_READ;
137              $calids[$id] = array('readonly' => !$write);
138          }
139      }
140      return $calids;
141  }
142
143  public function createCalendarForPage($name, $description, $id = null, $userid = null)
144  {
145      if(is_null($id))
146      {
147          global $ID;
148          $id = $ID;
149      }
150      if(is_null($userid))
151          $userid = $_SERVER['REMOTE_USER'];
152      $values = array('principals/'.$userid,
153                      $name,
154                      str_replace(array('/', ' ', ':'), '_', $id),
155                      $description,
156                      'VEVENT,VTODO',
157                      0,
158                      1);
159      $query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) VALUES (".$this->sqlite->quote_and_join($values, ',').");";
160      $res = $this->sqlite->query($query);
161      if($res === false)
162        return false;
163      $query = "SELECT id FROM calendars WHERE principaluri=".$this->sqlite->quote_string($values[0])." AND ".
164               "displayname=".$this->sqlite->quote_string($values[1])." AND ".
165               "uri=".$this->sqlite->quote_string($values[2])." AND ".
166               "description=".$this->sqlite->quote_string($values[3]);
167      $res = $this->sqlite->query($query);
168      $row = $this->sqlite->res2row($res);
169      if(isset($row['id']))
170      {
171          $values = array($id, $row['id']);
172          $query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (".$this->sqlite->quote_and_join($values, ',').")";
173          $res = $this->sqlite->query($query);
174          return ($res !== false);
175      }
176
177      return false;
178  }
179
180  public function addCalendarEntryToCalendarForPage($id, $user, $params)
181  {
182      $settings = $this->getPersonalSettings($user);
183      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
184          $timezone = new \DateTimeZone($settings['timezone']);
185      else
186          $timezone = new \DateTimeZone('UTC');
187      $startDate = explode('-', $params['eventfrom']);
188      $startTime = explode(':', $params['eventfromtime']);
189      $endDate = explode('-', $params['eventto']);
190      $endTime = explode(':', $params['eventtotime']);
191      require_once('vendor/autoload.php');
192      $vcalendar = new \Sabre\VObject\Component\VCalendar();
193      $event = $vcalendar->add('VEVENT');
194      $uuid = \Sabre\VObject\UUIDUtil::getUUID();
195      $event->add('UID', $uuid);
196      $event->summary = $params['eventname'];
197      $description = $params['eventdescription'];
198      if($description !== '')
199        $event->add('DESCRIPTION', $description);
200      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
201      $event->add('DTSTAMP', $dtStamp);
202      $event->add('CREATED', $dtStamp);
203      $event->add('LAST-MODIFIED', $dtStamp);
204      $dtStart = new \DateTime();
205      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
206      if($params['allday'] != '1')
207        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
208      $dtStart->setTimezone($timezone);
209      $dtEnd = new \DateTime();
210      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
211      if($params['allday'] != '1')
212        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
213      $dtEnd->setTimezone($timezone);
214      // According to the VCal spec, we need to add a whole day here
215      if($params['allday'] == '1')
216          $dtEnd->add(new \DateInterval('P1D'));
217      $dtStartEv = $event->add('DTSTART', $dtStart);
218      $dtEndEv = $event->add('DTEND', $dtEnd);
219      if($params['allday'] == '1')
220      {
221          $dtStartEv['VALUE'] = 'DATE';
222          $dtEndEv['VALUE'] = 'DATE';
223      }
224      $calid = $this->getCalendarIdForPage($id);
225      $uri = uniqid('dokuwiki-').'.ics';
226      $now = new DateTime();
227      $eventStr = $vcalendar->serialize();
228
229      $values = array($calid,
230                      $uri,
231                      $eventStr,
232                      $now->getTimestamp(),
233                      'VEVENT',
234                      $event->DTSTART->getDateTime()->getTimeStamp(),
235                      $event->DTEND->getDateTime()->getTimeStamp(),
236                      strlen($eventStr),
237                      md5($eventStr),
238                      uniqid()
239      );
240
241      $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (".$this->sqlite->quote_and_join($values, ',').")";
242      $res = $this->sqlite->query($query);
243      if($res !== false)
244      {
245          $this->updateSyncTokenLog($calid, $uri, 'added');
246          return true;
247      }
248      return false;
249  }
250
251  public function getCalendarSettings($calid)
252  {
253      $query = "SELECT principaluri, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id=".$this->sqlite->quote_string($calid);
254      $res = $this->sqlite->query($query);
255      $row = $this->sqlite->res2row($res);
256      return $row;
257  }
258
259  public function getEventsWithinDateRange($id, $user, $startDate, $endDate)
260  {
261      $settings = $this->getPersonalSettings($user);
262      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
263          $timezone = new \DateTimeZone($settings['timezone']);
264      else
265          $timezone = new \DateTimeZone('UTC');
266      $data = array();
267      require_once('vendor/autoload.php');
268      $calid = $this->getCalendarIdForPage($id);
269      $startTs = new \DateTime($startDate);
270      $endTs = new \DateTime($endDate);
271      $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE (calendarid=".
272                $this->sqlite->quote_string($calid)." AND firstoccurence > ".
273                $this->sqlite->quote_string($startTs->getTimestamp())." AND firstoccurence < ".
274                $this->sqlite->quote_string($endTs->getTimestamp()).") OR (calendarid=".
275                $this->sqlite->quote_string($calid)." AND lastoccurence > ".
276                $this->sqlite->quote_string($startTs->getTimestamp())." AND lastoccurence < ".
277                $this->sqlite->quote_string($endTs->getTimestamp()).")";
278      $res = $this->sqlite->query($query);
279      $arr = $this->sqlite->res2arr($res);
280      foreach($arr as $row)
281      {
282          if(isset($row['calendardata']))
283          {
284              $entry = array();
285              $vcal = \Sabre\VObject\Reader::read($row['calendardata']);
286              $start = $vcal->VEVENT->DTSTART;
287              if($start !== null)
288              {
289                $dtStart = $start->getDateTime();
290                $dtStart->setTimezone($timezone);
291                $entry['start'] = $dtStart->format(\DateTime::ATOM);
292                if($start['VALUE'] == 'DATE')
293                  $entry['allDay'] = true;
294                else
295                  $entry['allDay'] = false;
296              }
297              $end = $vcal->VEVENT->DTEND;
298              if($end !== null)
299              {
300                $dtEnd = $end->getDateTime();
301                $dtEnd->setTimezone($timezone);
302                $entry['end'] = $dtEnd->format(\DateTime::ATOM);
303              }
304              $description = $vcal->VEVENT->DESCRIPTION;
305              if($description !== null)
306                $entry['description'] = (string)$description;
307              else
308                $entry['description'] = '';
309              $entry['title'] = (string)$vcal->VEVENT->summary;
310              $entry['id'] = $row['uid'];
311              $data[] = $entry;
312          }
313      }
314      return $data;
315  }
316
317  public function getEventWithUid($uid)
318  {
319      $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid=".
320                $this->sqlite->quote_string($uid);
321      $res = $this->sqlite->query($query);
322      $row = $this->sqlite->res2row($res);
323      return $row;
324  }
325
326  public function editCalendarEntryForPage($id, $user, $params)
327  {
328      $settings = $this->getPersonalSettings($user);
329      if($settings['timezone'] !== '' && $settings['timezone'] !== 'local')
330          $timezone = new \DateTimeZone($settings['timezone']);
331      else
332          $timezone = new \DateTimeZone('UTC');
333      $startDate = explode('-', $params['eventfrom']);
334      $startTime = explode(':', $params['eventfromtime']);
335      $endDate = explode('-', $params['eventto']);
336      $endTime = explode(':', $params['eventtotime']);
337      $uid = $params['uid'];
338      $event = $this->getEventWithUid($uid);
339      require_once('vendor/autoload.php');
340      if(!isset($event['calendardata']))
341        return false;
342      $uri = $event['uri'];
343      $calid = $event['calendarid'];
344      $vcal = \Sabre\VObject\Reader::read($event['calendardata']);
345      $vevent = $vcal->VEVENT;
346      $vevent->summary = $params['eventname'];
347      $dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
348      $description = $params['eventdescription'];
349      $vevent->remove('DESCRIPTION');
350      $vevent->remove('DTSTAMP');
351      $vevent->remove('LAST-MODIFIED');
352      $vevent->add('DTSTAMP', $dtStamp);
353      $vevent->add('LAST-MODIFIED', $dtStamp);
354      if($description !== '')
355        $vevent->add('DESCRIPTION', $description);
356      $dtStart = new \DateTime();
357      $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
358      if($params['allday'] != '1')
359        $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
360      $dtStart->setTimezone($timezone);
361      $dtEnd = new \DateTime();
362      $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
363      if($params['allday'] != '1')
364        $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
365      $dtEnd->setTimezone($timezone);
366      // According to the VCal spec, we need to add a whole day here
367      if($params['allday'] == '1')
368          $dtEnd->add(new \DateInterval('P1D'));
369      $vevent->remove('DTSTART');
370      $vevent->remove('DTEND');
371      $dtStartEv = $vevent->add('DTSTART', $dtStart);
372      $dtEndEv = $vevent->add('DTEND', $dtEnd);
373      if($params['allday'] == '1')
374      {
375          $dtStartEv['VALUE'] = 'DATE';
376          $dtEndEv['VALUE'] = 'DATE';
377      }
378      $now = new DateTime();
379      $eventStr = $vcal->serialize();
380
381      $query = "UPDATE calendarobjects SET calendardata=".$this->sqlite->quote_string($eventStr).
382               ", lastmodified=".$this->sqlite->quote_string($now->getTimestamp()).
383               ", firstoccurence=".$this->sqlite->quote_string($dtStart->getTimestamp()).
384               ", lastoccurence=".$this->sqlite->quote_string($dtEnd->getTimestamp()).
385               ", size=".strlen($eventStr).
386               ", etag=".$this->sqlite->quote_string(md5($eventStr)).
387               " WHERE uid=".$this->sqlite->quote_string($uid);
388      $res = $this->sqlite->query($query);
389      if($res !== false)
390      {
391          $this->updateSyncTokenLog($calid, $uri, 'modified');
392          return true;
393      }
394      return false;
395  }
396
397  public function deleteCalendarEntryForPage($id, $params)
398  {
399      $uid = $params['uid'];
400      $event = $this->getEventWithUid($uid);
401      $calid = $event['calendarid'];
402      $uri = $event['uri'];
403      $query = "DELETE FROM calendarobjects WHERE uid=".$this->sqlite->quote_string($uid);
404      $res = $this->sqlite->query($query);
405      if($res !== false)
406      {
407          $this->updateSyncTokenLog($calid, $uri, 'deleted');
408      }
409      return true;
410  }
411
412  public function getSyncTokenForCalendar($calid)
413  {
414      $row = $this->getCalendarSettings($calid);
415      if(isset($row['synctoken']))
416          return $row['synctoken'];
417      return false;
418  }
419
420  public function operationNameToOperation($operationName)
421  {
422      switch($operationName)
423      {
424          case 'added':
425              return 1;
426          break;
427          case 'modified':
428              return 2;
429          break;
430          case 'deleted':
431              return 3;
432          break;
433      }
434      return false;
435  }
436
437  private function updateSyncTokenLog($calid, $uri, $operation)
438  {
439      $currentToken = $this->getSyncTokenForCalendar($calid);
440      $operationCode = $this->operationNameToOperation($operation);
441      if(($operationCode === false) || ($currentToken === false))
442          return false;
443      $values = array($uri,
444                      $currentToken,
445                      $calid,
446                      $operationCode
447      );
448      $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(".
449               $this->sqlite->quote_and_join($values, ',').")";
450      $res = $this->sqlite->query($query);
451      if($res === false)
452        return false;
453      $currentToken++;
454      $query = "UPDATE calendars SET synctoken=".$this->sqlite->quote_string($currentToken)." WHERE id=".
455               $this->sqlite->quote_string($calid);
456      $res = $this->sqlite->query($query);
457      return ($res !== false);
458  }
459
460  public function getSyncUrlForPage($id, $user = null)
461  {
462      if(is_null($user))
463        $user = $_SERVER['REMOTE_USER'];
464
465      $calid = $this->getCalendarIdForPage($id);
466      if($calid === false)
467        return false;
468
469      $calsettings = $this->getCalendarSettings($calid);
470      if(!isset($calsettings['uri']))
471        return false;
472
473      $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
474      return $syncurl;
475  }
476
477}
478