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 $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 198 $event->add('DTSTAMP', $dtStamp); 199 $event->add('CREATED', $dtStamp); 200 $event->add('LAST-MODIFIED', $dtStamp); 201 $dtStart = new \DateTime(); 202 $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 203 if($params['allday'] != '1') 204 $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 205 $dtStart->setTimezone($timezone); 206 $dtEnd = new \DateTime(); 207 $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 208 if($params['allday'] != '1') 209 $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 210 $dtEnd->setTimezone($timezone); 211 // According to the VCal spec, we need to add a whole day here 212 if($params['allday'] == '1') 213 $dtEnd->add(new \DateInterval('P1D')); 214 $dtStartEv = $event->add('DTSTART', $dtStart); 215 $dtEndEv = $event->add('DTEND', $dtEnd); 216 if($params['allday'] == '1') 217 { 218 $dtStartEv['VALUE'] = 'DATE'; 219 $dtEndEv['VALUE'] = 'DATE'; 220 } 221 $calid = $this->getCalendarIdForPage($id); 222 $uri = uniqid('dokuwiki-').'.ics'; 223 $now = new DateTime(); 224 $eventStr = $vcalendar->serialize(); 225 226 $values = array($calid, 227 $uri, 228 $eventStr, 229 $now->getTimestamp(), 230 'VEVENT', 231 $event->DTSTART->getDateTime()->getTimeStamp(), 232 $event->DTEND->getDateTime()->getTimeStamp(), 233 strlen($eventStr), 234 md5($eventStr), 235 uniqid() 236 ); 237 238 $query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (".$this->sqlite->quote_and_join($values, ',').")"; 239 $res = $this->sqlite->query($query); 240 if($res !== false) 241 { 242 $this->updateSyncTokenLog($calid, $uri, 'added'); 243 return true; 244 } 245 return false; 246 } 247 248 public function getCalendarSettings($calid) 249 { 250 $query = "SELECT principaluri, displayname, uri, description, components, transparent, synctoken FROM calendars WHERE id=".$this->sqlite->quote_string($calid); 251 $res = $this->sqlite->query($query); 252 $row = $this->sqlite->res2row($res); 253 return $row; 254 } 255 256 public function getEventsWithinDateRange($id, $user, $startDate, $endDate) 257 { 258 $settings = $this->getPersonalSettings($user); 259 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 260 $timezone = new \DateTimeZone($settings['timezone']); 261 else 262 $timezone = new \DateTimeZone('UTC'); 263 $data = array(); 264 require_once('vendor/autoload.php'); 265 $calid = $this->getCalendarIdForPage($id); 266 $startTs = new \DateTime($startDate); 267 $endTs = new \DateTime($endDate); 268 $query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE (calendarid=". 269 $this->sqlite->quote_string($calid)." AND firstoccurence > ". 270 $this->sqlite->quote_string($startTs->getTimestamp())." AND firstoccurence < ". 271 $this->sqlite->quote_string($endTs->getTimestamp()).") OR (calendarid=". 272 $this->sqlite->quote_string($calid)." AND lastoccurence > ". 273 $this->sqlite->quote_string($startTs->getTimestamp())." AND lastoccurence < ". 274 $this->sqlite->quote_string($endTs->getTimestamp()).")"; 275 $res = $this->sqlite->query($query); 276 $arr = $this->sqlite->res2arr($res); 277 foreach($arr as $row) 278 { 279 if(isset($row['calendardata'])) 280 { 281 $entry = array(); 282 $vcal = \Sabre\VObject\Reader::read($row['calendardata']); 283 $start = $vcal->VEVENT->DTSTART; 284 if($start !== null) 285 { 286 $dtStart = $start->getDateTime(); 287 $dtStart->setTimezone($timezone); 288 $entry['start'] = $dtStart->format(\DateTime::ATOM); 289 if($start['VALUE'] == 'DATE') 290 $entry['allDay'] = true; 291 else 292 $entry['allDay'] = false; 293 } 294 $end = $vcal->VEVENT->DTEND; 295 if($end !== null) 296 { 297 $dtEnd = $end->getDateTime(); 298 $dtEnd->setTimezone($timezone); 299 // Subtract the plus one day that was added earlier 300 //if($entry['allDay'] === true) 301 // $dtEnd->sub(new \DateInterval('P1D')); 302 $entry['end'] = $dtEnd->format(\DateTime::ATOM); 303 } 304 $entry['title'] = (string)$vcal->VEVENT->summary; 305 $entry['id'] = $row['uid']; 306 $data[] = $entry; 307 } 308 } 309 return $data; 310 } 311 312 public function getEventWithUid($uid) 313 { 314 $query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid=". 315 $this->sqlite->quote_string($uid); 316 $res = $this->sqlite->query($query); 317 $row = $this->sqlite->res2row($res); 318 return $row; 319 } 320 321 public function editCalendarEntryForPage($id, $user, $params) 322 { 323 $settings = $this->getPersonalSettings($user); 324 if($settings['timezone'] !== '' && $settings['timezone'] !== 'local') 325 $timezone = new \DateTimeZone($settings['timezone']); 326 else 327 $timezone = new \DateTimeZone('UTC'); 328 $startDate = explode('-', $params['eventfrom']); 329 $startTime = explode(':', $params['eventfromtime']); 330 $endDate = explode('-', $params['eventto']); 331 $endTime = explode(':', $params['eventtotime']); 332 $uid = $params['uid']; 333 $event = $this->getEventWithUid($uid); 334 require_once('vendor/autoload.php'); 335 if(!isset($event['calendardata'])) 336 return false; 337 $uri = $event['uri']; 338 $calid = $event['calendarid']; 339 $vcal = \Sabre\VObject\Reader::read($event['calendardata']); 340 $vevent = $vcal->VEVENT; 341 $vevent->summary = $params['eventname']; 342 $dtStamp = new \DateTime(null, new \DateTimeZone('UTC')); 343 $vevent->remove('DTSTAMP'); 344 $vevent->remove('LAST-MODIFIED'); 345 $vevent->add('DTSTAMP', $dtStamp); 346 $vevent->add('LAST-MODIFIED', $dtStamp); 347 $dtStart = new \DateTime(); 348 $dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2])); 349 if($params['allday'] != '1') 350 $dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0); 351 $dtStart->setTimezone($timezone); 352 $dtEnd = new \DateTime(); 353 $dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2])); 354 if($params['allday'] != '1') 355 $dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0); 356 $dtEnd->setTimezone($timezone); 357 // According to the VCal spec, we need to add a whole day here 358 if($params['allday'] == '1') 359 $dtEnd->add(new \DateInterval('P1D')); 360 $vevent->remove('DTSTART'); 361 $vevent->remove('DTEND'); 362 $dtStartEv = $vevent->add('DTSTART', $dtStart); 363 $dtEndEv = $vevent->add('DTEND', $dtEnd); 364 if($params['allday'] == '1') 365 { 366 $dtStartEv['VALUE'] = 'DATE'; 367 $dtEndEv['VALUE'] = 'DATE'; 368 } 369 $now = new DateTime(); 370 $eventStr = $vcal->serialize(); 371 372 $query = "UPDATE calendarobjects SET calendardata=".$this->sqlite->quote_string($eventStr). 373 ", lastmodified=".$this->sqlite->quote_string($now->getTimestamp()). 374 ", firstoccurence=".$this->sqlite->quote_string($dtStart->getTimestamp()). 375 ", lastoccurence=".$this->sqlite->quote_string($dtEnd->getTimestamp()). 376 ", size=".strlen($eventStr). 377 ", etag=".$this->sqlite->quote_string(md5($eventStr)). 378 " WHERE uid=".$this->sqlite->quote_string($uid); 379 $res = $this->sqlite->query($query); 380 if($res !== false) 381 { 382 $this->updateSyncTokenLog($calid, $uri, 'modified'); 383 return true; 384 } 385 return false; 386 } 387 388 public function deleteCalendarEntryForPage($id, $params) 389 { 390 $uid = $params['uid']; 391 $event = $this->getEventWithUid($uid); 392 $calid = $event['calendarid']; 393 $uri = $event['uri']; 394 $query = "DELETE FROM calendarobjects WHERE uid=".$this->sqlite->quote_string($uid); 395 $res = $this->sqlite->query($query); 396 if($res !== false) 397 { 398 $this->updateSyncTokenLog($calid, $uri, 'deleted'); 399 } 400 return true; 401 } 402 403 public function getSyncTokenForCalendar($calid) 404 { 405 $row = $this->getCalendarSettings($calid); 406 if(isset($row['synctoken'])) 407 return $row['synctoken']; 408 return false; 409 } 410 411 public function operationNameToOperation($operationName) 412 { 413 switch($operationName) 414 { 415 case 'added': 416 return 1; 417 break; 418 case 'modified': 419 return 2; 420 break; 421 case 'deleted': 422 return 3; 423 break; 424 } 425 return false; 426 } 427 428 private function updateSyncTokenLog($calid, $uri, $operation) 429 { 430 $currentToken = $this->getSyncTokenForCalendar($calid); 431 $operationCode = $this->operationNameToOperation($operation); 432 if(($operationCode === false) || ($currentToken === false)) 433 return false; 434 $values = array($uri, 435 $currentToken, 436 $calid, 437 $operationCode 438 ); 439 $query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(". 440 $this->sqlite->quote_and_join($values, ',').")"; 441 $res = $this->sqlite->query($query); 442 if($res === false) 443 return false; 444 $currentToken++; 445 $query = "UPDATE calendars SET synctoken=".$this->sqlite->quote_string($currentToken)." WHERE id=". 446 $this->sqlite->quote_string($calid); 447 $res = $this->sqlite->query($query); 448 return ($res !== false); 449 } 450 451 public function getSyncUrlForPage($id, $user = null) 452 { 453 if(is_null($user)) 454 $user = $_SERVER['REMOTE_USER']; 455 456 $calid = $this->getCalendarIdForPage($id); 457 if($calid === false) 458 return false; 459 460 $calsettings = $this->getCalendarSettings($calid); 461 if(!isset($calsettings['uri'])) 462 return false; 463 464 $syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri']; 465 return $syncurl; 466 } 467 468} 469