diff --git a/all.css b/all.css
--- a/all.css
+++ b/all.css
@@ -1,43 +1,184 @@
@import "fullcalendar-3.2.0/fullcalendar.less";
@import "datetimepicker-2.4.5/jquery.datetimepicker.less";
/* modal windows */
#dw_davcal__edit,
#dw_davcal__settings,
#dw_davcal__confirm {
position: absolute;
background-color: #fff;
color: #000;
z-index: 20;
overflow: hidden;
}
#dw_davcal__edit .ui-dialog-content,
#dw_davcal__confirm .ui-dialog-content,
#dw_davcal__settings .ui-dialog-content, {
padding-left: 0;
padding-right: 0;
}
.dw_davcal__date {
width: 90px;
}
.dw_davcal__time {
width: 50px;
}
.dw_davcal__text {
width: 220px;
}
#dw_davcal__editevent_attachments td {
border: 0;
}
#dokuwiki__pagetools {
z-index: 10;
}
+
+.davcalevents
+{
+ margin:50px auto 0 auto;
+ width:100%;
+ align-items:space-around;
+ max-width:1200px;
+ display:block;
+}
+.davcalevents .tile
+{
+ width:280px;
+ height:200px;
+ margin:10px;
+
+ display:inline-block;
+ background-size:cover;
+ position:relative;
+ cursor:pointer;
+ transition: all 0.4s ease-out;
+ box-shadow: 0px 35px 77px -17px rgba(0,0,0,0.44);
+ overflow:hidden;
+ color:white;
+ font-family:'Roboto';
+
+}
+.davcalevents .tile img
+{
+ height:100%;
+ width:100%;
+ position:absolute;
+ top:0;
+ left:0;
+ z-index:0;
+ transition: all 0.4s ease-out;
+}
+.davcalevents .tile .text
+{
+/* z-index:99; */
+ position:absolute;
+ padding:30px;
+ height:calc(100% - 60px);
+}
+.davcalevents .tile h1
+{
+
+ font-weight:300;
+ margin:0;
+ text-shadow: 2px 2px 10px rgba(0,0,0,0.3);
+}
+.davcalevents .tile h2
+{
+ font-weight:100;
+ margin:5px 0 0 0;
+ font-style:italic;
+ /* transform: translateX(200px); */
+}
+.davcalevents .tile p
+{
+ font-weight:300;
+ margin:20px 0 0 0;
+ line-height: 25px;
+/* opacity:0; */
+ transform: translateX(-200px);
+ transition-delay: 0.2s;
+}
+.davcalevents .animate-text
+{
+ opacity:0;
+ transition: all 0.6s ease-in-out;
+}
+.davcalevents .tile:hover
+{
+/* background-color:#99aeff; */
+box-shadow: 0px 35px 77px -17px rgba(0,0,0,0.64);
+ transform:scale(1.05);
+}
+.davcalevents .tile:hover img
+{
+ opacity: 0.2;
+}
+.davcalevents .tile:hover .animate-text
+{
+ transform:translateX(0);
+ opacity:1;
+}
+.davcalevents .dots
+{
+ position:absolute;
+ bottom:20px;
+ right:30px;
+ margin: 0 auto;
+ width:30px;
+ height:30px;
+ color:currentColor;
+ display:flex;
+ flex-direction:column;
+ align-items:center;
+ justify-content:space-around;
+
+}
+
+.davcalevents .dots span
+{
+ width: 5px;
+ height:5px;
+ background-color: currentColor;
+ border-radius: 50%;
+ display:block;
+ opacity:0;
+ transition: transform 0.4s ease-out, opacity 0.5s ease;
+ transform: translateY(30px);
+
+}
+
+.davcalevents .tile:hover span
+{
+ opacity:1;
+ transform:translateY(0px);
+}
+
+.davcalevents .dots span:nth-child(1)
+{
+ transition-delay: 0.05s;
+}
+.davcalevents .dots span:nth-child(2)
+{
+ transition-delay: 0.1s;
+}
+.davcalevents .dots span:nth-child(3)
+{
+ transition-delay: 0.15s;
+}
+
+
+@media (max-width: 1000px) {
+ .davcalevents {
+ flex-direction: column;
+ width:400px;
+ }
+}
\ No newline at end of file
diff --git a/helper.php b/helper.php
--- a/helper.php
+++ b/helper.php
@@ -1,1944 +1,1950 @@
sqlite === null)
{
$this->sqlite = plugin_load('helper', 'sqlite');
if(!$this->sqlite)
{
dbglog('This plugin requires the sqlite plugin. Please install it.');
msg('This plugin requires the sqlite plugin. Please install it.', -1);
return false;
}
if(!$this->sqlite->init('davcal', DOKU_PLUGIN.'davcal/db/'))
{
$this->sqlite = null;
dbglog('Error initialising the SQLite DB for DAVCal');
return false;
}
}
return $this->sqlite;
}
/**
* Retrieve meta data for a given page
*
* @param string $id optional The page ID
* @return array The metadata
*/
private function getMeta($id = null) {
global $ID;
global $INFO;
if ($id === null) $id = $ID;
if($ID === $id && $INFO['meta']) {
$meta = $INFO['meta'];
} else {
$meta = p_get_metadata($id);
}
return $meta;
}
/**
* Retrieve the meta data for a given page
*
* @param string $id optional The page ID
* @return array with meta data
*/
public function getCalendarMetaForPage($id = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
$meta = $this->getMeta($id);
if(isset($meta['plugin_davcal']))
return $meta['plugin_davcal'];
else
return array();
}
/**
* Check the permission of a user for a given calendar ID
*
* @param string $id The calendar ID to check
* @return int AUTH_* constants
*/
public function checkCalendarPermission($id)
{
if(strpos($id, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
return AUTH_NONE;
$connectionId = str_replace('webdav://', '', $id);
$settings = $wdc->getConnection($connectionId);
if($settings === false)
return AUTH_NONE;
if($settings['write'] === '1')
return AUTH_CREATE;
return AUTH_READ;
}
else
{
$calid = $this->getCalendarIdForPage($id);
// We return AUTH_READ if the calendar does not exist. This makes
// davcal happy when there are just included calendars
if($calid === false)
return AUTH_READ;
return auth_quickaclcheck($id);
}
}
/**
* Filter calendar pages and return only those where the current
* user has at least read permission.
*
* @param array $calendarPages Array with calendar pages to check
* @return array with filtered calendar pages
*/
public function filterCalendarPagesByUserPermission($calendarPages)
{
$retList = array();
foreach($calendarPages as $page => $data)
{
// WebDAV Connections are always readable
if(strpos($page, 'webdav://') === 0)
{
$retList[$page] = $data;
}
elseif(auth_quickaclcheck($page) >= AUTH_READ)
{
$retList[$page] = $data;
}
}
return $retList;
}
/**
* Get all calendar pages used by a given page
* based on the stored metadata
*
* @param string $id optional The page id
* @return mixed The pages as array or false
*/
public function getCalendarPagesByMeta($id = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
$meta = $this->getCalendarMetaForPage($id);
if(isset($meta['id']))
{
// Filter the list of pages by permission
$pages = $this->filterCalendarPagesByUserPermission($meta['id']);
if(empty($pages))
return false;
return $pages;
}
return false;
}
/**
* Get a list of calendar names/pages/ids/colors
* for an array of page ids
*
* @param array $calendarPages The calendar pages to retrieve
* @return array The list
*/
public function getCalendarMapForIDs($calendarPages)
{
$data = array();
foreach($calendarPages as $page => $color)
{
if(strpos($page, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
continue;
$connectionId = str_replace('webdav://', '', $page);
$settings = $wdc->getConnection($connectionId);
if($settings === false)
continue;
$name = $settings['displayname'];
$write = ($settings['write'] === '1');
$calid = $connectionId;
$color = '#3a87ad';
}
else
{
$calid = $this->getCalendarIdForPage($page);
if($calid !== false)
{
$settings = $this->getCalendarSettings($calid);
$name = $settings['displayname'];
$color = $settings['calendarcolor'];
$write = (auth_quickaclcheck($page) > AUTH_READ);
}
else
{
continue;
}
}
$data[] = array('name' => $name, 'page' => $page, 'calid' => $calid,
'color' => $color, 'write' => $write);
}
return $data;
}
/**
* Get the saved calendar color for a given page.
*
* @param string $id optional The page ID
* @return mixed The color on success, otherwise false
*/
public function getCalendarColorForPage($id = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
return $this->getCalendarColorForCalendar($calid);
}
/**
* Get the saved calendar color for a given calendar ID.
*
* @param string $id optional The calendar ID
* @return mixed The color on success, otherwise false
*/
public function getCalendarColorForCalendar($calid)
{
if(isset($this->cachedValues['calendarcolor'][$calid]))
return $this->cachedValues['calendarcolor'][$calid];
$row = $this->getCalendarSettings($calid);
if(!isset($row['calendarcolor']))
return false;
$color = $row['calendarcolor'];
$this->cachedValues['calendarcolor'][$calid] = $color;
return $color;
}
/**
* Get the user's principal URL for iOS sync
* @param string $user the user name
* @return the URL to the principal sync
*/
public function getPrincipalUrlForUser($user)
{
if(is_null($user))
return false;
$url = DOKU_URL.'lib/plugins/davcal/calendarserver.php/principals/'.$user;
return $url;
}
/**
* Set the calendar color for a given page.
*
* @param string $color The color definition
* @param string $id optional The page ID
* @return boolean True on success, otherwise false
*/
public function setCalendarColorForPage($color, $id = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET calendarcolor = ? ".
" WHERE id = ?";
$res = $sqlite->query($query, $color, $calid);
if($res !== false)
{
$this->cachedValues['calendarcolor'][$calid] = $color;
return true;
}
return false;
}
/**
* Set the calendar name and description for a given page with a given
* page id.
* If the calendar doesn't exist, the calendar is created!
*
* @param string $name The name of the new calendar
* @param string $description The description of the new calendar
* @param string $id (optional) The ID of the page
* @param string $userid The userid of the creating user
*
* @return boolean True on success, otherwise false.
*/
public function setCalendarNameForPage($name, $description, $id = null, $userid = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
if(is_null($userid))
{
if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
{
$userid = $_SERVER['REMOTE_USER'];
}
else
{
$userid = uniqid('davcal-');
}
}
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return $this->createCalendarForPage($name, $description, $id, $userid);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET displayname = ?, description = ? WHERE id = ?";
$res = $sqlite->query($query, $name, $description, $calid);
if($res !== false)
return true;
return false;
}
/**
* Update a calendar's displayname
*
* @param int $calid The calendar's ID
* @param string $name The new calendar name
*
* @return boolean True on success, otherwise false
*/
public function updateCalendarName($calid, $name)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET displayname = ? WHERE id = ?";
$res = $sqlite->query($query, $calid, $name);
if($res !== false)
{
$this->updateSyncTokenLog($calid, '', 'modified');
return true;
}
return false;
}
/**
* Update the calendar description
*
* @param int $calid The calendar's ID
* @param string $description The new calendar's description
*
* @return boolean True on success, otherwise false
*/
public function updateCalendarDescription($calid, $description)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET description = ? WHERE id = ?";
$res = $sqlite->query($query, $calid, $description);
if($res !== false)
{
$this->updateSyncTokenLog($calid, '', 'modified');
return true;
}
return false;
}
/**
* Update a calendar's timezone information
*
* @param int $calid The calendar's ID
* @param string $timezone The new timezone to set
*
* @return boolean True on success, otherwise false
*/
public function updateCalendarTimezone($calid, $timezone)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET timezone = ? WHERE id = ?";
$res = $sqlite->query($query, $calid, $timezone);
if($res !== false)
{
$this->updateSyncTokenLog($calid, '', 'modified');
return true;
}
return false;
}
/**
* Save the personal settings to the SQLite database 'calendarsettings'.
*
* @param array $settings The settings array to store
* @param string $userid (optional) The userid to store
*
* @param boolean True on success, otherwise false
*/
public function savePersonalSettings($settings, $userid = null)
{
if(is_null($userid))
{
if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
{
$userid = $_SERVER['REMOTE_USER'];
}
else
{
return false;
}
}
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$sqlite->query("BEGIN TRANSACTION");
$query = "DELETE FROM calendarsettings WHERE userid = ?";
$sqlite->query($query, $userid);
foreach($settings as $key => $value)
{
$query = "INSERT INTO calendarsettings (userid, key, value) VALUES (?, ?, ?)";
$res = $sqlite->query($query, $userid, $key, $value);
if($res === false)
return false;
}
$sqlite->query("COMMIT TRANSACTION");
$this->cachedValues['settings'][$userid] = $settings;
return true;
}
/**
* Retrieve the settings array for a given user id.
* Some sane defaults are returned, currently:
*
* timezone => local
* weeknumbers => 0
* workweek => 0
*
* @param string $userid (optional) The user id to retrieve
*
* @return array The settings array
*/
public function getPersonalSettings($userid = null)
{
// Some sane default settings
$settings = array(
'timezone' => $this->getConf('timezone'),
'weeknumbers' => $this->getConf('weeknumbers'),
'workweek' => $this->getConf('workweek'),
'monday' => $this->getConf('monday'),
'timeformat' => $this->getConf('timeformat')
);
if(is_null($userid))
{
if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
{
$userid = $_SERVER['REMOTE_USER'];
}
else
{
return $settings;
}
}
$sqlite = $this->getDB();
if(!$sqlite)
return false;
if(isset($this->cachedValues['settings'][$userid]))
return $this->cachedValues['settings'][$userid];
$query = "SELECT key, value FROM calendarsettings WHERE userid = ?";
$res = $sqlite->query($query, $userid);
$arr = $sqlite->res2arr($res);
foreach($arr as $row)
{
$settings[$row['key']] = $row['value'];
}
$this->cachedValues['settings'][$userid] = $settings;
return $settings;
}
/**
* Retrieve the calendar ID based on a page ID from the SQLite table
* 'pagetocalendarmapping'.
*
* @param string $id (optional) The page ID to retrieve the corresponding calendar
*
* @return mixed the ID on success, otherwise false
*/
public function getCalendarIdForPage($id = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
if(isset($this->cachedValues['calid'][$id]))
return $this->cachedValues['calid'][$id];
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calid FROM pagetocalendarmapping WHERE page = ?";
$res = $sqlite->query($query, $id);
$row = $sqlite->res2row($res);
if(isset($row['calid']))
{
$calid = $row['calid'];
$this->cachedValues['calid'] = $calid;
return $calid;
}
return false;
}
/**
* Retrieve the complete calendar id to page mapping.
* This is necessary to be able to retrieve a list of
* calendars for a given user and check the access rights.
*
* @return array The mapping array
*/
public function getCalendarIdToPageMapping()
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calid, page FROM pagetocalendarmapping";
$res = $sqlite->query($query);
$arr = $sqlite->res2arr($res);
return $arr;
}
/**
* Retrieve all calendar IDs a given user has access to.
* The user is specified by the principalUri, so the
* user name is actually split from the URI component.
*
* Access rights are checked against DokuWiki's ACL
* and applied accordingly.
*
* @param string $principalUri The principal URI to work on
*
* @return array An associative array of calendar IDs
*/
public function getCalendarIdsForUser($principalUri)
{
global $auth;
$user = explode('/', $principalUri);
$user = end($user);
$mapping = $this->getCalendarIdToPageMapping();
$calids = array();
$ud = $auth->getUserData($user);
$groups = $ud['grps'];
foreach($mapping as $row)
{
$id = $row['calid'];
$enabled = $this->getCalendarStatus($id);
if($enabled == false)
continue;
$page = $row['page'];
$acl = auth_aclcheck($page, $user, $groups);
if($acl >= AUTH_READ)
{
$write = $acl > AUTH_READ;
$calids[$id] = array('readonly' => !$write);
}
}
return $calids;
}
/**
* Create a new calendar for a given page ID and set name and description
* accordingly. Also update the pagetocalendarmapping table on success.
*
* @param string $name The calendar's name
* @param string $description The calendar's description
* @param string $id (optional) The page ID to work on
* @param string $userid (optional) The user ID that created the calendar
*
* @return boolean True on success, otherwise false
*/
public function createCalendarForPage($name, $description, $id = null, $userid = null)
{
if(is_null($id))
{
global $ID;
$id = $ID;
}
if(is_null($userid))
{
if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
{
$userid = $_SERVER['REMOTE_USER'];
}
else
{
$userid = uniqid('davcal-');
}
}
$values = array('principals/'.$userid,
$name,
str_replace(array('/', ' ', ':'), '_', $id),
$description,
'VEVENT,VTODO',
0,
1);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "INSERT INTO calendars (principaluri, displayname, uri, description, components, transparent, synctoken) ".
"VALUES (?, ?, ?, ?, ?, ?, ?)";
$res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6]);
if($res === false)
return false;
// Get the new calendar ID
$query = "SELECT id FROM calendars WHERE principaluri = ? AND displayname = ? AND ".
"uri = ? AND description = ?";
$res = $sqlite->query($query, $values[0], $values[1], $values[2], $values[3]);
$row = $sqlite->res2row($res);
// Update the pagetocalendarmapping table with the new calendar ID
if(isset($row['id']))
{
$query = "INSERT INTO pagetocalendarmapping (page, calid) VALUES (?, ?)";
$res = $sqlite->query($query, $id, $row['id']);
return ($res !== false);
}
return false;
}
/**
* Add a new calendar entry to the given calendar. Calendar data is
* specified as ICS file, thus it needs to be parsed first.
*
* This is mainly needed for the sync support.
*
* @param int $calid The calendar's ID
* @param string $uri The new object URI
* @param string $ics The ICS file
*
* @return mixed The etag.
*/
public function addCalendarEntryToCalendarByICS($calid, $uri, $ics)
{
$extraData = $this->getDenormalizedData($ics);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)";
$res = $sqlite->query($query,
$calid,
$uri,
$ics,
time(),
$extraData['etag'],
$extraData['size'],
$extraData['componentType'],
$extraData['firstOccurence'],
$extraData['lastOccurence'],
$extraData['uid']);
// If successfully, update the sync token database
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'added');
}
return $extraData['etag'];
}
/**
* Edit a calendar entry by providing a new ICS file. This is mainly
* needed for the sync support.
*
* @param int $calid The calendar's IS
* @param string $uri The object's URI to modify
* @param string $ics The new object's ICS file
*/
public function editCalendarEntryToCalendarByICS($calid, $uri, $ics)
{
$extraData = $this->getDenormalizedData($ics);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?";
$res = $sqlite->query($query,
$ics,
time(),
$extraData['etag'],
$extraData['size'],
$extraData['componentType'],
$extraData['firstOccurence'],
$extraData['lastOccurence'],
$extraData['uid'],
$calid,
$uri
);
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'modified');
}
return $extraData['etag'];
}
/**
* Add a new iCal entry for a given page, i.e. a given calendar.
*
* The parameter array needs to contain
* detectedtz => The timezone as detected by the browser
* currenttz => The timezone in use by the calendar
* eventfrom => The event's start date
* eventfromtime => The event's start time
* eventto => The event's end date
* eventtotime => The event's end time
* eventname => The event's name
* eventdescription => The event's description
*
* @param string $id The page ID to work on
* @param string $user The user who created the calendar
* @param string $params A parameter array with values to create
*
* @return boolean True on success, otherwise false
*/
public function addCalendarEntryToCalendarForPage($id, $user, $params)
{
if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
$timezone = new \DateTimeZone($params['currenttz']);
elseif($params['currenttz'] === 'local')
$timezone = new \DateTimeZone($params['detectedtz']);
else
$timezone = new \DateTimeZone('UTC');
// Retrieve dates from settings
$startDate = explode('-', $params['eventfrom']);
$startTime = explode(':', $params['eventfromtime']);
$endDate = explode('-', $params['eventto']);
$endTime = explode(':', $params['eventtotime']);
// Load SabreDAV
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
$vcalendar = new \Sabre\VObject\Component\VCalendar();
// Add VCalendar, UID and Event Name
$event = $vcalendar->add('VEVENT');
$uuid = \Sabre\VObject\UUIDUtil::getUUID();
$event->add('UID', $uuid);
$event->summary = $params['eventname'];
// Add a description if requested
$description = $params['eventdescription'];
if($description !== '')
$event->add('DESCRIPTION', $description);
// Add a location if requested
$location = $params['eventlocation'];
if($location !== '')
$event->add('LOCATION', $location);
// Add attachments
$attachments = $params['attachments'];
if(!is_null($attachments))
foreach($attachments as $attachment)
$event->add('ATTACH', $attachment);
// Create a timestamp for last modified, created and dtstamp values in UTC
$dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
$event->add('DTSTAMP', $dtStamp);
$event->add('CREATED', $dtStamp);
$event->add('LAST-MODIFIED', $dtStamp);
// Adjust the start date, based on the given timezone information
$dtStart = new \DateTime();
$dtStart->setTimezone($timezone);
$dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
// Only add the time values if it's not an allday event
if($params['allday'] != '1')
$dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
// Adjust the end date, based on the given timezone information
$dtEnd = new \DateTime();
$dtEnd->setTimezone($timezone);
$dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
// Only add the time values if it's not an allday event
if($params['allday'] != '1')
$dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
// According to the VCal spec, we need to add a whole day here
if($params['allday'] == '1')
$dtEnd->add(new \DateInterval('P1D'));
// Really add Start and End events
$dtStartEv = $event->add('DTSTART', $dtStart);
$dtEndEv = $event->add('DTEND', $dtEnd);
// Adjust the DATE format for allday events
if($params['allday'] == '1')
{
$dtStartEv['VALUE'] = 'DATE';
$dtEndEv['VALUE'] = 'DATE';
}
$eventStr = $vcalendar->serialize();
if(strpos($id, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
return false;
$connectionId = str_replace('webdav://', '', $id);
return $wdc->addCalendarEntry($connectionId, $eventStr);
}
else
{
// Actually add the values to the database
$calid = $this->getCalendarIdForPage($id);
$uri = $uri = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics';
$now = new \DateTime();
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified, componenttype, firstoccurence, lastoccurence, size, etag, uid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$res = $sqlite->query($query, $calid, $uri, $eventStr, $now->getTimestamp(), 'VEVENT',
$event->DTSTART->getDateTime()->getTimeStamp(), $event->DTEND->getDateTime()->getTimeStamp(),
strlen($eventStr), md5($eventStr), $uuid);
// If successfully, update the sync token database
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'added');
return true;
}
}
return false;
}
/**
* Retrieve the calendar settings of a given calendar id
*
* @param string $calid The calendar ID
*
* @return array The calendar settings array
*/
public function getCalendarSettings($calid)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT id, principaluri, calendarcolor, displayname, uri, description, components, transparent, synctoken, disabled FROM calendars WHERE id= ? ";
$res = $sqlite->query($query, $calid);
$row = $sqlite->res2row($res);
return $row;
}
/**
* Retrieve the calendar status of a given calendar id
*
* @param string $calid The calendar ID
* @return boolean True if calendar is enabled, otherwise false
*/
public function getCalendarStatus($calid)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT disabled FROM calendars WHERE id = ?";
$res = $sqlite->query($query, $calid);
$row = $sqlite->res2row($res);
if($row['disabled'] == 1)
return false;
else
return true;
}
/**
* Disable a calendar for a given page
*
* @param string $id The page ID
*
* @return boolean true on success, otherwise false
*/
public function disableCalendarForPage($id)
{
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET disabled = 1 WHERE id = ?";
$res = $sqlite->query($query, $calid);
if($res !== false)
return true;
return false;
}
/**
* Enable a calendar for a given page
*
* @param string $id The page ID
*
* @return boolean true on success, otherwise false
*/
public function enableCalendarForPage($id)
{
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "UPDATE calendars SET disabled = 0 WHERE id = ?";
$res = $sqlite->query($query, $calid);
if($res !== false)
return true;
return false;
}
/**
* Retrieve all events that are within a given date range,
* based on the timezone setting.
*
* There is also support for retrieving recurring events,
* using Sabre's VObject Iterator. Recurring events are represented
* as individual calendar entries with the same UID.
*
* @param string $id The page ID to work with
* @param string $user The user ID to work with
* @param string $startDate The start date as a string
* @param string $endDate The end date as a string
* @param string $color (optional) The calendar's color
+ * @param array $additional (optional) Parse additional fields
*
* @return array An array containing the calendar entries.
*/
- public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null)
+ public function getEventsWithinDateRange($id, $user, $startDate, $endDate, $timezone, $color = null, $additional = array())
{
if($timezone !== '' && $timezone !== 'local')
$timezone = new \DateTimeZone($timezone);
else
$timezone = new \DateTimeZone('UTC');
$data = array();
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calendardata, componenttype, uid FROM calendarobjects WHERE calendarid = ?";
$startTs = null;
$endTs = null;
if($startDate !== null)
{
$startTs = new \DateTime($startDate);
$query .= " AND lastoccurence > ".$sqlite->quote_string($startTs->getTimestamp());
}
if($endDate !== null)
{
$endTs = new \DateTime($endDate);
$query .= " AND firstoccurence < ".$sqlite->quote_string($endTs->getTimestamp());
}
// Load SabreDAV
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
if(strpos($id, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
return $data;
$connectionId = str_replace('webdav://', '', $id);
$arr = $wdc->getCalendarEntries($connectionId, $startDate, $endDate);
}
else
{
$calid = $this->getCalendarIdForPage($id);
if(is_null($color))
$color = $this->getCalendarColorForCalendar($calid);
$enabled = $this->getCalendarStatus($calid);
if($enabled === false)
return $data;
// Retrieve matching calendar objects
$res = $sqlite->query($query, $calid);
$arr = $sqlite->res2arr($res);
}
// Parse individual calendar entries
foreach($arr as $row)
{
if(isset($row['calendardata']))
{
$entry = array();
$vcal = \Sabre\VObject\Reader::read($row['calendardata']);
$recurrence = $vcal->VEVENT->RRULE;
// If it is a recurring event, pass it through Sabre's EventIterator
if($recurrence != null)
{
$rEvents = new \Sabre\VObject\Recur\EventIterator(array($vcal->VEVENT));
$rEvents->rewind();
while($rEvents->valid())
{
$event = $rEvents->getEventObject();
// If we are after the given time range, exit
if(($endTs !== null) && ($rEvents->getDtStart()->getTimestamp() > $endTs->getTimestamp()))
break;
// If we are before the given time range, continue
if(($startTs != null) && ($rEvents->getDtEnd()->getTimestamp() < $startTs->getTimestamp()))
{
$rEvents->next();
continue;
}
// If we are within the given time range, parse the event
- $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true);
+ $data[] = $this->convertIcalDataToEntry($event, $id, $timezone, $row['uid'], $color, true, $additional);
$rEvents->next();
}
}
else
- $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color);
+ $data[] = $this->convertIcalDataToEntry($vcal->VEVENT, $id, $timezone, $row['uid'], $color, false, $additional);
}
}
return $data;
}
/**
* Helper function that parses the iCal data of a VEVENT to a calendar entry.
*
* @param \Sabre\VObject\VEvent $event The event to parse
* @param \DateTimeZone $timezone The timezone object
* @param string $uid The entry's UID
* @param boolean $recurring (optional) Set to true to define a recurring event
+ * @param array $additional (optional) Parse additional fields
*
* @return array The parse calendar entry
*/
- private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false)
+ private function convertIcalDataToEntry($event, $page, $timezone, $uid, $color, $recurring = false, $additional = array())
{
$entry = array();
$start = $event->DTSTART;
// Parse only if the start date/time is present
if($start !== null)
{
$dtStart = $start->getDateTime();
$dtStart->setTimezone($timezone);
// moment.js doesn't like times be given even if
// allDay is set to true
// This should fix T23
if($start['VALUE'] == 'DATE')
{
$entry['allDay'] = true;
$entry['start'] = $dtStart->format("Y-m-d");
}
else
{
$entry['allDay'] = false;
$entry['start'] = $dtStart->format(\DateTime::ATOM);
}
}
$end = $event->DTEND;
// Parse only if the end date/time is present
if($end !== null)
{
$dtEnd = $end->getDateTime();
$dtEnd->setTimezone($timezone);
if($end['VALUE'] == 'DATE')
$entry['end'] = $dtEnd->format("Y-m-d");
else
$entry['end'] = $dtEnd->format(\DateTime::ATOM);
}
$description = $event->DESCRIPTION;
if($description !== null)
$entry['description'] = (string)$description;
else
$entry['description'] = '';
$attachments = $event->ATTACH;
if($attachments !== null)
{
$entry['attachments'] = array();
foreach($attachments as $attachment)
$entry['attachments'][] = (string)$attachment;
}
+ foreach($additional as $prop)
+ {
+ $entry[$prop] = (string)$event->{$prop};
+ }
$entry['title'] = (string)$event->summary;
$entry['location'] = (string)$event->location;
$entry['id'] = $uid;
$entry['page'] = $page;
$entry['color'] = $color;
$entry['recurring'] = $recurring;
return $entry;
}
/**
* Retrieve an event by its UID
*
* @param string $uid The event's UID
*
* @return mixed The table row with the given event
*/
public function getEventWithUid($uid)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calendardata, calendarid, componenttype, uri FROM calendarobjects WHERE uid = ?";
$res = $sqlite->query($query, $uid);
$row = $sqlite->res2row($res);
return $row;
}
/**
* Retrieve information of a calendar's object, not including the actual
* calendar data! This is mainly needed for the sync support.
*
* @param int $calid The calendar ID
*
* @return mixed The result
*/
public function getCalendarObjects($calid)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM calendarobjects WHERE calendarid = ?";
$res = $sqlite->query($query, $calid);
$arr = $sqlite->res2arr($res);
return $arr;
}
/**
* Retrieve a single calendar object by calendar ID and URI
*
* @param int $calid The calendar's ID
* @param string $uri The object's URI
*
* @return mixed The result
*/
public function getCalendarObjectByUri($calid, $uri)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri = ?";
$res = $sqlite->query($query, $calid, $uri);
$row = $sqlite->res2row($res);
return $row;
}
/**
* Retrieve several calendar objects by specifying an array of URIs.
* This is mainly neede for sync.
*
* @param int $calid The calendar's ID
* @param array $uris An array of URIs
*
* @return mixed The result
*/
public function getMultipleCalendarObjectsByUri($calid, $uris)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM calendarobjects WHERE calendarid = ? AND uri IN (";
// Inserting a whole bunch of question marks
$query .= implode(',', array_fill(0, count($uris), '?'));
$query .= ')';
$vals = array_merge(array($calid), $uris);
$res = $sqlite->query($query, $vals);
$arr = $sqlite->res2arr($res);
return $arr;
}
/**
* Retrieve all calendar events for a given calendar ID
*
* @param string $calid The calendar's ID
*
* @return array An array containing all calendar data
*/
public function getAllCalendarEvents($calid)
{
$enabled = $this->getCalendarStatus($calid);
if($enabled === false)
return false;
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calendardata, uid, componenttype, uri FROM calendarobjects WHERE calendarid = ?";
$res = $sqlite->query($query, $calid);
$arr = $sqlite->res2arr($res);
return $arr;
}
/**
* Edit a calendar entry for a page, given by its parameters.
* The params array has the same format as @see addCalendarEntryForPage
*
* @param string $id The page's ID to work on
* @param string $user The user's ID to work on
* @param array $params The parameter array for the edited calendar event
*
* @return boolean True on success, otherwise false
*/
public function editCalendarEntryForPage($id, $user, $params)
{
if($params['currenttz'] !== '' && $params['currenttz'] !== 'local')
$timezone = new \DateTimeZone($params['currenttz']);
elseif($params['currenttz'] === 'local')
$timezone = new \DateTimeZone($params['detectedtz']);
else
$timezone = new \DateTimeZone('UTC');
// Parse dates
$startDate = explode('-', $params['eventfrom']);
$startTime = explode(':', $params['eventfromtime']);
$endDate = explode('-', $params['eventto']);
$endTime = explode(':', $params['eventtotime']);
// Retrieve the existing event based on the UID
$uid = $params['uid'];
if(strpos($id, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
return false;
$event = $wdc->getCalendarEntryByUid($uid);
}
else
{
$event = $this->getEventWithUid($uid);
}
// Load SabreDAV
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
if(!isset($event['calendardata']))
return false;
$uri = $event['uri'];
$calid = $event['calendarid'];
// Parse the existing event
$vcal = \Sabre\VObject\Reader::read($event['calendardata']);
$vevent = $vcal->VEVENT;
// Set the new event values
$vevent->summary = $params['eventname'];
$dtStamp = new \DateTime(null, new \DateTimeZone('UTC'));
$description = $params['eventdescription'];
$location = $params['eventlocation'];
// Remove existing timestamps to overwrite them
$vevent->remove('DESCRIPTION');
$vevent->remove('DTSTAMP');
$vevent->remove('LAST-MODIFIED');
$vevent->remove('ATTACH');
$vevent->remove('LOCATION');
// Add new time stamps, description and location
$vevent->add('DTSTAMP', $dtStamp);
$vevent->add('LAST-MODIFIED', $dtStamp);
if($description !== '')
$vevent->add('DESCRIPTION', $description);
if($location !== '')
$vevent->add('LOCATION', $location);
// Add attachments
$attachments = $params['attachments'];
if(!is_null($attachments))
foreach($attachments as $attachment)
$vevent->add('ATTACH', $attachment);
// Setup DTSTART
$dtStart = new \DateTime();
$dtStart->setTimezone($timezone);
$dtStart->setDate(intval($startDate[0]), intval($startDate[1]), intval($startDate[2]));
if($params['allday'] != '1')
$dtStart->setTime(intval($startTime[0]), intval($startTime[1]), 0);
// Setup DTEND
$dtEnd = new \DateTime();
$dtEnd->setTimezone($timezone);
$dtEnd->setDate(intval($endDate[0]), intval($endDate[1]), intval($endDate[2]));
if($params['allday'] != '1')
$dtEnd->setTime(intval($endTime[0]), intval($endTime[1]), 0);
// According to the VCal spec, we need to add a whole day here
if($params['allday'] == '1')
$dtEnd->add(new \DateInterval('P1D'));
$vevent->remove('DTSTART');
$vevent->remove('DTEND');
$dtStartEv = $vevent->add('DTSTART', $dtStart);
$dtEndEv = $vevent->add('DTEND', $dtEnd);
// Remove the time for allday events
if($params['allday'] == '1')
{
$dtStartEv['VALUE'] = 'DATE';
$dtEndEv['VALUE'] = 'DATE';
}
$eventStr = $vcal->serialize();
if(strpos($id, 'webdav://') === 0)
{
$connectionId = str_replace('webdav://', '', $id);
return $wdc->editCalendarEntry($connectionId, $uid, $eventStr);
}
else
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$now = new DateTime();
// Actually write to the database
$query = "UPDATE calendarobjects SET calendardata = ?, lastmodified = ?, ".
"firstoccurence = ?, lastoccurence = ?, size = ?, etag = ? WHERE uid = ?";
$res = $sqlite->query($query, $eventStr, $now->getTimestamp(), $dtStart->getTimestamp(),
$dtEnd->getTimestamp(), strlen($eventStr), md5($eventStr), $uid);
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'modified');
return true;
}
}
return false;
}
/**
* Delete an event from a calendar by calendar ID and URI
*
* @param int $calid The calendar's ID
* @param string $uri The object's URI
*
* @return true
*/
public function deleteCalendarEntryForCalendarByUri($calid, $uri)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?";
$res = $sqlite->query($query, $calid, $uri);
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'deleted');
}
return true;
}
/**
* Delete a calendar entry for a given page. Actually, the event is removed
* based on the entry's UID, so that page ID is no used.
*
* @param string $id The page's ID (unused)
* @param array $params The parameter array to work with
*
* @return boolean True
*/
public function deleteCalendarEntryForPage($id, $params)
{
$uid = $params['uid'];
if(strpos($id, 'webdav://') === 0)
{
$wdc =& plugin_load('helper', 'webdavclient');
if(is_null($wdc))
return false;
$connectionId = str_replace('webdav://', '', $id);
$result = $wdc->deleteCalendarEntry($connectionId, $uid);
return $result;
}
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$event = $this->getEventWithUid($uid);
$calid = $event['calendarid'];
$uri = $event['uri'];
$query = "DELETE FROM calendarobjects WHERE uid = ?";
$res = $sqlite->query($query, $uid);
if($res !== false)
{
$this->updateSyncTokenLog($calid, $uri, 'deleted');
}
return true;
}
/**
* Retrieve the current sync token for a calendar
*
* @param string $calid The calendar id
*
* @return mixed The synctoken or false
*/
public function getSyncTokenForCalendar($calid)
{
$row = $this->getCalendarSettings($calid);
if(isset($row['synctoken']))
return $row['synctoken'];
return false;
}
/**
* Helper function to convert the operation name to
* an operation code as stored in the database
*
* @param string $operationName The operation name
*
* @return mixed The operation code or false
*/
public function operationNameToOperation($operationName)
{
switch($operationName)
{
case 'added':
return 1;
break;
case 'modified':
return 2;
break;
case 'deleted':
return 3;
break;
}
return false;
}
/**
* Update the sync token log based on the calendar id and the
* operation that was performed.
*
* @param string $calid The calendar ID that was modified
* @param string $uri The calendar URI that was modified
* @param string $operation The operation that was performed
*
* @return boolean True on success, otherwise false
*/
private function updateSyncTokenLog($calid, $uri, $operation)
{
$currentToken = $this->getSyncTokenForCalendar($calid);
$operationCode = $this->operationNameToOperation($operation);
if(($operationCode === false) || ($currentToken === false))
return false;
$values = array($uri,
$currentToken,
$calid,
$operationCode
);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "INSERT INTO calendarchanges (uri, synctoken, calendarid, operation) VALUES(?, ?, ?, ?)";
$res = $sqlite->query($query, $uri, $currentToken, $calid, $operationCode);
if($res === false)
return false;
$currentToken++;
$query = "UPDATE calendars SET synctoken = ? WHERE id = ?";
$res = $sqlite->query($query, $currentToken, $calid);
return ($res !== false);
}
/**
* Return the sync URL for a given Page, i.e. a calendar
*
* @param string $id The page's ID
* @param string $user (optional) The user's ID
*
* @return mixed The sync url or false
*/
public function getSyncUrlForPage($id, $user = null)
{
if(is_null($userid))
{
if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
{
$userid = $_SERVER['REMOTE_USER'];
}
else
{
return false;
}
}
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
$calsettings = $this->getCalendarSettings($calid);
if(!isset($calsettings['uri']))
return false;
$syncurl = DOKU_URL.'lib/plugins/davcal/calendarserver.php/calendars/'.$user.'/'.$calsettings['uri'];
return $syncurl;
}
/**
* Return the private calendar's URL for a given page
*
* @param string $id the page ID
*
* @return mixed The private URL or false
*/
public function getPrivateURLForPage($id)
{
$calid = $this->getCalendarIdForPage($id);
if($calid === false)
return false;
return $this->getPrivateURLForCalendar($calid);
}
/**
* Return the private calendar's URL for a given calendar ID
*
* @param string $calid The calendar's ID
*
* @return mixed The private URL or false
*/
public function getPrivateURLForCalendar($calid)
{
if(isset($this->cachedValues['privateurl'][$calid]))
return $this->cachedValues['privateurl'][$calid];
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT url FROM calendartoprivateurlmapping WHERE calid = ?";
$res = $sqlite->query($query, $calid);
$row = $sqlite->res2row($res);
if(!isset($row['url']))
{
$url = 'dokuwiki-' . bin2hex(random_bytes(16)) . '.ics';
$query = "INSERT INTO calendartoprivateurlmapping (url, calid) VALUES(?, ?)";
$res = $sqlite->query($query, $url, $calid);
if($res === false)
return false;
}
else
{
$url = $row['url'];
}
$url = DOKU_URL.'lib/plugins/davcal/ics.php/'.$url;
$this->cachedValues['privateurl'][$calid] = $url;
return $url;
}
/**
* Retrieve the calendar ID for a given private calendar URL
*
* @param string $url The private URL
*
* @return mixed The calendar ID or false
*/
public function getCalendarForPrivateURL($url)
{
$sqlite = $this->getDB();
if(!$sqlite)
return false;
$query = "SELECT calid FROM calendartoprivateurlmapping WHERE url = ?";
$res = $sqlite->query($query, $url);
$row = $sqlite->res2row($res);
if(!isset($row['calid']))
return false;
return $row['calid'];
}
/**
* Return a given calendar as ICS feed, i.e. all events in one ICS file.
*
* @param string $calid The calendar ID to retrieve
*
* @return mixed The calendar events as string or false
*/
public function getCalendarAsICSFeed($calid)
{
$calSettings = $this->getCalendarSettings($calid);
if($calSettings === false)
return false;
$events = $this->getAllCalendarEvents($calid);
if($events === false)
return false;
// Load SabreDAV
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
$out = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//DAVCal//DAVCal for DokuWiki//EN\r\nCALSCALE:GREGORIAN\r\nX-WR-CALNAME:";
$out .= $calSettings['displayname']."\r\n";
foreach($events as $event)
{
$vcal = \Sabre\VObject\Reader::read($event['calendardata']);
$evt = $vcal->VEVENT;
$out .= $evt->serialize();
}
$out .= "END:VCALENDAR\r\n";
return $out;
}
/**
* Retrieve a configuration option for the plugin
*
* @param string $key The key to query
* @return mixed The option set, null if not found
*/
public function getConfig($key)
{
return $this->getConf($key);
}
/**
* Parses some information from calendar objects, used for optimized
* calendar-queries. Taken nearly unmodified from Sabre's PDO backend
*
* Returns an array with the following keys:
* * etag - An md5 checksum of the object without the quotes.
* * size - Size of the object in bytes
* * componentType - VEVENT, VTODO or VJOURNAL
* * firstOccurence
* * lastOccurence
* * uid - value of the UID property
*
* @param string $calendarData
* @return array
*/
protected function getDenormalizedData($calendarData)
{
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
$vObject = \Sabre\VObject\Reader::read($calendarData);
$componentType = null;
$component = null;
$firstOccurence = null;
$lastOccurence = null;
$uid = null;
foreach ($vObject->getComponents() as $component)
{
if ($component->name !== 'VTIMEZONE')
{
$componentType = $component->name;
$uid = (string)$component->UID;
break;
}
}
if (!$componentType)
{
return false;
}
if ($componentType === 'VEVENT')
{
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
// Finding the last occurence is a bit harder
if (!isset($component->RRULE))
{
if (isset($component->DTEND))
{
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
}
elseif (isset($component->DURATION))
{
$endDate = clone $component->DTSTART->getDateTime();
$endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
$lastOccurence = $endDate->getTimeStamp();
}
elseif (!$component->DTSTART->hasTime())
{
$endDate = clone $component->DTSTART->getDateTime();
$endDate->modify('+1 day');
$lastOccurence = $endDate->getTimeStamp();
}
else
{
$lastOccurence = $firstOccurence;
}
}
else
{
$it = new \Sabre\VObject\Recur\EventIterator($vObject, (string)$component->UID);
$maxDate = new \DateTime('2038-01-01');
if ($it->isInfinite())
{
$lastOccurence = $maxDate->getTimeStamp();
}
else
{
$end = $it->getDtEnd();
while ($it->valid() && $end < $maxDate)
{
$end = $it->getDtEnd();
$it->next();
}
$lastOccurence = $end->getTimeStamp();
}
}
}
return array(
'etag' => md5($calendarData),
'size' => strlen($calendarData),
'componentType' => $componentType,
'firstOccurence' => $firstOccurence,
'lastOccurence' => $lastOccurence,
'uid' => $uid,
);
}
/**
* Query a calendar by ID and taking several filters into account.
* This is heavily based on Sabre's PDO backend.
*
* @param int $calendarId The calendar's ID
* @param array $filters The filter array to apply
*
* @return mixed The result
*/
public function calendarQuery($calendarId, $filters)
{
dbglog('davcal::helper::calendarQuery');
$componentType = null;
$requirePostFilter = true;
$timeRange = null;
$sqlite = $this->getDB();
if(!$sqlite)
return false;
// if no filters were specified, we don't need to filter after a query
if (!$filters['prop-filters'] && !$filters['comp-filters'])
{
$requirePostFilter = false;
}
// Figuring out if there's a component filter
if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined'])
{
$componentType = $filters['comp-filters'][0]['name'];
// Checking if we need post-filters
if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters'])
{
$requirePostFilter = false;
}
// There was a time-range filter
if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range']))
{
$timeRange = $filters['comp-filters'][0]['time-range'];
// If start time OR the end time is not specified, we can do a
// 100% accurate mysql query.
if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end']))
{
$requirePostFilter = false;
}
}
}
if ($requirePostFilter)
{
$query = "SELECT uri, calendardata FROM calendarobjects WHERE calendarid = ?";
}
else
{
$query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
}
$values = array(
$calendarId
);
if ($componentType)
{
$query .= " AND componenttype = ?";
$values[] = $componentType;
}
if ($timeRange && $timeRange['start'])
{
$query .= " AND lastoccurence > ?";
$values[] = $timeRange['start']->getTimeStamp();
}
if ($timeRange && $timeRange['end'])
{
$query .= " AND firstoccurence < ?";
$values[] = $timeRange['end']->getTimeStamp();
}
$res = $sqlite->query($query, $values);
$arr = $sqlite->res2arr($res);
$result = array();
foreach($arr as $row)
{
if ($requirePostFilter)
{
if (!$this->validateFilterForObject($row, $filters))
{
continue;
}
}
$result[] = $row['uri'];
}
return $result;
}
/**
* This method validates if a filter (as passed to calendarQuery) matches
* the given object. Taken from Sabre's PDO backend
*
* @param array $object
* @param array $filters
* @return bool
*/
protected function validateFilterForObject($object, $filters)
{
require_once(DOKU_PLUGIN.'davcal/vendor/autoload.php');
// Unfortunately, setting the 'calendardata' here is optional. If
// it was excluded, we actually need another call to get this as
// well.
if (!isset($object['calendardata']))
{
$object = $this->getCalendarObjectByUri($object['calendarid'], $object['uri']);
}
$vObject = \Sabre\VObject\Reader::read($object['calendardata']);
$validator = new \Sabre\CalDAV\CalendarQueryValidator();
$res = $validator->validate($vObject, $filters);
return $res;
}
/**
* Retrieve changes for a given calendar based on the given syncToken.
*
* @param int $calid The calendar's ID
* @param int $syncToken The supplied sync token
* @param int $syncLevel The sync level
* @param int $limit The limit of changes
*
* @return array The result
*/
public function getChangesForCalendar($calid, $syncToken, $syncLevel, $limit = null)
{
// Current synctoken
$currentToken = $this->getSyncTokenForCalendar($calid);
if ($currentToken === false) return null;
$result = array(
'syncToken' => $currentToken,
'added' => array(),
'modified' => array(),
'deleted' => array(),
);
$sqlite = $this->getDB();
if(!$sqlite)
return false;
if ($syncToken)
{
$query = "SELECT uri, operation FROM calendarchanges WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken";
if ($limit > 0) $query .= " LIMIT " . (int)$limit;
// Fetching all changes
$res = $sqlite->query($query, $syncToken, $currentToken, $calid);
if($res === false)
return null;
$arr = $sqlite->res2arr($res);
$changes = array();
// This loop ensures that any duplicates are overwritten, only the
// last change on a node is relevant.
foreach($arr as $row)
{
$changes[$row['uri']] = $row['operation'];
}
foreach ($changes as $uri => $operation)
{
switch ($operation)
{
case 1 :
$result['added'][] = $uri;
break;
case 2 :
$result['modified'][] = $uri;
break;
case 3 :
$result['deleted'][] = $uri;
break;
}
}
}
else
{
// No synctoken supplied, this is the initial sync.
$query = "SELECT uri FROM calendarobjects WHERE calendarid = ?";
$res = $sqlite->query($query);
$arr = $sqlite->res2arr($res);
$result['added'] = $arr;
}
return $result;
}
}
diff --git a/lang/de-informal/lang.php b/lang/de-informal/lang.php
--- a/lang/de-informal/lang.php
+++ b/lang/de-informal/lang.php
@@ -1,64 +1,65 @@
*/
$lang['created_by_davcal'] = 'Erstellt von DAVCal für DokuWiki';
$lang['unknown_error'] = 'Unbekannter Fehler';
$lang['event_added'] = 'Eintrag hinzugefügt';
$lang['event_edited'] = 'Eintrag bearbeitet';
$lang['event_deleted'] = 'Eintrag entfernt';
$lang['no_permission'] = 'Du hast nicht die erforderlichen Rechte, um den Eintrag zu bearbeiten';
$lang['settings'] = 'Einstellungen/Sync';
$lang['settings_saved'] = 'Einstellungen erfolgreich gespeichert';
$lang['error_saving'] = 'Fehler beim Speichern der Einstellungen.';
$lang['local_time'] = 'Lokale Zeit (Browser-basierend)';
$lang['from'] = 'Von';
$lang['to'] = 'Bis';
$lang['at'] = 'Am';
$lang['description'] = 'Beschreibung';
$lang['title'] = 'Titel';
$lang['error_timezone_not_in_list'] = 'Die angegebene Zeitzone wird von PHP nicht unterstützt';
$lang['error_option_error'] = 'In den Optionen wurden ein Fehler festgestellt!';
$lang['this_calendar_uses_timezone'] = 'Dieser Kalender wird in der Zeitzone %s angezeigt.';
+$lang['costs'] = 'Kosten';
$lang['js']['create_new_event'] = 'Neuen Eintrag anlegen';
$lang['js']['cancel'] = 'Abbrechen';
$lang['js']['create'] = 'Anlegen';
$lang['js']['save'] = 'Speichern';
$lang['js']['settings'] = 'Einstellungen';
$lang['js']['edit'] = 'Bearbeiten';
$lang['js']['delete'] = 'Löschen';
$lang['js']['edit_event'] = 'Eintrag bearbeiten';
$lang['js']['allday'] = 'Ganzer Tag';
$lang['js']['title'] = 'Titel';
$lang['js']['location'] = 'Ort';
$lang['js']['from'] = 'Von';
$lang['js']['to'] = 'Bis';
$lang['js']['yes'] = 'Ja';
$lang['js']['confirmation'] = 'Bestätigung';
$lang['js']['ok'] = 'OK';
$lang['js']['info'] = 'Info';
$lang['js']['really_delete_this_event'] = 'Diesen Eintrag wirklich löschen?';
$lang['js']['timezone'] = 'Zeitzone';
$lang['js']['weeknumbers'] = 'Wochennummern';
$lang['js']['use_lang_tz'] = 'Zeitzone der Spracheinstellung verwenden';
$lang['js']['only_workweek'] = 'Zeige nur die Arbeitswoche';
$lang['js']['sync_url'] = 'CalDAV URL';
$lang['js']['error_retrieving_data'] = 'Beim Abfragen der Daten vom Kalenderserver ist ein Fehler aufgetreten.';
$lang['js']['start_date_invalid'] = 'Beginnzeit/-datum ist ungültig';
$lang['js']['end_date_invalid'] = 'Endzeit/-datum ist ungültig';
$lang['js']['end_date_before_start_date'] = 'Endzeit/-datum liegt vor Startzeit/-datum';
$lang['js']['end_date_is_same_as_start_date'] = 'Endzeit/-datum ist gleich wie Startzeit/-datum';
$lang['js']['description'] = 'Beschreibung';
$lang['js']['private_url'] = 'Private URL';
$lang['js']['recurring_cant_edit'] = 'Das Bearbeiten wiederkehrender Einträge wird derzeit nicht unterstützt';
$lang['js']['no_permission'] = 'Du hast nicht die erforderlichen Rechte, diesen Kalender zu bearbeiten';
$lang['js']['calendar'] = 'Kalender';
$lang['js']['start_monday'] = 'Die Woche beginnt am Montag';
$lang['js']['nothing_to_show'] = 'Nichts anzuzeigen';
$lang['js']['add_attachment'] = 'Link hinzufügen';
$lang['js']['attachments'] = 'Angehängte Links';
$lang['js']['language_specific'] = 'Sprachabhängig';
$lang['js']['sync_ical'] = 'iCal Sync URL';
$lang['js']['timeformat'] = 'Zeitformat';
diff --git a/lang/de/lang.php b/lang/de/lang.php
--- a/lang/de/lang.php
+++ b/lang/de/lang.php
@@ -1,64 +1,65 @@
*/
$lang['created_by_davcal'] = 'Erstellt von DAVCal für DokuWiki';
$lang['unknown_error'] = 'Unbekannter Fehler';
$lang['event_added'] = 'Eintrag hinzugefügt';
$lang['event_edited'] = 'Eintrag bearbeitet';
$lang['event_deleted'] = 'Eintrag entfernt';
$lang['no_permission'] = 'Sie haben nicht die erforderlichen Rechte, um den Eintrag zu bearbeiten';
$lang['settings'] = 'Einstellungen/Sync';
$lang['settings_saved'] = 'Einstellungen erfolgreich gespeichert';
$lang['error_saving'] = 'Fehler beim Speichern der Einstellungen.';
$lang['local_time'] = 'Lokale Zeit (Browser-basierend)';
$lang['from'] = 'Von';
$lang['to'] = 'Bis';
$lang['at'] = 'Am';
$lang['description'] = 'Beschreibung';
$lang['title'] = 'Titel';
$lang['error_timezone_not_in_list'] = 'Die angegebene Zeitzone wird von PHP nicht unterstützt';
$lang['error_option_error'] = 'In den Optionen wurden ein Fehler festgestellt!';
$lang['this_calendar_uses_timezone'] = 'Dieser Kalender wird in der Zeitzone %s angezeigt.';
+$lang['costs'] = 'Kosten';
$lang['js']['create_new_event'] = 'Neuen Eintrag anlegen';
$lang['js']['cancel'] = 'Abbrechen';
$lang['js']['create'] = 'Anlegen';
$lang['js']['save'] = 'Speichern';
$lang['js']['settings'] = 'Einstellungen';
$lang['js']['edit'] = 'Bearbeiten';
$lang['js']['delete'] = 'Löschen';
$lang['js']['edit_event'] = 'Eintrag bearbeiten';
$lang['js']['allday'] = 'Ganzer Tag';
$lang['js']['title'] = 'Titel';
$lang['js']['location'] = 'Ort';
$lang['js']['from'] = 'Von';
$lang['js']['to'] = 'Bis';
$lang['js']['yes'] = 'Ja';
$lang['js']['confirmation'] = 'Bestätigung';
$lang['js']['ok'] = 'OK';
$lang['js']['info'] = 'Info';
$lang['js']['really_delete_this_event'] = 'Diesen Eintrag wirklich löschen?';
$lang['js']['timezone'] = 'Zeitzone';
$lang['js']['weeknumbers'] = 'Wochennummern';
$lang['js']['use_lang_tz'] = 'Zeitzone der Spracheinstellung verwenden';
$lang['js']['only_workweek'] = 'Zeige nur die Arbeitswoche';
$lang['js']['sync_url'] = 'CalDAV URL';
$lang['js']['error_retrieving_data'] = 'Beim Abfragen der Daten vom Kalenderserver ist ein Fehler aufgetreten.';
$lang['js']['start_date_invalid'] = 'Beginnzeit/-datum ist ungültig';
$lang['js']['end_date_invalid'] = 'Endzeit/-datum ist ungültig';
$lang['js']['end_date_before_start_date'] = 'Endzeit/-datum liegt vor Startzeit/-datum';
$lang['js']['end_date_is_same_as_start_date'] = 'Endzeit/-datum ist gleich wie Startzeit/-datum';
$lang['js']['description'] = 'Beschreibung';
$lang['js']['private_url'] = 'Private URL';
$lang['js']['recurring_cant_edit'] = 'Das Bearbeiten wiederkehrender Einträge wird derzeit nicht unterstützt';
$lang['js']['no_permission'] = 'Sie haben nicht die erforderlichen Rechte, diesen Kalender zu bearbeiten';
$lang['js']['calendar'] = 'Kalender';
$lang['js']['start_monday'] = 'Die Woche beginnt am Montag';
$lang['js']['nothing_to_show'] = 'Nichts anzuzeigen';
$lang['js']['add_attachment'] = 'Link hinzufügen';
$lang['js']['attachments'] = 'Angehängte Links';
$lang['js']['language_specific'] = 'Sprachabhängig';
$lang['js']['sync_ical'] = 'iCal Sync URL';
$lang['js']['timeformat'] = 'Zeitformat';
diff --git a/lang/en/lang.php b/lang/en/lang.php
--- a/lang/en/lang.php
+++ b/lang/en/lang.php
@@ -1,64 +1,65 @@
*/
$lang['created_by_davcal'] = 'Created by DAVCal for DokuWiki';
$lang['unknown_error'] = 'Unknown Error';
$lang['event_added'] = 'Event added';
$lang['event_edited'] = 'Event edited';
$lang['event_deleted'] = 'Event deleted';
$lang['no_permission'] = 'You don\'t have permission to modify this calendar';
$lang['settings'] = 'Settings/Sync';
$lang['settings_saved'] = 'Settings saved successfully';
$lang['error_saving'] = 'Error saving settings.';
$lang['local_time'] = 'Local time (Browser based)';
$lang['from'] = 'From';
$lang['to'] = 'To';
$lang['at'] = 'At';
$lang['description'] = 'Description';
$lang['title'] = 'Title';
$lang['error_timezone_not_in_list'] = 'The desired timezone is not supported by PHP!';
$lang['error_option_error'] = 'You have an error in the option list';
$lang['this_calendar_uses_timezone'] = 'This calendar is shown in the timezone %s.';
+$lang['costs'] = 'Costs';
$lang['js']['create_new_event'] = 'Create new Event';
$lang['js']['cancel'] = 'Cancel';
$lang['js']['create'] = 'Create';
$lang['js']['save'] = 'Save';
$lang['js']['settings'] = 'Settings';
$lang['js']['edit'] = 'Edit';
$lang['js']['delete'] = 'Delete';
$lang['js']['edit_event'] = 'Edit Event';
$lang['js']['allday'] = 'All Day Event';
$lang['js']['title'] = 'Title';
$lang['js']['location'] = 'Location';
$lang['js']['from'] = 'From';
$lang['js']['to'] = 'To';
$lang['js']['yes'] = 'Yes';
$lang['js']['confirmation'] = 'Confirmation';
$lang['js']['ok'] = 'OK';
$lang['js']['info'] = 'Info';
$lang['js']['really_delete_this_event'] = 'Really delete this event?';
$lang['js']['timezone'] = 'Timezone';
$lang['js']['weeknumbers'] = 'Week Numbers';
$lang['js']['use_lang_tz'] = 'Use Timezone from language setting';
$lang['js']['only_workweek'] = 'Show only Work Week';
$lang['js']['sync_url'] = 'CalDAV URL';
$lang['js']['error_retrieving_data'] = 'There was an error retrieving data from the calendar server.';
$lang['js']['start_date_invalid'] = 'Start date/time is invalid.';
$lang['js']['end_date_invalid'] = 'End date/time is invalid.';
$lang['js']['end_date_before_start_date'] = 'End date/time is before start date/time.';
$lang['js']['end_date_is_same_as_start_date'] = 'End date/time is equal to start date/time.';
$lang['js']['description'] = 'Description';
$lang['js']['private_url'] = 'Private URL';
$lang['js']['recurring_cant_edit'] = 'Editing recurring events is currently not supported';
$lang['js']['no_permission'] = 'You don\'t have permission to modify this calendar';
$lang['js']['calendar'] = 'Calendar';
$lang['js']['start_monday'] = 'Week starts on Monday';
$lang['js']['nothing_to_show'] = 'Nothing to show';
$lang['js']['add_attachment'] = 'Add link';
$lang['js']['attachments'] = 'Attached links';
$lang['js']['language_specific'] = 'Language specific';
$lang['js']['sync_ical'] = 'iCal Sync URL';
$lang['js']['timeformat'] = 'Time Format';
diff --git a/syntax/events.php b/syntax/events.php
new file mode 100644
--- /dev/null
+++ b/syntax/events.php
@@ -0,0 +1,344 @@
+
+ */
+
+// must be run within Dokuwiki
+if(!defined('DOKU_INC')) die();
+
+if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
+require_once(DOKU_PLUGIN.'syntax.php');
+
+class syntax_plugin_davcal_events extends DokuWiki_Syntax_Plugin {
+
+ protected $hlp = null;
+
+ // Load the helper plugin
+ public function syntax_plugin_davcal_events() {
+ $this->hlp =& plugin_load('helper', 'davcal');
+ }
+
+
+ /**
+ * What kind of syntax are we?
+ */
+ function getType(){
+ return 'substition';
+ }
+
+ /**
+ * What about paragraphs?
+ */
+ function getPType(){
+ return 'normal';
+ }
+
+ /**
+ * Where to sort in?
+ */
+ function getSort(){
+ return 165;
+ }
+
+ /**
+ * Connect pattern to lexer
+ */
+ function connectTo($mode) {
+ $this->Lexer->addSpecialPattern('\{\{davcalevents>[^}]*\}\}',$mode,'plugin_davcal_events');
+ }
+
+ /**
+ * Handle the match
+ */
+ function handle($match, $state, $pos, Doku_Handler $handler){
+ global $ID;
+ $data = array('id' => array(),
+ 'startdate' => 'today',
+ 'numdays' => 30,
+ 'startisend' => false,
+ 'dateformat' => 'Y-m-d H:i',
+ 'alldayformat' => 'Y-m-d',
+ 'onlystart' => false,
+ 'sort' => 'desc',
+ 'timezone' => 'local',
+ 'timeformat' => null,
+ );
+
+ $lastid = $ID;
+ $options = trim(substr($match,15,-2));
+ $options = explode(',', $options);
+
+ foreach($options as $option)
+ {
+ list($key, $val) = explode('=', $option);
+ $key = strtolower(trim($key));
+ $val = trim($val);
+ switch($key)
+ {
+ case 'id':
+ $lastid = $val;
+ if(!in_array($val, $data['id']))
+ $data['id'][$val] = null;
+ break;
+ case 'color':
+ $data['id'][$lastid] = $val;
+ break;
+ case 'onlystart':
+ if(($val === 'on') || ($val === 'true'))
+ $data['onlystart'] = true;
+ break;
+ case 'startisend':
+ if(($val === 'on') || ($val === 'true'))
+ $data['startisend'] = true;
+ break;
+ case 'timezone':
+ $tzlist = \DateTimeZone::listIdentifiers(DateTimeZone::ALL);
+ if(in_array($val, $tzlist) || $val === 'no')
+ $data['timezone'] = $val;
+ else
+ msg($this->getLang('error_timezone_not_in_list'), -1);
+ break;
+ default:
+ $data[$key] = $val;
+ }
+ }
+
+ // Handle the default case when the user didn't enter a different ID
+ if(empty($data['id']))
+ {
+ $data['id'] = array($ID => '#3a87ad');
+ }
+
+ // Fix up the colors, if no color information is given
+ foreach($data['id'] as $id => $color)
+ {
+ if(is_null($color))
+ {
+ // If this is the current calendar or a WebDAV calendar, use the
+ // default color
+ if(($id === $ID) || (strpos($id, 'webdav://') === 0))
+ {
+ $data['id'][$id] = '#3a87ad';
+ }
+ // Otherwise, retrieve the color information from the calendar settings
+ else
+ {
+ $calid = $this->hlp->getCalendarIdForPage($ID);
+ $settings = $this->hlp->getCalendarSettings($calid);
+ $color = $settings['calendarcolor'];
+ $data['id'][$id] = $color;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ private static function sort_events_asc($a, $b)
+ {
+ $from1 = new \DateTime($a['start']);
+ $from2 = new \DateTime($b['start']);
+ return $from2 < $from1;
+ }
+
+ private static function sort_events_desc($a, $b)
+ {
+ $from1 = new \DateTime($a['start']);
+ $from2 = new \DateTime($b['start']);
+ return $from1 < $from2;
+ }
+
+ /**
+ * Create output
+ */
+ function render($format, Doku_Renderer $R, $data) {
+ if($format == 'metadata')
+ {
+ $R->meta['plugin_davcal']['events'] = true;
+ return true;
+ }
+ if(($format != 'xhtml') && ($format != 'odt')) return false;
+ global $ID;
+
+ $events = array();
+ $from = $data['startdate'];
+ $toStr = null;
+
+ // Handle the various options to 'startDate'
+ if($from === 'today')
+ {
+ $from = new \DateTime();
+ }
+ elseif(strpos($from, 'today-') === 0)
+ {
+ $days = intval(str_replace('today-', '', $from));
+ $from = new \DateTime();
+ $from->sub(new \DateInterval('P'.$days.'D'));
+ }
+ elseif(strpos($from, 'today+') === 0)
+ {
+ $days = intval(str_replace('today+', '', $from));
+ $from = new \DateTime();
+ $from->add(new \DateInterval('P'.$days.'D'));
+ }
+ else
+ {
+ $from = new \DateTime($from);
+ }
+
+ // Handle the option 'startisend'
+ if($data['startisend'] === true)
+ {
+ if($data['numdays'] > 0)
+ {
+ $to = clone $from;
+ $to->sub(new \DateInterval('P'.$data['numdays'].'D'));
+ $fromStr = $to->format('Y-m-d');
+ }
+ else
+ {
+ $fromStr = null;
+ }
+ $toStr = $from->format('Y-m-d');
+ }
+ else
+ {
+ if($data['numdays'] > 0)
+ {
+ $to = clone $from;
+ $to->add(new \DateInterval('P'.$data['numdays'].'D'));
+ $toStr = $to->format('Y-m-d');
+ }
+ else
+ {
+ $toStr = null;
+ }
+ $fromStr = $from->format('Y-m-d');
+ }
+
+ // Support for timezone
+ $timezone = $data['timezone'];
+
+ // Filter events by user permissions
+ $userEvents = $this->hlp->filterCalendarPagesByUserPermission($data['id']);
+
+ // Fetch the events
+ foreach($userEvents as $calPage => $color)
+ {
+ $events = array_merge($events, $this->hlp->getEventsWithinDateRange($calPage,
+ $user, $fromStr, $toStr, $timezone, null,
+ array('URL', 'X-COST')));
+
+ }
+ // Sort the events
+ if($data['sort'] === 'desc')
+ usort($events, array("syntax_plugin_davcal_events", "sort_events_desc"));
+ else
+ usort($events, array("syntax_plugin_davcal_events", "sort_events_asc"));
+
+ $R->doc .= '
';
+
+
+ foreach($events as $event)
+ {
+ $from = new \DateTime($event['start']);
+
+ $color = $data['id'][$event['page']];
+ $R->doc .= '
';
+ $R->doc .= '
';
+ if($timezone !== 'local')
+ {
+ $from->setTimezone(new \DateTimeZone($timezone));
+ $to->setTimezone(new \DateTimeZone($timezone));
+ }
+ if($event['allDay'] === true)
+ $R->doc .= '
'.$from->format($data['alldayformat']).'
';
+ else
+ $R->doc .= '
'.$from->format($data['dateformat']).'
';
+ if(!is_null($data['timeformat']) && $event['allDay'] != true)
+ {
+ $R->doc .= '
'.$from->format($data['timeformat']).'
';
+ }
+ $R->doc .= $event['title'].'
';
+ $R->doc .= '
'.$this->getLang('costs').': '.$event['X-COST'].'';
+ $R->doc .= '
';
+ $R->doc .= '';
+ $R->doc .= '';
+ $R->doc .= '';
+ $R->doc .= '
';
+ $R->doc .= '
';
+ $R->doc .= '
';
+ }
+
+ $R->doc .= '
';
+
+ /*
+ // Create tabular output
+ $R->table_open();
+ $R->tablethead_open();
+ $R->tableheader_open();
+ $R->doc .= $data['onlystart'] ? $this->getLang('at') : $this->getLang('from');
+ $R->tableheader_close();
+ if(!$data['onlystart'])
+ {
+ $R->tableheader_open();
+ $R->doc .= $this->getLang('to');
+ $R->tableheader_close();
+ }
+ $R->tableheader_open();
+ $R->doc .= $this->getLang('title');
+ $R->tableheader_close();
+ $R->tableheader_open();
+ $R->doc .= $this->getLang('description');
+ $R->tableheader_close();
+ $R->tablethead_close();
+ foreach($events as $event)
+ {
+ $R->tablerow_open();
+ $R->tablecell_open();
+ $from = new \DateTime($event['start']);
+ if($timezone !== 'local')
+ {
+ $from->setTimezone(new \DateTimeZone($timezone));
+ $to->setTimezone(new \DateTimeZone($timezone));
+ }
+ if($event['allDay'] === true)
+ $R->doc .= $from->format($data['alldayformat']);
+ else
+ $R->doc .= $from->format($data['dateformat']);
+ $R->tablecell_close();
+ if(!$data['onlystart'])
+ {
+ $to = new \DateTime($event['end']);
+ // Fixup all day events, which have one day in excess
+ if($event['allDay'] === true)
+ {
+ $to->sub(new \DateInterval('P1D'));
+ }
+ $R->tablecell_open();
+ if($event['allDay'] === true)
+ $R->doc .= $to->format($data['alldayformat']);
+ else
+ $R->doc .= $to->format($data['dateformat']);
+ $R->tablecell_close();
+ }
+ $R->tablecell_open();
+ $R->doc .= $event['title'];
+ $R->tablecell_close();
+ $R->tablecell_open();
+ $R->doc .= $event['description'];
+ $R->tablecell_close();
+ $R->tablerow_close();
+ }
+ $R->table_close();
+ */
+ }
+
+
+
+}
+
+// vim:ts=4:sw=4:et:enc=utf-8:
diff --git a/syntax/table.php b/syntax/table.php
--- a/syntax/table.php
+++ b/syntax/table.php
@@ -1,279 +1,277 @@
*/
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
class syntax_plugin_davcal_table extends DokuWiki_Syntax_Plugin {
protected $hlp = null;
// Load the helper plugin
public function syntax_plugin_davcal_table() {
$this->hlp =& plugin_load('helper', 'davcal');
}
/**
* What kind of syntax are we?
*/
function getType(){
return 'substition';
}
/**
* What about paragraphs?
*/
function getPType(){
return 'normal';
}
/**
* Where to sort in?
*/
function getSort(){
return 165;
}
/**
* Connect pattern to lexer
*/
function connectTo($mode) {
$this->Lexer->addSpecialPattern('\{\{davcaltable>[^}]*\}\}',$mode,'plugin_davcal_table');
}
/**
* Handle the match
*/
function handle($match, $state, $pos, Doku_Handler $handler){
global $ID;
$options = trim(substr($match,14,-2));
$options = explode(',', $options);
$data = array('id' => array(),
'startdate' => 'today',
'numdays' => 30,
'startisend' => false,
'dateformat' => 'Y-m-d H:i',
'alldayformat' => 'Y-m-d',
'onlystart' => false,
'sort' => 'desc',
'timezone' => 'local'
);
- $lastid = $ID;
foreach($options as $option)
{
list($key, $val) = explode('=', $option);
$key = strtolower(trim($key));
$val = trim($val);
switch($key)
{
case 'id':
- $lastid = $val;
if(!in_array($val, $data['id']))
$data['id'][$val] = '#3a87ad';
break;
case 'onlystart':
if(($val === 'on') || ($val === 'true'))
$data['onlystart'] = true;
break;
case 'startisend':
if(($val === 'on') || ($val === 'true'))
$data['startisend'] = true;
break;
case 'timezone':
$tzlist = \DateTimeZone::listIdentifiers(DateTimeZone::ALL);
if(in_array($val, $tzlist) || $val === 'no')
$data['timezone'] = $val;
else
msg($this->getLang('error_timezone_not_in_list'), -1);
break;
default:
$data[$key] = $val;
}
}
// Handle the default case when the user didn't enter a different ID
if(empty($data['id']))
{
$data['id'] = array($ID => '#3a87ad');
}
return $data;
}
private static function sort_events_asc($a, $b)
{
$from1 = new \DateTime($a['start']);
$from2 = new \DateTime($b['start']);
return $from2 < $from1;
}
private static function sort_events_desc($a, $b)
{
$from1 = new \DateTime($a['start']);
$from2 = new \DateTime($b['start']);
return $from1 < $from2;
}
/**
* Create output
*/
function render($format, Doku_Renderer $R, $data) {
if($format == 'metadata')
{
$R->meta['plugin_davcal']['table'] = true;
return true;
}
if(($format != 'xhtml') && ($format != 'odt')) return false;
global $ID;
$events = array();
$from = $data['startdate'];
$toStr = null;
// Handle the various options to 'startDate'
if($from === 'today')
{
$from = new \DateTime();
}
elseif(strpos($from, 'today-') === 0)
{
$days = intval(str_replace('today-', '', $from));
$from = new \DateTime();
$from->sub(new \DateInterval('P'.$days.'D'));
}
elseif(strpos($from, 'today+') === 0)
{
$days = intval(str_replace('today+', '', $from));
$from = new \DateTime();
$from->add(new \DateInterval('P'.$days.'D'));
}
else
{
$from = new \DateTime($from);
}
// Handle the option 'startisend'
if($data['startisend'] === true)
{
if($data['numdays'] > 0)
{
$to = clone $from;
$to->sub(new \DateInterval('P'.$data['numdays'].'D'));
$fromStr = $to->format('Y-m-d');
}
else
{
$fromStr = null;
}
$toStr = $from->format('Y-m-d');
}
else
{
if($data['numdays'] > 0)
{
$to = clone $from;
$to->add(new \DateInterval('P'.$data['numdays'].'D'));
$toStr = $to->format('Y-m-d');
}
else
{
$toStr = null;
}
$fromStr = $from->format('Y-m-d');
}
// Support for timezone
$timezone = $data['timezone'];
// Filter events by user permissions
$userEvents = $this->hlp->filterCalendarPagesByUserPermission($data['id']);
// Fetch the events
foreach($userEvents as $calPage => $color)
{
$events = array_merge($events, $this->hlp->getEventsWithinDateRange($calPage,
$user, $fromStr, $toStr, $timezone));
}
// Sort the events
if($data['sort'] === 'desc')
usort($events, array("syntax_plugin_davcal_table", "sort_events_desc"));
else
usort($events, array("syntax_plugin_davcal_table", "sort_events_asc"));
// Create tabular output
$R->table_open();
$R->tablethead_open();
$R->tableheader_open();
$R->doc .= $data['onlystart'] ? $this->getLang('at') : $this->getLang('from');
$R->tableheader_close();
if(!$data['onlystart'])
{
$R->tableheader_open();
$R->doc .= $this->getLang('to');
$R->tableheader_close();
}
$R->tableheader_open();
$R->doc .= $this->getLang('title');
$R->tableheader_close();
$R->tableheader_open();
$R->doc .= $this->getLang('description');
$R->tableheader_close();
$R->tablethead_close();
foreach($events as $event)
{
$R->tablerow_open();
$R->tablecell_open();
$from = new \DateTime($event['start']);
if($timezone !== 'local')
{
$from->setTimezone(new \DateTimeZone($timezone));
$to->setTimezone(new \DateTimeZone($timezone));
}
if($event['allDay'] === true)
$R->doc .= $from->format($data['alldayformat']);
else
$R->doc .= $from->format($data['dateformat']);
$R->tablecell_close();
if(!$data['onlystart'])
{
$to = new \DateTime($event['end']);
// Fixup all day events, which have one day in excess
if($event['allDay'] === true)
{
$to->sub(new \DateInterval('P1D'));
}
$R->tablecell_open();
if($event['allDay'] === true)
$R->doc .= $to->format($data['alldayformat']);
else
$R->doc .= $to->format($data['dateformat']);
$R->tablecell_close();
}
$R->tablecell_open();
$R->doc .= $event['title'];
$R->tablecell_close();
$R->tablecell_open();
$R->doc .= $event['description'];
$R->tablecell_close();
$R->tablerow_close();
}
$R->table_close();
}
}
// vim:ts=4:sw=4:et:enc=utf-8: