moved deprecated communityhome, dav and yourls to the deprecated-addons repository

This commit is contained in:
Tobias Diekershoff 2018-06-02 11:23:11 +02:00
commit 31520f804d
675 changed files with 195144 additions and 0 deletions

376
dav/common/calendar.fnk.php Normal file
View file

@ -0,0 +1,376 @@
<?php
use Friendica\Util\DateTimeFormat;
define("DAV_ACL_READ", "{DAV:}read");
define("DAV_ACL_WRITE", "{DAV:}write");
define("DAV_DISPLAYNAME", "{DAV:}displayname");
define("DAV_CALENDARCOLOR", "{http://apple.com/ns/ical/}calendar-color");
class DAVVersionMismatchException extends Exception
{
}
class vcard_source_data_email
{
public $email, $type;
function __construct($type, $email)
{
$this->email = $email;
$this->type = $type;
}
}
class vcard_source_data_homepage
{
public $homepage, $type;
function __construct($type, $homepage)
{
$this->homepage = $homepage;
$this->type = $type;
}
}
class vcard_source_data_telephone
{
public $telephone, $type;
function __construct($type, $telephone)
{
$this->telephone = $telephone;
$this->type = $type;
}
}
class vcard_source_data_socialnetwork
{
public $nick, $type, $url;
function __construct($type, $nick, $url)
{
$this->nick = $nick;
$this->type = $type;
$this->url = $url;
}
}
class vcard_source_data_address
{
public $street, $street2, $zip, $city, $country, $type;
}
class vcard_source_data_photo
{
public $binarydata;
public $width, $height;
public $type;
}
class vcard_source_data
{
function __construct($name_first, $name_middle, $name_last)
{
$this->name_first = $name_first;
$this->name_middle = $name_middle;
$this->name_last = $name_last;
}
public $name_first, $name_middle, $name_last;
public $last_update;
public $picture_data;
/** @var array|vcard_source_data_telephone[] $telephones */
public $telephones;
/** @var array|vcard_source_data_homepage[] $homepages */
public $homepages;
/** @var array|vcard_source_data_socialnetwork[] $socialnetworks */
public $socialnetworks;
/** @var array|vcard_source_data_email[] $email */
public $emails;
/** @var array|vcard_source_data_address[] $addresses */
public $addresses;
/** @var vcard_source_data_photo */
public $photo;
}
/**
* @param vcard_source_data $vcardsource
* @return string
*/
function vcard_source_compile($vcardsource)
{
$str = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Friendica//DAV-Plugin//EN\r\n";
$str .= "N:" . str_replace(";", ",", $vcardsource->name_last) . ";" . str_replace(";", ",", $vcardsource->name_first) . ";" . str_replace(";", ",", $vcardsource->name_middle) . ";;\r\n";
$str .= "FN:" . str_replace(";", ",", $vcardsource->name_first) . " " . str_replace(";", ",", $vcardsource->name_middle) . " " . str_replace(";", ",", $vcardsource->name_last) . "\r\n";
$str .= "REV:" . str_replace(" ", "T", $vcardsource->last_update) . "Z\r\n";
$item_count = 0;
for ($i = 0; $i < count($vcardsource->homepages); $i++) {
if ($i == 0) $str .= "URL;type=" . $vcardsource->homepages[0]->type . ":" . $vcardsource->homepages[0]->homepage . "\r\n";
else {
$c = ++$item_count;
$str .= "item$c.URL;type=" . $vcardsource->homepages[0]->type . ":" . $vcardsource->homepages[0]->homepage . "\r\n";
$str .= "item$c.X-ABLabel:_\$!<HomePage>!\$_\r\n";
}
}
if (is_object($vcardsource->photo)) {
$data = base64_encode($vcardsource->photo->binarydata);
$str .= "PHOTO;ENCODING=BASE64;TYPE=" . $vcardsource->photo->type . ":" . $data . "\r\n";
}
if (isset($vcardsource->socialnetworks) && is_array($vcardsource->socialnetworks)) foreach ($vcardsource->socialnetworks as $netw) switch ($netw->type) {
case "dfrn":
$str .= "X-SOCIALPROFILE;type=dfrn;x-user=" . $netw->nick . ":" . $netw->url . "\r\n";
break;
case "facebook":
$str .= "X-SOCIALPROFILE;type=facebook;x-user=" . $netw->nick . ":" . $netw->url . "\r\n";
break;
case "twitter":
$str .= "X-SOCIALPROFILE;type=twitter;x-user=" . $netw->nick . ":" . $netw->url . "\r\n";
break;
}
$str .= "END:VCARD\r\n";
return $str;
}
/**
* @param int $phpDate (UTC)
* @return string (Lokalzeit)
*/
function wdcal_php2MySqlTime($phpDate)
{
return date(DateTimeFormat::MYSQL, $phpDate);
}
/**
* @param string $sqlDate
* @return int
*/
function wdcal_mySql2PhpTime($sqlDate)
{
$ts = DateTime::createFromFormat(DateTimeFormat::MYSQL, $sqlDate);
return $ts->format("U");
}
/**
* @param string $myqlDate
* @return array
*/
function wdcal_mySql2icalTime($myqlDate)
{
$x = explode(" ", $myqlDate);
$y = explode("-", $x[0]);
$ret = array("year"=> $y[0], "month"=> $y[1], "day"=> $y[2]);
$y = explode(":", $x[1]);
$ret["hour"] = $y[0];
$ret["minute"] = $y[1];
$ret["second"] = $y[2];
return $ret;
}
/**
* @param string $str
* @return string
*/
function icalendar_sanitize_string($str = "")
{
return preg_replace("/[\\r\\n]+/siu", "\r\n", $str);
}
/**
* @return Sabre_CalDAV_AnimexxCalendarRootNode
*/
function dav_createRootCalendarNode()
{
$backends = array(Sabre_CalDAV_Backend_Private::getInstance());
foreach ($GLOBALS["CALDAV_PRIVATE_SYSTEM_BACKENDS"] as $backendclass) $backends[] = $backendclass::getInstance();
return new Sabre_CalDAV_AnimexxCalendarRootNode(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), $backends);
}
/**
* @return Sabre_CardDAV_AddressBookRootFriendica
*/
function dav_createRootContactsNode()
{
$backends = array(Sabre_CardDAV_Backend_Std::getInstance());
foreach ($GLOBALS["CARDDAV_PRIVATE_SYSTEM_BACKENDS"] as $backendclass) $backends[] = $backendclass::getInstance();
return new Sabre_CardDAV_AddressBookRootFriendica(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), $backends);
}
/**
* @param bool $force_authentication
* @param bool $needs_caldav
* @param bool $needs_carddav
* @return Sabre_DAV_Server
*/
function dav_create_server($force_authentication = false, $needs_caldav = true, $needs_carddav = true)
{
$arr = array(
new Sabre_DAV_SimpleCollection('principals', array(
new Sabre_CalDAV_Principal_Collection(Sabre_DAVACL_PrincipalBackend_Std::getInstance(), "principals/users"),
)),
);
if ($needs_caldav) $arr[] = dav_createRootCalendarNode();
if ($needs_carddav) $arr[] = dav_createRootContactsNode();
$tree = new Sabre_DAV_SimpleCollection('root', $arr);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($tree);
if (CALDAV_URL_PREFIX != "") $server->setBaseUri(CALDAV_URL_PREFIX);
$authPlugin = new Sabre_DAV_Auth_Plugin(Sabre_DAV_Auth_Backend_Std::getInstance(), DAV_APPNAME);
$server->addPlugin($authPlugin);
if ($needs_caldav) {
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
}
if ($needs_carddav) {
$carddavPlugin = new Sabre_CardDAV_Plugin();
$server->addPlugin($carddavPlugin);
}
if ($GLOBALS["CALDAV_ACL_PLUGIN_CLASS"] != "") {
$aclPlugin = new $GLOBALS["CALDAV_ACL_PLUGIN_CLASS"]();
$aclPlugin->defaultUsernamePath = "principals/users";
$server->addPlugin($aclPlugin);
} else {
$aclPlugin = new Sabre_DAVACL_Plugin();
$aclPlugin->defaultUsernamePath = "principals/users";
$server->addPlugin($aclPlugin);
}
if ($force_authentication) $server->broadcastEvent('beforeMethod', array("GET", "/")); // Make it authenticate
return $server;
}
/**
* @param Sabre_DAV_Server $server
* @param string $with_privilege
* @return array|Sabre_CalDAV_Calendar[]
*/
function dav_get_current_user_calendars(&$server, $with_privilege = "")
{
if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
$a = get_app();
$calendar_path = "/calendars/" . strtolower($a->user["nickname"]) . "/";
/** @var Sabre_CalDAV_AnimexxUserCalendars $tree */
$tree = $server->tree->getNodeForPath($calendar_path);
/** @var array|Sabre_CalDAV_Calendar[] $calendars */
$children = $tree->getChildren();
$calendars = array();
/** @var Sabre_DAVACL_Plugin $aclplugin */
$aclplugin = $server->getPlugin("acl");
foreach ($children as $child) if (is_a($child, "Sabre_CalDAV_Calendar") || is_subclass_of($child, "Sabre_CalDAV_Calendar")) {
if ($with_privilege != "") {
$caluri = $calendar_path . $child->getName();
if ($aclplugin->checkPrivileges($caluri, $with_privilege, Sabre_DAVACL_Plugin::R_PARENT, false)) $calendars[] = $child;
} else {
$calendars[] = $child;
}
}
return $calendars;
}
/**
* @param Sabre_DAV_Server $server
* @param Sabre_CalDAV_Calendar $calendar
* @param string $calendarobject_uri
* @param string $with_privilege
* @return null|Sabre\VObject\Component\VCalendar
*/
function dav_get_current_user_calendarobject(&$server, &$calendar, $calendarobject_uri, $with_privilege = "")
{
$obj = $calendar->getChild($calendarobject_uri);
if ($with_privilege == "") $with_privilege = DAV_ACL_READ;
$a = get_app();
$uri = "/calendars/" . strtolower($a->user["nickname"]) . "/" . $calendar->getName() . "/" . $calendarobject_uri;
/** @var Sabre_DAVACL_Plugin $aclplugin */
$aclplugin = $server->getPlugin("acl");
if (!$aclplugin->checkPrivileges($uri, $with_privilege, Sabre_DAVACL_Plugin::R_PARENT, false)) return null;
$data = $obj->get();
$vObject = Sabre\VObject\Reader::read($data);
return $vObject;
}
/**
* @param Sabre_DAV_Server $server
* @param int $id
* @param string $with_privilege
* @return null|Sabre_CalDAV_Calendar
*/
function dav_get_current_user_calendar_by_id(&$server, $id, $with_privilege = "")
{
$calendars = dav_get_current_user_calendars($server, $with_privilege);
$calendar = null;
foreach ($calendars as $cal) {
$prop = $cal->getProperties(array("id"));
if (isset($prop["id"]) && $prop["id"] == $id) $calendar = $cal;
}
return $calendar;
}
/**
* @param string $uid
* @return Sabre\VObject\Component\VCalendar $vObject
*/
function dav_create_empty_vevent($uid = "")
{
if ($uid == "") $uid = uniqid();
return Sabre\VObject\Reader::read("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//" . DAV_APPNAME . "//DAV-Plugin//EN\r\nBEGIN:VEVENT\r\nUID:" . $uid . "@" . dav_compat_get_hostname() .
"\r\nDTSTAMP:" . date("Ymd") . "T" . date("His") . "Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n");
}
/**
* @param Sabre\VObject\Component\VCalendar $vObject
* @return Sabre\VObject\Component\VEvent|null
*/
function dav_get_eventComponent(&$vObject)
{
$component = null;
$componentType = "";
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if ($componentType != "VEVENT") return null;
return $component;
}

View file

@ -0,0 +1,188 @@
<?php
use Friendica\Util\DateTimeFormat;
/**
* @param Sabre\VObject\Component\VAlarm $alarm
* @param Sabre\VObject\Component\VEvent|Sabre\VObject\Component\VTodo $parent
* @return DateTime|null
* @throws Sabre_DAV_Exception
*/
function renderCalDavEntry_calcalarm(&$alarm, &$parent)
{
$trigger = $alarm->__get("TRIGGER");
if (!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
$triggerDuration = Sabre\VObject\DateTimeParser::parseDuration($trigger->value);
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
if ($related === 'START') {
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $parent->__get("DTSTART");
$effectiveTrigger = $dtstart->getDateTime();
$effectiveTrigger->add($triggerDuration);
} else {
if ($parent->name === 'VTODO') {
$endProp = 'DUE';
} else {
$endProp = 'DTEND';
}
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $parent->__get("DTSTART");
if (isset($parent->$endProp)) {
$effectiveTrigger = clone $parent->$endProp->getDateTime();
$effectiveTrigger->add($triggerDuration);
} elseif ($parent->__get("DURATION") != "") {
$effectiveTrigger = clone $dtstart->getDateTime();
$duration = Sabre\VObject\DateTimeParser::parseDuration($parent->__get("DURATION"));
$effectiveTrigger->add($duration);
$effectiveTrigger->add($triggerDuration);
} else {
$effectiveTrigger = clone $dtstart->getDateTime();
$effectiveTrigger->add($triggerDuration);
}
}
} else {
// ??? @TODO
$effectiveTrigger = $trigger->getDateTime();
}
return $effectiveTrigger;
}
/**
* @param array $calendar
* @param array $calendarobject
* @throws Sabre_DAV_Exception_BadRequest
* @return void
*/
function renderCalDavEntry_data(&$calendar, &$calendarobject)
{
/** @var Sabre\VObject\Component\VCalendar $vObject */
$vObject = Sabre\VObject\Reader::read($calendarobject["calendardata"]);
$componentType = null;
/** @var Sabre\VObject\Component\VEvent $component */
$component = null;
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if (!$componentType) {
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
}
$timezoneOffset = date("P"); // @TODO Get the actual timezone from the event
if ($componentType !== 'VEVENT') return;
$event = array(
"description" => ($component->__get("DESCRIPTION") ? $component->__get("DESCRIPTION")->value : null),
"summary" => ($component->__get("SUMMARY") ? $component->__get("SUMMARY")->value : null),
"location" => ($component->__get("LOCATION") ? $component->__get("LOCATION")->value : null),
"color" => ($component->__get("X-ANIMEXX-COLOR") ? $component->__get("X-ANIMEXX-COLOR")->value : null),
);
$recurring = ($component->__get("RRULE") ? 1 : 0);
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$allday = ($dtstart->getDateType() == Sabre\VObject\Property\DateTime::DATE ? 1 : 0);
/** @var array|Sabre\VObject\Component\VAlarm[] $alarms */
$alarms = array();
foreach ($component->getComponents() as $a_component) if ($a_component->name == "VALARM") {
/** var Sabre\VObject\Component\VAlarm $component */
$alarms[] = $a_component;
}
$it = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
$last_end = 0;
$max_ts = mktime(0, 0, 0, 1, 1, CALDAV_MAX_YEAR * 1);
$first = true;
while ($it->valid() && $last_end < $max_ts && ($recurring || $first)) {
$first = false;
$last_end = $it->getDtEnd()->getTimestamp();
$start = $it->getDtStart()->getTimestamp();
q("INSERT INTO %s%sjqcalendar (`calendar_id`, `calendarobject_id`, `Summary`, `StartTime`, `EndTime`, `IsEditable`, `IsAllDayEvent`, `IsRecurring`, `Color`) VALUES
(%d, %d, '%s', CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d, %d, %d, '%s')",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), dbesc($event["summary"]), date(DateTimeFormat::MYSQL, $start),
date(DateTimeFormat::MYSQL, $last_end), 1, $allday, $recurring, dbesc(substr($event["color"], 1))
);
foreach ($alarms as $alarm) {
$alarm = renderCalDavEntry_calcalarm($alarm, $component);
$notified = ($alarm->getTimestamp() < time() ? 1 : 0);
q("INSERT INTO %s%snotifications (`calendar_id`, `calendarobject_id`, `alert_date`, `notified`) VALUES (%d, %d, CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), $alarm->format(DateTimeFormat::MYSQL), $notified
);
}
$it->next();
}
return;
}
/**
*
*/
function renderAllCalDavEntries()
{
q("DELETE FROM %s%sjqcalendar", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
q("DELETE FROM %s%snotifications", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
$calendars = q("SELECT * FROM %s%scalendars", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
$anz = count($calendars);
$i = 0;
foreach ($calendars as $calendar) {
$i++;
if (($i % 100) == 0) echo "$i / $anz\n";
$calobjs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]));
foreach ($calobjs as $calobj) renderCalDavEntry_data($calendar, $calobj);
}
}
/**
* @param string $uri
* @return bool
*/
function renderCalDavEntry_uri($uri)
{
$calobj = q("SELECT * FROM %s%scalendarobjects WHERE `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($uri));
if (count($calobj) == 0) return false;
q("DELETE FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]), IntVal($calobj[0]["id"]));
q("DELETE FROM %s%snotifications WHERE `calendar_id` = %d AND `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]), IntVal($calobj[0]["id"]));
$calendars = q("SELECT * FROM %s%scalendars WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]));
renderCalDavEntry_data($calendars[0], $calobj[0]);
return true;
}
/**
* @param int $calobj_id
* @return bool
*/
function renderCalDavEntry_calobj_id($calobj_id)
{
$calobj_id = IntVal($calobj_id);
q("DELETE FROM %s%sjqcalendar WHERE `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
q("DELETE FROM %s%snotifications WHERE `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
$calobj = q("SELECT * FROM %s%scalendarobjects WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calobj_id);
if (count($calobj) == 0) return false;
$calendars = q("SELECT * FROM %s%scalendars WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calobj[0]["calendar_id"]));
renderCalDavEntry_data($calendars[0], $calobj[0]);
return true;
}

View file

@ -0,0 +1,287 @@
<?php
abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
{
/**
* @var array
*/
protected $propertyMap = array(
'{DAV:}displayname' => 'displayname',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
);
/**
* @abstract
* @return int
*/
abstract public function getNamespace();
/**
* @static
* @abstract
* @return string
*/
abstract public static function getBackendTypeName();
/**
* @param int $calendarId
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
abstract public function listItemsByRange($calendarId, $sd, $ed, $base_path);
/**
* @var array
*/
static private $calendarCache = array();
/**
* @var array
*/
static private $calendarObjectCache = array();
/**
* @static
* @param int $calendarId
* @return array
*/
static public function loadCalendarById($calendarId)
{
if (!isset(self::$calendarCache[$calendarId])) {
$c = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarCache[$calendarId] = $c[0];
}
return self::$calendarCache[$calendarId];
}
/**
* @static
* @param int $obj_id
* @return array
*/
static public function loadCalendarobjectById($obj_id)
{
if (!isset(self::$calendarObjectCache[$obj_id])) {
$o = q("SELECT * FROM %s%scalendarobjects WHERE `id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
);
self::$calendarObjectCache[$obj_id] = $o[0];
}
return self::$calendarObjectCache[$obj_id];
}
/**
* @static
* @param Sabre\VObject\Component\VEvent $component
* @return int
*/
public static function getDtEndTimeStamp(&$component)
{
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
if ($component->__get("DTEND")) {
/** @var Sabre\VObject\Property\DateTime $dtend */
$dtend = $component->__get("DTEND");
return $dtend->getDateTime()->getTimeStamp();
} elseif ($component->__get("DURATION")) {
$endDate = clone $dtstart->getDateTime();
$endDate->add(Sabre\VObject\DateTimeParser::parse($component->__get("DURATION")->value));
return $endDate->getTimeStamp();
} elseif ($dtstart->getDateType() === Sabre\VObject\Property\DateTime::DATE) {
$endDate = clone $dtstart->getDateTime();
$endDate->modify('+1 day');
return $endDate->getTimeStamp();
} else {
return $dtstart->getDateTime()->getTimeStamp() + 3600;
}
}
/**
* Parses some information from calendar objects, used for optimized
* calendar-queries.
*
* Returns an array with the following keys:
* * etag
* * size
* * componentType
* * firstOccurence
* * lastOccurence
*
* @param string $calendarData
* @throws Sabre_DAV_Exception_BadRequest
* @return array
*/
protected function getDenormalizedData($calendarData)
{
/** @var Sabre\VObject\Component\VEvent $vObject */
$vObject = Sabre\VObject\Reader::read($calendarData);
$componentType = null;
$component = null;
$firstOccurence = null;
$lastOccurence = null;
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
$componentType = $component->name;
break;
}
}
if (!$componentType) {
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
}
if ($componentType === 'VEVENT') {
/** @var Sabre\VObject\Component\VEvent $component */
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$firstOccurence = $dtstart->getDateTime()->getTimeStamp();
// Finding the last occurence is a bit harder
if (!$component->__get("RRULE")) {
$lastOccurence = self::getDtEndTimeStamp($component);
} else {
$it = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
$maxDate = new DateTime(CALDAV_MAX_YEAR . "-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,
);
}
/**
* Updates properties for a calendar.
*
* The mutations array uses the propertyName in clark-notation as key,
* and the array value for the property value. In the case a property
* should be deleted, the property value will be null.
*
* This method must be atomic. If one property cannot be changed, the
* entire operation must fail.
*
* If the operation was successful, true can be returned.
* If the operation failed, false can be returned.
*
* Deletion of a non-existent property is always successful.
*
* Lastly, it is optional to return detailed information about any
* failures. In this case an array should be returned with the following
* structure:
*
* array(
* 403 => array(
* '{DAV:}displayname' => null,
* ),
* 424 => array(
* '{DAV:}owner' => null,
* )
* )
*
* In this example it was forbidden to update {DAV:}displayname.
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
* (424 Failed Dependency) because the request needs to be atomic.
*
* @param mixed $calendarId
* @param array $mutations
* @return bool|array
*/
public function updateCalendar($calendarId, array $mutations)
{
$newValues = array();
$result = array(
200 => array(), // Ok
403 => array(), // Forbidden
424 => array(), // Failed Dependency
);
$hasError = false;
foreach ($mutations as $propertyName=> $propertyValue) {
// We don't know about this property.
if (!isset($this->propertyMap[$propertyName])) {
$hasError = true;
$result[403][$propertyName] = null;
unset($mutations[$propertyName]);
continue;
}
$fieldName = $this->propertyMap[$propertyName];
$newValues[$fieldName] = $propertyValue;
}
// If there were any errors we need to fail the request
if ($hasError) {
// Properties has the remaining properties
foreach ($mutations as $propertyName=> $propertyValue) {
$result[424][$propertyName] = null;
}
// Removing unused statuscodes for cleanliness
foreach ($result as $status=> $properties) {
if (is_array($properties) && count($properties) === 0) unset($result[$status]);
}
return $result;
}
$this->increaseCalendarCtag($calendarId);
$valuesSql = array();
foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
if (count($valuesSql) > 0) {
q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
}
return true;
}
/**
* @param int $calendarId
*/
protected function increaseCalendarCtag($calendarId)
{
q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
self::$calendarObjectCache = array();
}
/**
* @abstract
* @param int $calendar_id
* @param int $calendarobject_id
* @return string
*/
abstract function getItemDetailRedirect($calendar_id, $calendarobject_id);
}

View file

@ -0,0 +1,512 @@
<?php
use Friendica\Core\L10n;
class Sabre_CalDAV_Backend_Private extends Sabre_CalDAV_Backend_Common
{
/**
* @var null|Sabre_CalDAV_Backend_Private
*/
private static $instance = null;
/**
* @static
* @return Sabre_CalDAV_Backend_Private
*/
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new Sabre_CalDAV_Backend_Private();
}
return self::$instance;
}
/**
* @return int
*/
public function getNamespace()
{
return CALDAV_NAMESPACE_PRIVATE;
}
/**
* @static
* @return string
*/
public static function getBackendTypeName()
{
return L10n::t("Private Events");
}
/**
* @obsolete
* @param array $calendar
* @param int $user
* @return array
*/
public function getPermissionsCalendar($calendar, $user)
{
if ($calendar["namespace"] == CALDAV_NAMESPACE_PRIVATE && $user == $calendar["namespace_id"]) return array("read"=> true, "write"=> true);
return array("read"=> false, "write"=> false);
}
/**
* @obsolete
* @param array $calendar
* @param int $user
* @param string $calendarobject_id
* @param null|array $item_arr
* @return array
*/
public function getPermissionsItem($calendar, $user, $calendarobject_id, $item_arr = null)
{
return $this->getPermissionsCalendar($calendar, $user);
}
/**
* @param array $row
* @param array $calendar
* @param string $base_path
* @return array
*/
private function jqcal2wdcal($row, $calendar, $base_path)
{
$not = q("SELECT COUNT(*) num FROM %s%snotifications WHERE `calendar_id` = %d AND `calendarobject_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($row["calendar_id"]), IntVal($row["calendarobject_id"])
);
$editable = $this->getPermissionsItem($calendar["namespace_id"], $row["calendarobject_id"], $row);
$end = wdcal_mySql2PhpTime($row["EndTime"]);
if ($row["IsAllDayEvent"]) $end -= 1;
return array(
"jq_id" => $row["id"],
"ev_id" => $row["calendarobject_id"],
"summary" => escape_tags($row["Summary"]),
"start" => wdcal_mySql2PhpTime($row["StartTime"]),
"end" => $end,
"is_allday" => $row["IsAllDayEvent"],
"is_moredays" => 0,
"is_recurring" => $row["IsRecurring"],
"color" => (is_null($row["Color"]) || $row["Color"] == "" ? $calendar["calendarcolor"] : $row["Color"]),
"is_editable" => ($editable ? 1 : 0),
"is_editable_quick" => ($editable && !$row["IsRecurring"] ? 1 : 0),
"location" => "Loc.",
"attendees" => '',
"has_notification" => ($not[0]["num"] > 0 ? 1 : 0),
"url_detail" => $base_path . $row["calendarobject_id"] . "/",
"url_edit" => $base_path . $row["calendarobject_id"] . "/edit/",
"special_type" => "",
);
}
/**
* @param int $calendarId
* @param string $sd
* @param string $ed
* @param string $base_path
* @return array
*/
public function listItemsByRange($calendarId, $sd, $ed, $base_path)
{
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
$von = wdcal_php2MySqlTime($sd);
$bis = wdcal_php2MySqlTime($ed);
$timezoneOffset = date("P");
// @TODO Events, die früher angefangen haben, aber noch andauern
$evs = q("SELECT *, CONVERT_TZ(`StartTime`, @@session.time_zone, '$timezoneOffset') StartTime, CONVERT_TZ(`EndTime`, @@session.time_zone, '$timezoneOffset') EndTime
FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `StartTime` between '%s' and '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($von), dbesc($bis));
$events = array();
foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/");
return $events;
}
/**
* @param int $calendar_id
* @param int $calendarobject_id
* @return string
*/
public function getItemDetailRedirect($calendar_id, $calendarobject_id)
{
return "/dav/wdcal/$calendar_id/$calendarobject_id/edit/";
}
/**
* Returns a list of calendars for a principal.
*
* Every project is an array with the following keys:
* * id, a unique id that will be used by other functions to modify the
* calendar. This can be the same as the uri or a database key.
* * uri, which the basename of the uri with which the calendar is
* accessed.
* * principaluri. The owner of the calendar. Almost always the same as
* principalUri passed to this method.
*
* Furthermore it can contain webdav properties in clark notation. A very
* common one is '{DAV:}displayname'.
*
* @param string $principalUri
* @throws DAVVersionMismatchException
* @return array
*/
public function getCalendarsForUser($principalUri)
{
$n = dav_compat_principal2namespace($principalUri);
if ($n["namespace"] != $this->getNamespace()) return array();
$cals = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), IntVal($n["namespace_id"]));
$ret = array();
foreach ($cals as $cal) {
if (!isset($cal["uri"])) throw new DAVVersionMismatchException();
if (in_array($cal["uri"], $GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"])) continue;
$components = array();
if ($cal["has_vevent"]) $components[] = "VEVENT";
if ($cal["has_vtodo"]) $components[] = "VTODO";
$dat = array(
"id" => $cal["id"],
"uri" => $cal["uri"],
"principaluri" => $principalUri,
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag'] ? $cal['ctag'] : '0',
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
"calendar_class" => "Sabre_CalDAV_Calendar_Private",
);
foreach ($this->propertyMap as $key=> $field) $dat[$key] = $cal[$field];
$ret[] = $dat;
}
return $ret;
}
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @throws Sabre_DAV_Exception|Sabre_DAV_Exception_Conflict
* @return string|void
*/
public function createCalendar($principalUri, $calendarUri, array $properties)
{
$uid = dav_compat_principal2uid($principalUri);
$r = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, dbesc($calendarUri));
if (count($r) > 0) throw new Sabre_DAV_Exception_Conflict("A calendar with this URI already exists");
$keys = array("`namespace`", "`namespace_id`", "`ctag`", "`uri`");
$vals = array(CALDAV_NAMESPACE_PRIVATE, IntVal($uid), 1, "'" . dbesc($calendarUri) . "'");
// Default value
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
$has_vevent = $has_vtodo = 1;
if (isset($properties[$sccs])) {
if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
}
$v = $properties[$sccs]->getValue();
$has_vevent = $has_vtodo = 0;
foreach ($v as $w) {
if (mb_strtolower($w) == "vevent") $has_vevent = 1;
if (mb_strtolower($w) == "vtodo") $has_vtodo = 1;
}
}
$keys[] = "`has_vevent`";
$keys[] = "`has_vtodo`";
$vals[] = $has_vevent;
$vals[] = $has_vtodo;
foreach ($this->propertyMap as $xmlName=> $dbName) {
if (isset($properties[$xmlName])) {
$keys[] = "`$dbName`";
$vals[] = "'" . dbesc($properties[$xmlName]) . "'";
}
}
$sql = sprintf("INSERT INTO %s%scalendars (" . implode(', ', $keys) . ") VALUES (" . implode(', ', $vals) . ")", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
q($sql);
$x = q("SELECT id FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, $calendarUri
);
return $x[0]["id"];
}
/**
* Updates properties for a calendar.
*
* The mutations array uses the propertyName in clark-notation as key,
* and the array value for the property value. In the case a property
* should be deleted, the property value will be null.
*
* This method must be atomic. If one property cannot be changed, the
* entire operation must fail.
*
* If the operation was successful, true can be returned.
* If the operation failed, false can be returned.
*
* Deletion of a non-existent property is always successful.
*
* Lastly, it is optional to return detailed information about any
* failures. In this case an array should be returned with the following
* structure:
*
* array(
* 403 => array(
* '{DAV:}displayname' => null,
* ),
* 424 => array(
* '{DAV:}owner' => null,
* )
* )
*
* In this example it was forbidden to update {DAV:}displayname.
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
* (424 Failed Dependency) because the request needs to be atomic.
*
* @param string $calendarId
* @param array $mutations
* @return bool|array
*/
public function updateCalendar($calendarId, array $mutations)
{
$newValues = array();
$result = array(
200 => array(), // Ok
403 => array(), // Forbidden
424 => array(), // Failed Dependency
);
$hasError = false;
foreach ($mutations as $propertyName=> $propertyValue) {
// We don't know about this property.
if (!isset($this->propertyMap[$propertyName])) {
$hasError = true;
$result[403][$propertyName] = null;
unset($mutations[$propertyName]);
continue;
}
$fieldName = $this->propertyMap[$propertyName];
$newValues[$fieldName] = $propertyValue;
}
// If there were any errors we need to fail the request
if ($hasError) {
// Properties has the remaining properties
foreach ($mutations as $propertyName=> $propertyValue) {
$result[424][$propertyName] = null;
}
// Removing unused statuscodes for cleanliness
foreach ($result as $status=> $properties) {
if (is_array($properties) && count($properties) === 0) unset($result[$status]);
}
return $result;
}
$sql = "`ctag` = `ctag` + 1";
foreach ($newValues as $key=> $val) $sql .= ", `" . $key . "` = '" . dbesc($val) . "'";
$sql = sprintf("UPDATE %s%scalendars SET $sql WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
q($sql);
return true;
}
/**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @return void
*/
public function deleteCalendar($calendarId)
{
q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
q("DELETE FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
}
/**
* Returns all calendar objects within a calendar.
*
* Every item contains an array with the following keys:
* * id - unique identifier which will be used for subsequent updates
* * calendardata - The iCalendar-compatible calendar data
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
* * lastmodified - a timestamp of the last modification time
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
* ' "abcdef"')
* * calendarid - The calendarid as it was passed to this function.
* * size - The size of the calendar objects, in bytes.
*
* Note that the etag is optional, but it's highly encouraged to return for
* speed reasons.
*
* The calendardata is also optional. If it's not returned
* 'getCalendarObject' will be called later, which *is* expected to return
* calendardata.
*
* If neither etag or size are specified, the calendardata will be
* used/fetched to determine these numbers. If both are specified the
* amount of times this is needed is reduced by a great degree.
*
* @param mixed $calendarId
* @return array
*/
function getCalendarObjects($calendarId)
{
$objs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
$ret = array();
foreach ($objs as $obj) {
$ret[] = array(
"id" => IntVal($obj["id"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["uri"],
"lastmodified" => $obj["lastmodified"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
}
return $ret;
}
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
function getCalendarObject($calendarId, $objectUri)
{
$o = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
if (count($o) > 0) {
$o[0]["calendarid"] = $calendarId;
$o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]);
return $o[0];
} else throw new Sabre_DAV_Exception_NotFound($calendarId . " / " . $objectUri);
}
/**
* Creates a new calendar object.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
function createCalendarObject($calendarId, $objectUri, $calendarData)
{
$calendarData = icalendar_sanitize_string($calendarData);
$extraData = $this->getDenormalizedData($calendarData);
q("INSERT INTO %s%scalendarobjects (`calendar_id`, `uri`, `calendardata`, `lastmodified`, `componentType`, `firstOccurence`, `lastOccurence`, `etag`, `size`)
VALUES (%d, '%s', '%s', NOW(), '%s', '%s', '%s', '%s', %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri), addslashes($calendarData), dbesc($extraData['componentType']),
dbesc(wdcal_php2MySqlTime($extraData['firstOccurence'])), dbesc(wdcal_php2MySqlTime($extraData['lastOccurence'])), dbesc($extraData["etag"]), IntVal($extraData["size"])
);
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_uri($objectUri);
return '"' . $extraData['etag'] . '"';
}
/**
* Updates an existing calendarobject, based on it's uri.
*
* It is possible return an etag from this function, which will be used in
* the response to this PUT request. Note that the ETag must be surrounded
* by double-quotes.
*
* However, you should only really return this ETag if you don't mangle the
* calendar-data. If the result of a subsequent GET to this object is not
* the exact same as this request body, you should omit the ETag.
*
* @param mixed $calendarId
* @param string $objectUri
* @param string $calendarData
* @return string|null
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData)
{
$calendarData = icalendar_sanitize_string($calendarData);
$extraData = $this->getDenormalizedData($calendarData);
q("UPDATE %s%scalendarobjects SET `calendardata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d, `componentType` = '%s', `firstOccurence` = '%s', `lastOccurence` = '%s'
WHERE `calendar_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($calendarData), dbesc($extraData["etag"]), IntVal($extraData["size"]), dbesc($extraData["componentType"]),
dbesc(wdcal_php2MySqlTime($extraData["firstOccurence"])), dbesc(wdcal_php2MySqlTime($extraData["lastOccurence"])), IntVal($calendarId), dbesc($objectUri));
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_uri($objectUri);
return '"' . $extraData['etag'] . '"';
}
/**
* Deletes an existing calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return void
*/
function deleteCalendarObject($calendarId, $objectUri)
{
$r = q("SELECT `id` FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
$this->increaseCalendarCtag($calendarId);
renderCalDavEntry_calobj_id($r[0]["id"]);
}
}

View file

@ -0,0 +1,186 @@
<?php
abstract class Sabre_CalDAV_Backend_Virtual extends Sabre_CalDAV_Backend_Common
{
/**
* @static
* @abstract
* @param int $calendarId
* @param string $uri
* @return array
*/
/*
abstract public function getItemsByUri($calendarId, $uri);
*/
/**
* @static
* @param int $uid
* @param int $namespace
*/
static public function invalidateCache($uid = 0, $namespace = 0) {
q("DELETE FROM %s%scal_virtual_object_sync WHERE `uid` = %d AND `namespace` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($uid), IntVal($namespace));
}
/**
* @static
* @abstract
* @param int $calendarId
*/
static abstract protected function createCache_internal($calendarId);
/**
* @static
* @param int $calendarId
*/
static protected function createCache($calendarId) {
$calendarId = IntVal($calendarId);
q("DELETE FROM %s%scal_virtual_object_cache WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
static::createCache_internal($calendarId);
q("REPLACE INTO %s%scal_virtual_object_sync (`calendar_id`, `date`) VALUES (%d, NOW())", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
}
/**
* @param string $calendarId
* @return array
*/
public function getCalendarObjects($calendarId)
{
$calendarId = IntVal($calendarId);
$r = q("SELECT COUNT(*) n FROM %s%scal_virtual_object_sync WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
if ($r[0]["n"] == 0) static::createCache($calendarId);
$r = q("SELECT * FROM %s%scal_virtual_object_cache WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $calendarId);
$ret = array();
foreach ($r as $obj) {
$ret[] = array(
"id" => IntVal($obj["data_uri"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["data_uri"],
"lastmodified" => $obj["date"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
}
return $ret;
}
/**
* Returns information from a single calendar object, based on it's object
* uri.
*
* The returned array must have the same keys as getCalendarObjects. The
* 'calendardata' object is required here though, while it's not required
* for getCalendarObjects.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getCalendarObject($calendarId, $objectUri)
{
$calendarId = IntVal($calendarId);
$r = q("SELECT COUNT(*) n FROM %s%scal_virtual_object_sync WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
if ($r[0]["n"] == 0) static::createCache($calendarId);
$r = q("SELECT * FROM %s%scal_virtual_object_cache WHERE `data_uri` = '%s' AND `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($objectUri), IntVal($calendarId));
if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
$obj = $r[0];
$ret = array(
"id" => IntVal($obj["data_uri"]),
"calendardata" => $obj["calendardata"],
"uri" => $obj["data_uri"],
"lastmodified" => $obj["date"],
"calendarid" => $calendarId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
return $ret;
}
/**
* Creates a new calendar for a principal.
*
* If the creation was a success, an id must be returned that can be used to reference
* this calendar in other methods, such as updateCalendar.
*
* @param string $principalUri
* @param string $calendarUri
* @param array $properties
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function createCalendar($principalUri, $calendarUri, array $properties)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Delete a calendar and all it's objects
*
* @param string $calendarId
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function deleteCalendar($calendarId)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Creates a new calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @throws Sabre_DAV_Exception_Forbidden
* @return null|string|void
*/
function createCalendarObject($calendarId, $objectUri, $calendarData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Updates an existing calendarobject, based on it's uri.
*
* @param string $calendarId
* @param string $objectUri
* @param string $calendarData
* @throws Sabre_DAV_Exception_Forbidden
* @return null|string|void
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Deletes an existing calendar object.
*
* @param string $calendarId
* @param string $objectUri
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
function deleteCalendarObject($calendarId, $objectUri)
{
throw new Sabre_DAV_Exception_Forbidden();
}
}

View file

@ -0,0 +1,50 @@
<?php
class Sabre_CalDAV_Calendar_Private extends Sabre_CalDAV_Calendar
{
public function getACL()
{
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
/*
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
'protected' => true,
),
array(
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
'principal' => '{DAV:}authenticated',
'protected' => true,
),
*/
);
}
}

View file

@ -0,0 +1,44 @@
<?php
class Sabre_CalDAV_Calendar_Virtual extends Sabre_CalDAV_Calendar {
/**
* Returns a list of ACE's for this node.
*
* Each ACE has the following properties:
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
* currently the only supported privileges
* * 'principal', a url to the principal who owns the node
* * 'protected' (optional), indicating that this ACE is not allowed to
* be updated.
*
* @return array
*/
public function getACL() {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
'protected' => true,
),
array(
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
'principal' => '{DAV:}authenticated',
'protected' => true,
),
);
}
}

View file

@ -0,0 +1,69 @@
<?php
/**
* Users collection
*
* This object is responsible for generating a collection of users.
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CalDAV_AnimexxCalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
/**
* CalDAV backend
*
* @var array|Sabre_CalDAV_Backend_Abstract[]
*/
protected $caldavBackends;
/**
* Constructor
*
* This constructor needs both an authentication and a caldav backend.
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param array|Sabre_CalDAV_Backend_Abstract[] $caldavBackends
* @param string $principalPrefix
*/
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,$caldavBackends, $principalPrefix = 'principals/users') {
parent::__construct($principalBackend, $principalPrefix);
$this->caldavBackends = $caldavBackends;
}
/**
* Returns the nodename
*
* We're overriding this, because the default will be the 'principalPrefix',
* and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT
*
* @return string
*/
public function getName() {
return Sabre_CalDAV_Plugin::CALENDAR_ROOT;
}
/**
* This method returns a node for a principal.
*
* The passed array contains principal information, and is guaranteed to
* at least contain a uri item. Other properties may or may not be
* supplied by the authentication backend.
*
* @param array $principal
* @return \Sabre_CalDAV_AnimexxUserCalendars|\Sabre_DAVACL_IPrincipal
*/
public function getChildForPrincipal(array $principal) {
return new Sabre_CalDAV_AnimexxUserCalendars($this->principalBackend, $this->caldavBackends, $principal['uri']);
}
}

View file

@ -0,0 +1,116 @@
<?php
abstract class Sabre_CardDAV_Backend_Common extends Sabre_CardDAV_Backend_Abstract
{
/**
* @abstract
* @return int
*/
abstract public function getNamespace();
/**
* @static
* @abstract
* @return string
*/
abstract public static function getBackendTypeName();
/**
* @var array
*/
static private $addressbookCache = array();
/**
* @var array
*/
static private $addressbookObjectCache = array();
/**
* @static
* @param int $addressbookId
* @return array
*/
static public function loadCalendarById($addressbookId)
{
if (!isset(self::$addressbookCache[$addressbookId])) {
$c = q("SELECT * FROM %s%saddressbooks WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId));
self::$addressbookCache[$addressbookId] = $c[0];
}
return self::$addressbookCache[$addressbookId];
}
/**
* @static
* @param int $obj_id
* @return array
*/
static public function loadAddressbookobjectById($obj_id)
{
if (!isset(self::$addressbookObjectCache[$obj_id])) {
$o = q("SELECT * FROM %s%saddressbookobjects WHERE `id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
);
self::$addressbookObjectCache[$obj_id] = $o[0];
}
return self::$addressbookObjectCache[$obj_id];
}
/**
* Updates an addressbook's properties
*
* See Sabre_DAV_IProperties for a description of the mutations array, as
* well as the return value.
*
* @param mixed $addressBookId
* @param array $mutations
* @throws Sabre_DAV_Exception_Forbidden
* @see Sabre_DAV_IProperties::updateProperties
* @return bool|array
*/
public function updateAddressBook($addressBookId, array $mutations)
{
$updates = array();
foreach ($mutations as $property=> $newValue) {
switch ($property) {
case '{DAV:}displayname' :
$updates['displayname'] = $newValue;
break;
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
$updates['description'] = $newValue;
break;
default :
// If any unsupported values were being updated, we must
// let the entire request fail.
return false;
}
}
// No values are being updated?
if (!$updates) {
return false;
}
$query = 'UPDATE ' . CALDAV_SQL_DB . CALDAV_SQL_PREFIX . 'addressbooks SET ctag = ctag + 1 ';
foreach ($updates as $key=> $value) {
$query .= ', `' . dbesc($key) . '` = ' . dbesc($key) . ' ';
}
$query .= ' WHERE id = ' . IntVal($addressBookId);
q($query);
return true;
}
/**
* @param int $addressbookId
*/
protected function increaseAddressbookCtag($addressbookId)
{
q("UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId));
self::$addressbookCache = array();
}
}

View file

@ -0,0 +1,277 @@
<?php
use Friendica\Core\L10n;
class Sabre_CardDAV_Backend_Std extends Sabre_CardDAV_Backend_Common
{
/**
* @var null|Sabre_CardDAV_Backend_Std
*/
private static $instance = null;
/**
* @static
* @return Sabre_CardDAV_Backend_Std
*/
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new Sabre_CardDAV_Backend_Std();
}
return self::$instance;
}
/**
* Sets up the object
*/
public function __construct()
{
}
/**
* @return int
*/
public function getNamespace()
{
return CARDDAV_NAMESPACE_PRIVATE;
}
/**
* @static
* @return string
*/
public static function getBackendTypeName()
{
return L10n::t("Private Addressbooks");
}
/**
* Returns the list of addressbooks for a specific user.
*
* @param string $principalUri
* @return array
*/
public function getAddressBooksForUser($principalUri)
{
$n = dav_compat_principal2namespace($principalUri);
if ($n["namespace"] != $this->getNamespace()) return array();
$addressBooks = array();
$books = q("SELECT * FROM %s%saddressbooks WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($n["namespace"]), IntVal($n["namespace_id"]));
foreach ($books as $row) {
if (in_array($row["uri"], $GLOBALS["CARDDAV_PRIVATE_SYSTEM_ADDRESSBOOKS"])) continue;
$addressBooks[] = array(
'id' => $row['id'],
'uri' => $row['uri'],
'principaluri' => $principalUri,
'{DAV:}displayname' => $row['displayname'],
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
'{http://calendarserver.org/ns/}getctag' => $row['ctag'],
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' =>
new Sabre_CardDAV_Property_SupportedAddressData(),
);
}
return $addressBooks;
}
/**
* Creates a new address book
*
* @param string $principalUri
* @param string $url Just the 'basename' of the url.
* @param array $properties
* @throws Sabre_DAV_Exception_BadRequest
* @return void
*/
public function createAddressBook($principalUri, $url, array $properties)
{
$uid = dav_compat_principal2uid($principalUri);
$values = array(
'displayname' => null,
'description' => null,
'principaluri' => $principalUri,
'uri' => $url,
);
foreach ($properties as $property=> $newValue) {
switch ($property) {
case '{DAV:}displayname' :
$values['displayname'] = $newValue;
break;
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
$values['description'] = $newValue;
break;
default :
throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
}
}
q("INSERT INTO %s%saddressbooks (`uri`, `displayname`, `description`, `namespace`, `namespace_id`, `ctag`) VALUES ('%s', '%s', '%s', %d, %d, 1)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($values["uri"]), dbesc($values["displayname"]), dbesc($values["description"]), CARDDAV_NAMESPACE_PRIVATE, IntVal($uid)
);
}
/**
* Deletes an entire addressbook and all its contents
*
* @param int $addressBookId
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function deleteAddressBook($addressBookId)
{
q("DELETE FROM %s%saddressbookobjects WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
q("DELETE FROM %s%saddressbooks WHERE `addressbook_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
}
/**
* Returns all cards for a specific addressbook id.
*
* This method should return the following properties for each card:
* * carddata - raw vcard data
* * uri - Some unique url
* * lastmodified - A unix timestamp
*
* It's recommended to also return the following properties:
* * etag - A unique etag. This must change every time the card changes.
* * size - The size of the card in bytes.
*
* If these last two properties are provided, less time will be spent
* calculating them. If they are specified, you can also ommit carddata.
* This may speed up certain requests, especially with large cards.
*
* @param string $addressbookId
* @return array
*/
public function getCards($addressbookId)
{
$r = q('SELECT `id`, `carddata`, `uri`, `lastmodified`, `etag`, `size`, `contact` FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `manually_deleted` = 0',
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId)
);
if ($r) return $r;
return array();
}
/**
* Returns a specfic card.
*
* The same set of properties must be returned as with getCards. The only
* exception is that 'carddata' is absolutely required.
*
* @param mixed $addressBookId
* @param string $cardUri
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getCard($addressBookId, $cardUri)
{
$x = q("SELECT `id`, `carddata`, `uri`, `lastmodified`, `etag`, `size` FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `uri` = '%s'",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId), dbesc($cardUri));
if (count($x) == 0) throw new Sabre_DAV_Exception_NotFound();
return $x[0];
}
/**
* Creates a new card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
*
* It is possible to return an ETag from this method. This ETag is for the
* newly created resource, and must be enclosed with double quotes (that
* is, the string itself must contain the double quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param string $addressBookId
* @param string $cardUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return string
*/
public function createCard($addressBookId, $cardUri, $cardData)
{
$etag = md5($cardData);
q("INSERT INTO %s%saddressbookobjects (`carddata`, `uri`, `lastmodified`, `addressbook_id`, `etag`, `size`) VALUES ('%s', '%s', NOW(), %d, '%s', %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($cardUri), IntVal($addressBookId), dbesc($etag), strlen($cardData)
);
q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return '"' . $etag . '"';
}
/**
* Updates a card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
*
* It is possible to return an ETag from this method. This ETag should
* match that of the updated resource, and must be enclosed with double
* quotes (that is: the string itself must contain the actual quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param string $addressBookId
* @param string $cardUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return string|null
*/
public function updateCard($addressBookId, $cardUri, $cardData)
{
$etag = md5($cardData);
q("UPDATE %s%saddressbookobjects SET `carddata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d, `manually_edited` = 1 WHERE `uri` = '%s' AND `addressbook_id` = %d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), dbesc($etag), strlen($cardData), dbesc($cardUri), IntVal($addressBookId)
);
q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return '"' . $etag . '"';
}
/**
* Deletes a card
*
* @param string $addressBookId
* @param string $cardUri
* @throws Sabre_DAV_Exception_Forbidden
* @return bool
*/
public function deleteCard($addressBookId, $cardUri)
{
q("DELETE FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId), dbesc($cardUri));
q('UPDATE %s%saddressbooks SET `ctag` = `ctag` + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return true;
}
}

View file

@ -0,0 +1,269 @@
<?php
abstract class Sabre_CardDAV_Backend_Virtual extends Sabre_CardDAV_Backend_Common
{
/**
* @static
* @abstract
* @param int $addressbookId
* @param string $uri
* @return array
*/
/*
abstract public function getItemsByUri($addressbookId, $uri);
*/
/**
* @static
* @param int $addressbookId
*/
static public function invalidateCache($addressbookId) {
q("UPDATE %s%saddressbooks SET `needs_rebuild` = 1 WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressbookId));
}
/**
* @static
* @abstract
* @param int $addressbookId
* @param bool $force
*/
static abstract protected function createCache_internal($addressbookId, $force = false);
/**
* @param int $addressbookId
* @param null|array $addressbook
* @param bool $force
*/
public function createCache($addressbookId, $addressbook = null, $force = false) {
$addressbookId = IntVal($addressbookId);
if (!$addressbook) {
$add = q("SELECT `needs_rebuild`, `uri` FROM %s%saddressbooks WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
$addressbook = $add[0];
}
if ($addressbook["needs_rebuild"] == 1 || $force) {
static::createCache_internal($addressbookId, $force);
q("UPDATE %s%saddressbooks SET `needs_rebuild` = 0, `ctag` = `ctag` + 1 WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
}
}
/**
* @static
* @abstract
* @param int $addressbookId
* @param int $contactId
* @param bool $force
*/
static abstract protected function createCardCache($addressbookId, $contactId, $force = false);
/**
* @param int $addressbookId
* @return array
*/
public function getCards($addressbookId)
{
$addressbookId = IntVal($addressbookId);
$add = q("SELECT * FROM %s%saddressbooks WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
if ($add[0]["needs_rebuild"]) {
static::createCache_internal($addressbookId);
q("UPDATE %s%saddressbooks SET `needs_rebuild` = 0, `ctag` = `ctag` + 1 WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
$add[0]["needs_rebuild"] = 0;
$add[0]["ctag"]++;
}
$ret = array();
$x = q("SELECT * FROM %s%saddressbookobjects WHERE `addressbook_id` = %d AND `manually_deleted` = 0", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
foreach ($x as $y) $ret[] = self::getCard($addressbookId, $add[0]["uri"], $add[0], $y);
return $ret;
}
/**
* Replaces the x-prop_name value. Replaces the prop_name value IF the old value is the same as the old value of x-prop_name (meaning: the user has not manually changed it)
*
* @param Sabre\VObject\Component $component
* @param string $prop_name
* @param string $prop_value
* @param array $parameters
* @return void
*/
static public function card_set_automatic_value(&$component, $prop_name, $prop_value, $parameters = array()) {
$automatic = $component->select("X-" . $prop_name);
$curr = $component->select($prop_name);
if (count($automatic) == 0) {
$prop = new Sabre\VObject\Property('X-' . $prop_name, $prop_value);
foreach ($parameters as $key=>$val) $prop->add($key, $val);
$component->children[] = $prop;
if (count($curr) == 0) {
$prop = new Sabre\VObject\Property($prop_name, $prop_value);
foreach ($parameters as $key=>$val) $prop->add($key, $val);
$component->children[] = $prop;
}
} else foreach ($automatic as $auto_prop) {
/** @var Sabre\VObject\Property $auto_prop */
/** @var Sabre\VObject\Property $actual_prop */
foreach ($curr as $actual_prop) {
if ($auto_prop->value == $actual_prop->value) $actual_prop->setValue($prop_value);
}
$auto_prop->setValue($prop_value);
}
}
/**
* Deletes the x-prop_name value. Deletes the prop_name value IF the old value is the same as the old value of x-prop_name (meaning: the user has not manually changed it)
*
* @param Sabre\VObject\Component $component
* @param string $prop_name
* @param array $parameters
*/
static public function card_del_automatic_value(&$component, $prop_name, $parameters = array()) {
// @TODO
}
/**
* @param int $addressbookId
* @param string $objectUri
* @param array $book
* @param array $obj
* @throws Sabre_DAV_Exception_NotFound
* @return array
*/
public function getCard($addressbookId, $objectUri, $book = null, $obj = null)
{
$addressbookId = IntVal($addressbookId);
if ($book == null) {
$add = q("SELECT `needs_rebuild`, `uri` FROM %s%saddressbooks WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
$book = $add[0];
}
if ($book["needs_rebuild"] == 1) {
static::createCache_internal($addressbookId);
q("UPDATE %s%saddressbooks SET `needs_rebuild` = 0, `ctag` = `ctag` + 1 WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $addressbookId);
$add[0]["needs_rebuild"] = 0;
}
if ($obj == null) {
$r = q("SELECT * FROM %s%saddressbookobjects WHERE `uri` = '%s' AND `addressbook_id` = %d AND `manually_deleted` = 0",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($objectUri), IntVal($addressbookId));
if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
$obj = $r[0];
if ($obj["needs_rebuild"] == 1) $obj = static::createCardCache($addressbookId, $obj["contact"]);
}
$ret = array(
"id" => IntVal($obj["uri"]),
"carddata" => $obj["carddata"],
"uri" => $obj["uri"],
"lastmodified" => $obj["lastmodified"],
"addressbookid" => $addressbookId,
"etag" => $obj["etag"],
"size" => IntVal($obj["size"]),
);
return $ret;
}
/**
* @param string $principalUri
* @param string $addressbookUri
* @param array $properties
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function createAddressBook($principalUri, $addressbookUri, array $properties)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* @param string $addressbookId
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function deleteAddressBook($addressbookId)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* @param string $addressbookId
* @param string $objectUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return null|string|void
*/
function createCard($addressbookId, $objectUri, $cardData)
{
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Updates a card.
*
* The addressbook id will be passed as the first argument. This is the
* same id as it is returned from the getAddressbooksForUser method.
*
* The cardUri is a base uri, and doesn't include the full path. The
* cardData argument is the vcard body, and is passed as a string.
*
* It is possible to return an ETag from this method. This ETag should
* match that of the updated resource, and must be enclosed with double
* quotes (that is: the string itself must contain the actual quotes).
*
* You should only return the ETag if you store the carddata as-is. If a
* subsequent GET request on the same card does not have the same body,
* byte-by-byte and you did return an ETag here, clients tend to get
* confused.
*
* If you don't return an ETag, you can just return null.
*
* @param string $addressBookId
* @param string $cardUri
* @param string $cardData
* @throws Sabre_DAV_Exception_Forbidden
* @return string|null
*/
public function updateCard($addressBookId, $cardUri, $cardData)
{
echo "Die!"; die(); // @TODO
$x = explode("-", $addressBookId);
$etag = md5($cardData);
q("UPDATE %s%scards SET carddata = '%s', lastmodified = %d, etag = '%s', size = %d, manually_edited = 1 WHERE uri = '%s' AND namespace = %d AND namespace_id =%d",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($cardData), time(), $etag, strlen($cardData), dbesc($cardUri), IntVal($x[10]), IntVal($x[1])
);
q('UPDATE %s%saddressbooks_community SET ctag = ctag + 1 WHERE uid = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($x[1]));
return '"' . $etag . '"';
}
/**
* Deletes a card
*
* @param string $addressBookId
* @param string $cardUri
* @throws Sabre_DAV_Exception_Forbidden
* @return bool
*/
public function deleteCard($addressBookId, $cardUri)
{
q("UPDATE %s%scards SET `manually_deleted` = 1 WHERE `addressbook_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId), dbesc($cardUri));
q('UPDATE %s%saddressbooks SET ctag = ctag + 1 WHERE `id` = %d', CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($addressBookId));
return true;
}
}

View file

@ -0,0 +1,64 @@
<?php
class Sabre_CardDAV_AddressBookRootFriendica extends Sabre_DAVACL_AbstractPrincipalCollection {
/**
* Principal Backend
*
* @var Sabre_DAVACL_IPrincipalBackend
*/
protected $principalBackend;
/**
* CardDAV backend
*
* @var array|Sabre_CardDAV_Backend_Abstract[]
*/
protected $carddavBackends;
/**
* Constructor
*
* This constructor needs both a principal and a carddav backend.
*
* By default this class will show a list of addressbook collections for
* principals in the 'principals' collection. If your main principals are
* actually located in a different path, use the $principalPrefix argument
* to override this.
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param array|Sabre_CardDAV_Backend_Abstract[] $carddavBackends
* @param string $principalPrefix
*/
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $carddavBackends, $principalPrefix = 'principals/users') {
$this->carddavBackends = $carddavBackends;
parent::__construct($principalBackend, $principalPrefix);
}
/**
* Returns the name of the node
*
* @return string
*/
public function getName() {
return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
}
/**
* This method returns a node for a principal.
*
* The passed array contains principal information, and is guaranteed to
* at least contain a uri item. Other properties may or may not be
* supplied by the authentication backend.
*
* @param array $principal
* @return Sabre_DAVACL_IPrincipal
*/
public function getChildForPrincipal(array $principal) {
return new Sabre_CardDAV_UserAddressBooksMultiBackend($this->carddavBackends, $principal['uri']);
}
}

View file

@ -0,0 +1,264 @@
<?php
/**
* The UserCalenders class contains all calendars associated to one user
*
* @package Sabre
* @subpackage CalDAV
* @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
* @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_CardDAV_UserAddressBooksMultiBackend extends Sabre_DAV_Collection implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
/**
* Principal uri
*
* @var array
*/
protected $principalUri;
/**
* carddavBackend
*
* @var array|Sabre_CardDAV_Backend_Abstract[]
*/
protected $carddavBackends;
/**
* Constructor
*
* @param array|Sabre_CardDAV_Backend_Abstract[] $carddavBackends
* @param string $principalUri
*/
public function __construct($carddavBackends, $principalUri) {
$this->carddavBackends = $carddavBackends;
$this->principalUri = $principalUri;
}
/**
* Returns the name of this object
*
* @return string
*/
public function getName() {
list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
return $name;
}
/**
* Updates the name of this object
*
* @param string $name
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function setName($name) {
throw new Sabre_DAV_Exception_MethodNotAllowed();
}
/**
* Deletes this object
*
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function delete() {
throw new Sabre_DAV_Exception_MethodNotAllowed();
}
/**
* Returns the last modification date
*
* @return int
*/
public function getLastModified() {
return null;
}
/**
* Creates a new file under this object.
*
* This is currently not allowed
*
* @param string $filename
* @param resource $data
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return null|string|void
*/
public function createFile($filename, $data=null) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
}
/**
* Creates a new directory under this object.
*
* This is currently not allowed.
*
* @param string $filename
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function createDirectory($filename) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
}
/**
* Returns a single calendar, by name
*
* @param string $name
* @throws Sabre_DAV_Exception_NotFound
* @todo needs optimizing
* @return \Sabre_CardDAV_AddressBook|\Sabre_DAV_INode
*/
public function getChild($name) {
foreach($this->getChildren() as $child) {
if ($name==$child->getName())
return $child;
}
throw new Sabre_DAV_Exception_NotFound('Addressbook with name \'' . $name . '\' could not be found');
}
/**
* Returns a list of addressbooks
*
* @return array|Sabre_DAV_INode[]
*/
public function getChildren() {
$objs = array();
foreach ($this->carddavBackends as $backend) {
$addressbooks = $backend->getAddressbooksForUser($this->principalUri);
foreach($addressbooks as $addressbook) {
$objs[] = new Sabre_CardDAV_AddressBook($backend, $addressbook);
}
}
return $objs;
}
/**
* Creates a new addressbook
*
* @param string $name
* @param array $resourceType
* @param array $properties
* @throws Sabre_DAV_Exception_InvalidResourceType
* @return void
*/
public function createExtendedCollection($name, array $resourceType, array $properties) {
if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
}
$this->carddavBackends[0]->createAddressBook($this->principalUri, $name, $properties);
}
/**
* Returns the owner principal
*
* This must be a url to a principal, or null if there's no owner
*
* @return string|null
*/
public function getOwner() {
return $this->principalUri;
}
/**
* Returns a group principal
*
* This must be a url to a principal, or null if there's no owner
*
* @return string|null
*/
public function getGroup() {
return null;
}
/**
* Returns a list of ACE's for this node.
*
* Each ACE has the following properties:
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
* currently the only supported privileges
* * 'principal', a url to the principal who owns the node
* * 'protected' (optional), indicating that this ACE is not allowed to
* be updated.
*
* @return array
*/
public function getACL() {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->principalUri,
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->principalUri,
'protected' => true,
),
);
}
/**
* Updates the ACL
*
* This method will receive a list of new ACE's.
*
* @param array $acl
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function setACL(array $acl) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
}
/**
* Returns the list of supported privileges for this node.
*
* The returned data structure is a list of nested privileges.
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
* standard structure.
*
* If null is returned from this method, the default privilege set is used,
* which is fine for most common usecases.
*
* @return array|null
*/
public function getSupportedPrivilegeSet() {
return null;
}
}

View file

@ -0,0 +1,296 @@
<?php
class Sabre_CalDAV_AnimexxUserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
/**
* Principal backend
*
* @var Sabre_DAVACL_IPrincipalBackend
*/
protected $principalBackend;
/**
* CalDAV backends
*
* @var array|Sabre_CalDAV_Backend_Common[]
*/
protected $caldavBackends;
/**
* Principal information
*
* @var array
*/
protected $principalInfo;
/**
* Constructor
*
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
* @param array|Sabre_CalDAV_Backend_Common[] $caldavBackends
* @param mixed $userUri
*/
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $caldavBackends, $userUri) {
$this->principalBackend = $principalBackend;
$this->caldavBackends = $caldavBackends;
$this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
}
/**
* Returns the name of this object
*
* @return string
*/
public function getName() {
list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
return $name;
}
/**
* Updates the name of this object
*
* @param string $name
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function setName($name) {
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Deletes this object
*
* @throws Sabre_DAV_Exception_Forbidden
* @return void
*/
public function delete() {
throw new Sabre_DAV_Exception_Forbidden();
}
/**
* Returns the last modification date
*
* @return int
*/
public function getLastModified() {
return null;
}
/**
* Creates a new file under this object.
*
* This is currently not allowed
*
* @param string $filename
* @param resource $data
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return null|string|void
*/
public function createFile($filename, $data=null) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
}
/**
* Creates a new directory under this object.
*
* This is currently not allowed.
*
* @param string $filename
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function createDirectory($filename) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
}
/**
* Returns a single calendar, by name
*
* @param string $name
* @throws Sabre_DAV_Exception_NotFound
* @todo needs optimizing
* @return \Sabre_CalDAV_Calendar|\Sabre_DAV_INode
*/
public function getChild($name) {
foreach($this->getChildren() as $child) {
if ($name==$child->getName())
return $child;
}
throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found');
}
/**
* Checks if a calendar exists.
*
* @param string $name
* @todo needs optimizing
* @return bool
*/
public function childExists($name) {
foreach($this->getChildren() as $child) {
if ($name==$child->getName())
return true;
}
return false;
}
/**
* Returns a list of calendars
*
* @return array|\Sabre_DAV_INode[]
*/
public function getChildren() {
$objs = array();
foreach ($this->caldavBackends as $backend) {
$calendars = $backend->getCalendarsForUser($this->principalInfo["uri"]);
foreach($calendars as $calendar) {
$objs[] = new $calendar["calendar_class"]($this->principalBackend, $backend, $calendar);
}
}
return $objs;
}
/**
* Creates a new calendar
*
* @param string $name
* @param array $resourceType
* @param array $properties
* @throws Sabre_DAV_Exception_InvalidResourceType
* @return void
*/
public function createExtendedCollection($name, array $resourceType, array $properties) {
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
}
$this->caldavBackends[0]->createCalendar($this->principalInfo['uri'], $name, $properties);
}
/**
* Returns the owner principal
*
* This must be a url to a principal, or null if there's no owner
*
* @return string|null
*/
public function getOwner() {
return $this->principalInfo['uri'];
}
/**
* Returns a group principal
*
* This must be a url to a principal, or null if there's no owner
*
* @return string|null
*/
public function getGroup() {
return null;
}
/**
* Returns a list of ACE's for this node.
*
* Each ACE has the following properties:
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
* currently the only supported privileges
* * 'principal', a url to the principal who owns the node
* * 'protected' (optional), indicating that this ACE is not allowed to
* be updated.
*
* @return array
*/
public function getACL() {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->principalInfo['uri'],
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}write',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
'protected' => true,
),
array(
'privilege' => '{DAV:}read',
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
'protected' => true,
),
);
}
/**
* Updates the ACL
*
* This method will receive a list of new ACE's.
*
* @param array $acl
* @throws Sabre_DAV_Exception_MethodNotAllowed
* @return void
*/
public function setACL(array $acl) {
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
}
/**
* Returns the list of supported privileges for this node.
*
* The returned data structure is a list of nested privileges.
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
* standard structure.
*
* If null is returned from this method, the default privilege set is used,
* which is fine for most common usecases.
*
* @return array|null
*/
function getSupportedPrivilegeSet()
{
return null;
}
}

221
dav/common/wdcal.js Normal file
View file

@ -0,0 +1,221 @@
function wdcal_edit_getStartEnd() {
"use strict";
var start = $("#cal_start_date").datepicker("getDate");
var start_time = $.timePicker("#cal_start_time").getTime();
start.setHours(start_time.getHours());
start.setMinutes(start_time.getMinutes());
var end = $("#cal_end_date").datepicker("getDate");
var end_time = $.timePicker("#cal_end_time").getTime();
end.setHours(end_time.getHours());
end.setMinutes(end_time.getMinutes());
return {"start": start, "end": end};
}
function wdcal_edit_checktime_startChanged() {
"use strict";
var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) {
var newend = new Date(time.start.getTime() + 3600000);
$("#cal_end_date").datepicker("setDate", newend);
$.timePicker("#cal_end_time").setTime(newend);
}
wdcal_edit_recur_recalc();
}
function wdcal_edit_checktime_endChanged() {
"use strict";
var time = wdcal_edit_getStartEnd();
if (time.start.getTime() >= time.end.getTime()) {
var newstart = new Date(time.end.getTime() - 3600000);
$("#cal_start_date").datepicker("setDate", newstart);
$.timePicker("#cal_start_time").setTime(newstart);
}
}
function wdcal_edit_recur_recalc() {
"use strict";
var start = $("#cal_start_date").datepicker("getDate");
$(".rec_month_name").text($.datepicker._defaults.monthNames[start.getMonth()]);
$("#rec_yearly_day option[value=bymonthday]").text($("#rec_yearly_day option[value=bymonthday]").data("orig").replace("#num#", start.getDate()));
$("#rec_monthly_day option[value=bymonthday]").text($("#rec_monthly_day option[value=bymonthday]").data("orig").replace("#num#", start.getDate()));
var month = new Date(start.getFullYear(), start.getMonth() + 1, 0);
var monthlast = month.getDate() - start.getDate() + 1;
$("#rec_yearly_day option[value=bymonthday_neg]").text($("#rec_yearly_day option[value=bymonthday_neg]").data("orig").replace("#num#", monthlast));
$("#rec_monthly_day option[value=bymonthday_neg]").text($("#rec_monthly_day option[value=bymonthday_neg]").data("orig").replace("#num#", monthlast));
var wk = Math.ceil(start.getDate() / 7);
var wkname = $.datepicker._defaults.dayNames[start.getDay()];
$("#rec_yearly_day option[value=byday]").text($("#rec_yearly_day option[value=byday]").data("orig").replace("#num#", wk).replace("#wkday#", wkname));
$("#rec_monthly_day option[value=byday]").text($("#rec_monthly_day option[value=byday]").data("orig").replace("#num#", wk).replace("#wkday#", wkname));
var wk_inv = Math.ceil(monthlast / 7);
$("#rec_yearly_day option[value=byday_neg]").text($("#rec_yearly_day option[value=byday_neg]").data("orig").replace("#num#", wk_inv).replace("#wkday#", wkname));
$("#rec_monthly_day option[value=byday_neg]").text($("#rec_monthly_day option[value=byday_neg]").data("orig").replace("#num#", wk_inv).replace("#wkday#", wkname));
}
function wdcal_edit_init(dateFormat, base_path) {
"use strict";
$("#cal_color").colorPicker();
$("#color_override").on("click", function() {
if ($("#color_override").prop("checked")) $("#cal_color_holder").show();
else $("#cal_color_holder").hide();
});
$("#cal_start_time").timePicker({ step: 15 }).on("change", wdcal_edit_checktime_startChanged);
$("#cal_end_time").timePicker().on("change", wdcal_edit_checktime_endChanged);
$("#cal_start_date").datepicker({
"dateFormat": dateFormat
}).on("change", wdcal_edit_checktime_startChanged);
$("#cal_end_date").datepicker({
"dateFormat": dateFormat
}).on("change", wdcal_edit_checktime_endChanged);
$("#rec_until_date").datepicker({ "dateFormat": dateFormat });
$("#notification").on("click change", function() {
if ($(this).prop("checked")) $("#notification_detail").show();
else ($("#notification_detail")).hide();
}).change();
$("#cal_allday").on("click change", function() {
if ($(this).prop("checked")) $("#cal_end_time, #cal_start_time").hide();
else $("#cal_end_time, #cal_start_time").show();
}).change();
$("#rec_frequency").on("click change", function() {
var val = $("#rec_frequency").val();
if (val == "") $("#rec_details").hide();
else $("#rec_details").show();
if (val == "daily") $(".rec_daily").show();
else $(".rec_daily").hide();
if (val == "weekly") $(".rec_weekly").show();
else $(".rec_weekly").hide();
if (val == "monthly") $(".rec_monthly").show();
else $(".rec_monthly").hide();
if (val == "yearly") $(".rec_yearly").show();
else $(".rec_yearly").hide();
}).change();
$("#rec_until_type").on("click change", function() {
var val = $("#rec_until_type").val();
if (val == "count") $("#rec_until_count").show();
else $("#rec_until_count").hide();
if (val == "date") $("#rec_until_date").show();
else $("#rec_until_date").hide();
}).change();
$("#rec_yearly_day option, #rec_monthly_day option").each(function() {
$(this).data("orig", $(this).text());
});
$("#new_alarm_adder a").click(function(ev) {
$("#new_alarm").val("1");
$("#noti_new_row").show();
$("#new_alarm_adder").hide();
ev.preventDefault();
});
wdcal_edit_recur_recalc();
$(document).on("click", ".exception_remover", function(ev) {
ev.preventDefault();
var $this = $(this),
$par = $this.parents(".rec_exceptions");
$this.parents(".except").remove();
if ($par.find(".rec_exceptions_holder").children().length == 0) {
$par.find(".rec_exceptions_holder").hide();
$par.find(".rec_exceptions_none").show();
}
});
$(".exception_adder").click(function(ev) {
ev.preventDefault();
var exceptions = [];
$(".rec_exceptions .except input").each(function() {
exceptions.push($(this).val());
});
var rec_weekly_byday = [];
$(".rec_weekly_byday:checked").each(function() {
rec_weekly_byday.push($(this).val());
});
var rec_daily_byday = [];
$(".rec_daily_byday:checked").each(function() {
rec_daily_byday.push($(this).val());
});
var opts = {
"start_date": $("input[name=start_date]").val(),
"start_time": $("input[name=start_time]").val(),
"end_date": $("input[name=end_date]").val(),
"end_time": $("input[name=end_time]").val(),
"rec_frequency": $("#rec_frequency").val(),
"rec_interval": $("#rec_interval").val(),
"rec_until_type": $("#rec_until_type").val(),
"rec_until_count": $("#rec_until_count").val(),
"rec_until_date": $("#rec_until_date").val(),
"rec_weekly_byday": rec_weekly_byday,
"rec_daily_byday": rec_daily_byday,
"rec_weekly_wkst": $("input[name=rec_weekly_wkst]:checked").val(),
"rec_monthly_day": $("#rec_monthly_day").val(),
"rec_yearly_day": $("#rec_yearly_day").val(),
"rec_exceptions": exceptions
};
if ($("#cal_allday").prop("checked")) opts["allday"] = 1;
var $dial = $("<div id='exception_setter_dialog'>Loading...</div>");
$dial.appendTo("body");
$dial.dialog({
"width": 400,
"height": 300,
"title": "Exceptions"
});
$dial.load(base_path + "getExceptionDates/", opts, function() {
$dial.find(".exception_selector_link").click(function(ev2) {
ev2.preventDefault();
var ts = $(this).data("timestamp");
var str = $(this).html();
var $part = $("<div data-timestamp='" + ts + "' class='except'><input type='hidden' class='rec_exception' name='rec_exceptions[]' value='" + ts + "'><a href='#' class='exception_remover'>[remove]</a> " + str + "</div>");
var found = false;
$(".rec_exceptions_holder .except").each(function() {
if (!found && ts < $(this).data("timestamp")) {
found = true;
$part.insertBefore(this);
}
});
if (!found) $(".rec_exceptions_holder").append($part);
$(".rec_exceptions .rec_exceptions_holder").show();
$(".rec_exceptions .rec_exceptions_none").hide();
$dial.dialog("destroy").remove();
})
});
});
}
function wdcal_edit_calendars_start(dateFormat, base_path) {
"use strict";
$(".cal_color").colorPicker();
$(".delete_cal").click(function(ev) {
if (!confirm("Do you really want to delete this calendar? All events will be moved to another private calendar.")) ev.preventDefault();
});
$(".calendar_add_caller").click(function(ev) {
$(".cal_add_row").show();
$(this).parents("div").hide();
ev.preventDefault();
});
}

File diff suppressed because it is too large Load diff

210
dav/common/wdcal/js/main.js Normal file
View file

@ -0,0 +1,210 @@
$(function () {
"use strict";
$.fn.animexxCalendar = function (option) {
//(wdcal_view, std_theme, data_feed_url, readonly, height_diff) {
var url_cal_add = "?";
$(this).find(".calselect input[type=checkbox]").each(function() {
if ($(this).prop("checked")) url_cal_add += "cal[]=" + $(this).val() + "&";
});
var def = {
calendars:[],
calendars_show:[],
view:"week",
theme:0,
onWeekOrMonthToDay:wtd,
onBeforeRequestData:cal_beforerequest,
onAfterRequestData:cal_afterrequest,
onRequestDataError:cal_onerror,
autoload:true,
data_feed_url:"",
url:option.data_feed_url + url_cal_add + "method=list",
quickAddUrl:option.data_feed_url + url_cal_add + "method=add",
quickUpdateUrl:option.data_feed_url + url_cal_add + "method=update",
quickDeleteUrl:option.data_feed_url + url_cal_add + "method=remove"
};
option = $.extend(def, option);
var $animexxcal = $(this),
$gridcontainer = $animexxcal.find(".gridcontainer"),
$dv = $animexxcal.find(".calhead"),
$caltoolbar = $animexxcal.find(".ctoolbar"),
$txtdatetimeshow = $animexxcal.find(".txtdatetimeshow"),
$loadingpanel = $animexxcal.find(".loadingpanel"),
$loaderrpanel = $animexxcal.find(".loaderror");
var _MH = document.documentElement.clientHeight;
var dvH = $dv.height() + 2;
option.height = _MH - dvH - option.height_diff;
if (option.height < 300) option.height = 300;
option.eventItems = [];
$animexxcal.find(".hdtxtshow").datepicker({
changeMonth: true,
changeYear: true,
onSelect: function(dateText, inst) {
var r = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
var p = $gridcontainer.gotoDate(r).BcalGetOp();
if (p && p.datestrshow) {
$animexxcal.find(".txtdatetimeshow").text(p.datestrshow);
}
}
});
$animexxcal.find(".txtdatetimeshow").css("cursor", "pointer").bind("click", function() {
$animexxcal.find(".hdtxtshow").datepicker("show");
});
var p = $gridcontainer.bcalendar(option).BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
$caltoolbar.noSelect();
function cal_beforerequest(type) {
var t = "Lade Daten...";
switch (type) {
case 1:
t = "Lade Daten...";
break;
case 2:
case 3:
case 4:
t = "Wird bearbeitete ...";
break;
}
$loaderrpanel.hide();
$loadingpanel.html(t).show();
}
function cal_afterrequest(type) {
var p = $gridcontainer.BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
switch (type) {
case 1:
$loadingpanel.hide();
break;
case 2:
case 3:
case 4:
$loadingpanel.html("Erfolg!");
$gridcontainer.reload();
window.setTimeout(function () {
$loadingpanel.hide();
}, 2000);
break;
}
}
function cal_onerror(type, data) {
$loaderrpanel.show();
}
function wtd(p) {
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
$caltoolbar.find("div.fcurrent").removeClass("fcurrent");
$animexxcal.find(".showdaybtn").addClass("fcurrent");
}
//to show day view
$animexxcal.find(".showdaybtn").on("click", function (e) {
//document.location.href="#day";
$caltoolbar.find("div.fcurrent").removeClass("fcurrent");
$(this).addClass("fcurrent");
var p = $gridcontainer.switchView("day").BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
//to show week view
$animexxcal.find(".showweekbtn").on("click", function (e) {
//document.location.href="#week";
$caltoolbar.find("div.fcurrent").removeClass("fcurrent");
$(this).addClass("fcurrent");
var p = $gridcontainer.switchView("week").BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
//to show month view
$animexxcal.find(".showmonthbtn").on("click", function (e) {
//document.location.href="#month";
$caltoolbar.find("div.fcurrent").removeClass("fcurrent");
$(this).addClass("fcurrent");
var p = $gridcontainer.switchView("month").BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
$animexxcal.find(".showreflashbtn").on("click", function (e) {
$gridcontainer.reload();
e.preventDefault();
});
//go to today
$animexxcal.find(".showtodaybtn").on("click", function (e) {
var p = $gridcontainer.gotoDate().BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
//previous date range
$animexxcal.find(".sfprevbtn").on("click", function (e) {
var p = $gridcontainer.previousRange().BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
//next date range
$animexxcal.find(".sfnextbtn").on("click", function (e) {
var p = $gridcontainer.nextRange().BcalGetOp();
if (p && p.datestrshow) {
$txtdatetimeshow.text(p.datestrshow);
}
e.preventDefault();
});
$animexxcal.find(".calselect input[type=checkbox]").on("click change", function() {
var url_cal_add = option.data_feed_url + "?";
$animexxcal.find(".calselect input[type=checkbox]").each(function() {
if ($(this).prop("checked")) url_cal_add += "cal[]=" + $(this).val() + "&";
});
/*
url:option.data_feed_url + url_cal_add + "method=list",
quickAddUrl:option.data_feed_url + url_cal_add + "method=add",
quickUpdateUrl:option.data_feed_url + url_cal_add + "method=update",
quickDeleteUrl:option.data_feed_url + url_cal_add + "method=remove"
*/
var url = url_cal_add + "method=list";
var p = $gridcontainer.BcalGetOp();
if (p.url != url) {
$gridcontainer.BcalSetOp({
"url": url_cal_add + "method=list",
"quickAddUrl": url_cal_add + "method=add",
"quickUpdateUrl": url_cal_add + "method=update",
"quickDeleteUrl": url_cal_add + "method=remove"
});
$gridcontainer.reload();
}
});
}
});

View file

@ -0,0 +1,38 @@
var i18n = $.extend({}, i18n || {}, {
xgcalendar: {
dateformat: {
"fulldaykey": "ddMMyyyy",
"fulldayshow": "d L yyyy",
"separator": ".",
"year_index": 2,
"month_index": 1,
"day_index": 0,
"day": "d"
},
"no_implemented": "Nicht eingebaut",
"to_date_view": "Zum aktuellen Datum gehen",
"i_undefined": "Undefined",
"allday_event": "Ganztages-Termin",
"repeat_event": "Wiederholter Termin",
"time": "Zeit",
"event": "Termin",
"location": "Ort",
"participant": "Teilnehmer",
"get_data_exception": "Exception when getting data",
"new_event": "Neuer Termin",
"confirm_delete_event": "Diesen Termin wirklich löschen? ",
"confrim_delete_event_or_all": "Nur diesen einen Termin löschen, oder alle Wiederholungen? \r\n[OK] für diesen einen, [Abbrechen] für alle.",
"data_format_error": "Data format error! ",
"invalid_title": "Der Titel des Termins darf nicht leer sein und kein ($<>) enthalten.",
"view_no_ready": "View is not ready",
"example": "e.g., Treffen in Raum 23",
"content": "Was",
"create_event": "Termin anlegen",
"update_detail": "Details bearbeiten",
"click_to_detail": "Details anzeigen",
"i_delete": "Löschen",
"day_plural": "Tage",
"others": "Weitere: ",
"item": ""
}
});

View file

@ -0,0 +1,38 @@
var i18n = $.extend({}, i18n || {}, {
xgcalendar: {
dateformat: {
"fulldaykey": "ddMMyyyy",
"fulldayshow": "d L yyyy",
"separator": ".",
"year_index": 2,
"month_index": 1,
"day_index": 0,
"day": "d"
},
"no_implemented": "Not implemented",
"to_date_view": "Go to today",
"i_undefined": "Undefined",
"allday_event": "All-day event",
"repeat_event": "Recurring event",
"time": "Time",
"event": "Event",
"location": "Loaction",
"participant": "Attendees",
"get_data_exception": "Exception when getting data",
"new_event": "New event",
"confirm_delete_event": "Do you really want to delete this event?",
"confrim_delete_event_or_all": "Do you want to delete this event alone, or all recurrences? \r\n[OK] for this single item, [Abort] for all.",
"data_format_error": "Data format error!",
"invalid_title": "The title of an event must not be empty and must not contain ($<>).",
"view_no_ready": "View is not ready",
"example": "e.g., Meeting in room 23",
"content": "Title",
"create_event": "Create event",
"update_detail": "Edit",
"click_to_detail": "Show details",
"i_delete": "Delete",
"day_plural": "days",
"others": "More: ",
"item": ""
}
});

View file

@ -0,0 +1,238 @@
<?php
use Friendica\Core\L10n;
use Friendica\Util\DateTimeFormat;
/**
* @param mixed $obj
* @return string
*/
function wdcal_jsonp_encode($obj)
{
$str = json_encode($obj);
if (isset($_REQUEST["callback"])) {
$str = $_REQUEST["callback"] . "(" . $str . ")";
}
return $str;
}
/**
* @param string $day
* @param int $weekstartday
* @param int $num_days
* @param string $type
* @return array
*/
function wdcal_get_list_range_params($day, $weekstartday, $num_days, $type)
{
$phpTime = IntVal($day);
switch ($type) {
case "month":
$st = mktime(0, 0, 0, date("m", $phpTime), 1, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime) + 1, 1, date("Y", $phpTime));
break;
case "week":
//suppose first day of a week is monday
$monday = date("d", $phpTime) - date('N', $phpTime) + 1;
//echo date('N', $phpTime);
$st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), $monday + 7, date("Y", $phpTime));
break;
case "multi_days":
//suppose first day of a week is monday
$monday = date("d", $phpTime) - date('N', $phpTime) + $weekstartday;
//echo date('N', $phpTime);
$st = mktime(0, 0, 0, date("m", $phpTime), $monday, date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), $monday + $num_days, date("Y", $phpTime));
break;
case "day":
$st = mktime(0, 0, 0, date("m", $phpTime), date("d", $phpTime), date("Y", $phpTime));
$et = mktime(0, 0, -1, date("m", $phpTime), date("d", $phpTime) + 1, date("Y", $phpTime));
break;
default:
return array(0, 0);
}
return array($st, $et);
}
/**
* @param Sabre_DAV_Server $server
* @param string $right
* @return null|Sabre_CalDAV_Calendar
*/
function wdcal_print_feed_getCal(&$server, $right)
{
$cals = dav_get_current_user_calendars($server, $right);
$calfound = null;
for ($i = 0; $i < count($cals) && $calfound === null; $i++) {
$prop = $cals[$i]->getProperties(array("id"));
if (isset($prop["id"]) && (!isset($_REQUEST["cal"]) || in_array($prop["id"], $_REQUEST["cal"]))) $calfound = $cals[$i];
}
return $calfound;
}
/**
*
*/
function wdcal_print_feed($base_path = "")
{
$server = dav_create_server(true, true, false);
$ret = null;
$method = $_GET["method"];
switch ($method) {
case "add":
$cs = wdcal_print_feed_getCal($server, DAV_ACL_WRITE);
if ($cs == null) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
try {
$item = dav_create_empty_vevent();
$component = dav_get_eventComponent($item);
$component->add("SUMMARY", icalendar_sanitize_string(dav_compat_parse_text_serverside("CalendarTitle")));
if (isset($_REQUEST["allday"])) $type = Sabre\VObject\Property\DateTime::DATE;
else $type = Sabre\VObject\Property\DateTime::LOCALTZ;
$datetime_start = new Sabre\VObject\Property\DateTime("DTSTART");
$datetime_start->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, IntVal($_REQUEST["CalendarStartTime"]))), $type);
$datetime_end = new Sabre\VObject\Property\DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, IntVal($_REQUEST["CalendarEndTime"]))), $type);
$component->add($datetime_start);
$component->add($datetime_end);
$uid = $component->__get("UID");
$data = $item->serialize();
$cs->createFile($uid . ".ics", $data);
$ret = array(
'IsSuccess' => true,
'Msg' => 'add success',
'Data' => $uid . ".ics",
);
} catch (Exception $e) {
$ret = array(
'IsSuccess' => false,
'Msg' => $e->__toString(),
);
}
break;
case "list":
$weekstartday = (isset($_REQUEST["weekstartday"]) ? IntVal($_REQUEST["weekstartday"]) : 1); // 1 = Monday
$num_days = (isset($_REQUEST["num_days"]) ? IntVal($_REQUEST["num_days"]) : 7);
$ret = null;
$date = wdcal_get_list_range_params($_REQUEST["showdate"], $weekstartday, $num_days, $_REQUEST["viewtype"]);
$ret = array();
$ret['events'] = array();
$ret["issort"] = true;
$ret["start"] = $date[0];
$ret["end"] = $date[1];
$ret['error'] = null;
$cals = dav_get_current_user_calendars($server, DAV_ACL_READ);
foreach ($cals as $cal) {
$prop = $cal->getProperties(array("id"));
if (isset($prop["id"]) && (!isset($_REQUEST["cal"]) || in_array($prop["id"], $_REQUEST["cal"]))) {
$backend = wdcal_calendar_factory_by_id($prop["id"]);
$events = $backend->listItemsByRange($prop["id"], $date[0], $date[1], $base_path);
$ret["events"] = array_merge($ret["events"], $events);
}
}
$tmpev = array();
foreach ($ret["events"] as $e) {
if (!isset($tmpev[$e["start"]])) $tmpev[$e["start"]] = array();
$tmpev[$e["start"]][] = $e;
}
ksort($tmpev);
$ret["events"] = array();
foreach ($tmpev as $e) foreach ($e as $f) $ret["events"][] = $f;
break;
case "update":
$r = q("SELECT `calendarobject_id`, `calendar_id` FROM %s%sjqcalendar WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($_REQUEST["jq_id"]));
if (count($r) != 1) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
try {
$cs = dav_get_current_user_calendar_by_id($server, $r[0]["calendar_id"], DAV_ACL_READ);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($r[0]["calendarobject_id"]);
$vObject = dav_get_current_user_calendarobject($server, $cs, $obj_uri["uri"], DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if (!$component) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
if (isset($_REQUEST["allday"])) $type = Sabre\VObject\Property\DateTime::DATE;
else $type = Sabre\VObject\Property\DateTime::LOCALTZ;
$datetime_start = new Sabre\VObject\Property\DateTime("DTSTART");
$datetime_start->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, IntVal($_REQUEST["CalendarStartTime"]))), $type);
$datetime_end = new Sabre\VObject\Property\DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, IntVal($_REQUEST["CalendarEndTime"]))), $type);
$component->__unset("DTSTART");
$component->__unset("DTEND");
$component->add($datetime_start);
$component->add($datetime_end);
$data = $vObject->serialize();
/** @var Sabre_CalDAV_CalendarObject $child */
$child = $cs->getChild($obj_uri["uri"]);
$child->put($data);
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} catch (Exception $e) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
break;
case "remove":
$r = q("SELECT `calendarobject_id`, `calendar_id` FROM %s%sjqcalendar WHERE `id`=%d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($_REQUEST["jq_id"]));
if (count($r) != 1) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
try {
$cs = dav_get_current_user_calendar_by_id($server, $r[0]["calendar_id"], DAV_ACL_WRITE);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($r[0]["calendarobject_id"]);
$child = $cs->getChild($obj_uri["uri"]);
$child->delete();
$ret = array(
'IsSuccess' => true,
'Msg' => 'Succefully',
);
} catch (Exception $e) {
echo wdcal_jsonp_encode(array('IsSuccess' => false,
'Msg' => L10n::t('No access')));
killme();
}
break;
}
echo wdcal_jsonp_encode($ret);
killme();
}

View file

@ -0,0 +1,326 @@
<?php
use Friendica\Core\L10n;
use Friendica\Core\PConfig;
abstract class wdcal_local
{
const LOCAL_US = 0;
const LOCAL_DE = 1;
/**
* @static
* @return array|wdcal_local[]
*/
static function getInstanceClasses() {
return array(
self::LOCAL_US => "wdcal_local_us",
self::LOCAL_DE => "wdcal_local_de",
);
}
/**
* @static
* @param int $config
* @return null|wdcal_local
*/
static function getInstance($config = 0) {
$classes = self::getInstanceClasses();
if (isset($classes[$config])) return new $classes[$config];
return null;
}
/**
* @static
* @param int $uid
* @return wdcal_local
*/
static function getInstanceByUser($uid = 0) {
$dateformat = PConfig::get($uid, "dav", "dateformat");
$format = self::getInstance($dateformat);
if ($format == null) $format = self::getInstance(self::LOCAL_US);
return $format;
}
/**
* @static
* @abstract
* @return string
*/
abstract static function getLanguageCode();
/**
* @abstract
* @static
* @return string
*/
abstract static function getName();
/**
* @static
* @abstract
* @return int
*/
abstract static function getID();
/**
* @param string $str
* @return int
*/
function date_local2timestamp($str) {
$x = $this->date_parseLocal($str);
return mktime($x["hour"], $x["minute"], $x["second"], $x["month"], $x["day"], $x["year"]);
}
/**
* @abstract
* @param string $str
* @return array
*/
abstract function date_parseLocal($str);
/**
* @abstract
* @param int $ts
* @return string
*/
abstract function date_timestamp2local($ts);
/**
* @abstract
* @param int $ts
* @return string
*/
abstract function date_timestamp2localDate($ts);
/**
* @abstract
* @return int
*/
abstract function getFirstDayOfWeek();
/**
* @abstract
* @return string
*/
abstract function dateformat_js_dm1();
/**
* @abstract
* @return string
*/
abstract function dateformat_js_dm2();
/**
* @abstract
* @return string
*/
abstract function dateformat_js_dm3();
/**
* @abstract
* @return string
*/
abstract function dateformat_datepicker_js();
/**
* @abstract
* @param int $ts
* @return string
*/
abstract function dateformat_datepicker_php($ts = 0);
}
class wdcal_local_us extends wdcal_local {
/**
* @static
* @return string
*/
static function getLanguageCode() {
return "en";
}
/**
* @return string
*/
static function getName() {
return L10n::t("U.S. Time Format (mm/dd/YYYY)");
}
/**
* @static
* @return int
*/
static function getID() {
return wdcal_local::LOCAL_US;
}
/**
* @param string $str
* @return array
*/
function date_parseLocal($str) {
return date_parse_from_format("m/d/Y H:i", $str);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2local($ts)
{
return date("m/d/Y H:i", $ts);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2localDate($ts) {
return date("l, F jS Y", $ts);
}
/**
* @return int
*/
function getFirstDayOfWeek() {
return 0;
}
/**
* @return string
*/
function dateformat_js_dm1() {
return "W, M/d";
}
/**
* @return string
*/
function dateformat_js_dm2() {
return "d. L";
}
/**
* @return string
*/
function dateformat_js_dm3() {
return "d L yyyy";
}
/**
* @return string
*/
function dateformat_datepicker_js() {
return "mm/dd/yy";
}
/**
* @param int $ts
* @return string
*/
function dateformat_datepicker_php($ts = 0) {
return date("m/d/Y", $ts);
}
}
class wdcal_local_de extends wdcal_local {
/**
* @static
* @return string
*/
static function getLanguageCode() {
return "de";
}
/**
* @return string
*/
static function getName() {
return L10n::t("German Time Format (dd.mm.YYYY)");
}
/**
* @static
* @return int
*/
static function getID() {
return wdcal_local::LOCAL_DE;
}
/**
* @param string $str
* @return array
*/
function date_parseLocal($str)
{
return date_parse_from_format("d.m.Y H:i", $str);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2local($ts)
{
return date("d.m.Y H:i", $ts);
}
/**
* @param int $ts
* @return string
*/
function date_timestamp2localDate($ts) {
return date("l, j. F Y", $ts);
}
/**
* @return int
*/
function getFirstDayOfWeek() {
return 1;
}
/**
* @return string
*/
function dateformat_js_dm1() {
return "W, d.M";
}
/**
* @return string
*/
function dateformat_js_dm2() {
return "d. L";
}
/**
* @return string
*/
function dateformat_js_dm3() {
return "d L yyyy";
}
/**
* @return string
*/
function dateformat_datepicker_js() {
return "dd.mm.yy";
}
/**
* @param int $ts
* @return string
*/
function dateformat_datepicker_php($ts = 0) {
return date("d.m.Y", $ts);
}
}

View file

@ -0,0 +1,811 @@
<?php
use Friendica\Core\L10n;
use Friendica\Util\DateTimeFormat;
/**
* @param wdcal_local $localization
* @param string $baseurl
* @param int $calendar_id
* @param int $uri
* @return string
*/
function wdcal_getEditPage_str(&$localization, $baseurl, $calendar_id, $uri)
{
$server = dav_create_server(true, true, false);
if ($uri > 0) {
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
if (!$calendar) {
$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_READ);
$calendars = array();
} else {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
}
if ($calendar == null) return "Calendar not found";
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($uri);
$vObject = dav_get_current_user_calendarobject($server, $calendar, $obj_uri["uri"], DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if ($component == null) return L10n::t('Could not open component for editing');
/** @var Sabre\VObject\Property\DateTime $dtstart */
$dtstart = $component->__get("DTSTART");
$event = array(
"id" => IntVal($uri),
"Summary" => ($component->__get("SUMMARY") ? $component->__get("SUMMARY")->value : null),
"StartTime" => $dtstart->getDateTime()->getTimeStamp(),
"EndTime" => Sabre_CalDAV_Backend_Common::getDtEndTimeStamp($component),
"IsAllDayEvent" => (strlen($dtstart->value) == 8),
"Description" => ($component->__get("DESCRIPTION") ? $component->__get("DESCRIPTION")->value : null),
"Location" => ($component->__get("LOCATION") ? $component->__get("LOCATION")->value : null),
"Color" => ($component->__get("X-ANIMEXX-COLOR") ? $component->__get("X-ANIMEXX-COLOR")->value : null),
);
$exdates = $component->select("EXDATE");
$recurrentce_exdates = array();
/** @var Sabre\VObject\Property\MultiDateTime $x */
foreach ($exdates as $x) {
/** @var DateTime $y */
$z = $x->getDateTimes();
foreach ($z as $y) $recurrentce_exdates[] = $y->getTimeStamp();
}
$notifications = array();
$alarms = $component->select("VALARM");
foreach ($alarms as $alarm) {
/** @var Sabre_VObject_Component_VAlarm $alarm */
$action = $alarm->__get("ACTION")->value;
$trigger = $alarm->__get("TRIGGER");
if(isset($trigger['VALUE']) && strtoupper($trigger['VALUE']) !== 'DURATION') {
notice("The notification of this event cannot be parsed");
continue;
}
/** @var DateInterval $triggerDuration */
$triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($trigger);
$unit = "hour";
$value = 1;
if ($triggerDuration->s > 0) {
$unit = "second";
$value = $triggerDuration->s + $triggerDuration->i * 60 + $triggerDuration->h * 3600 + $triggerDuration->d * 3600 * 24; // @TODO support more than days?
} elseif ($triggerDuration->i) {
$unit = "minute";
$value = $triggerDuration->i + $triggerDuration->h * 60 + $triggerDuration->d * 60 * 24;
} elseif ($triggerDuration->h) {
$unit = "hour";
$value = $triggerDuration->h + $triggerDuration->d * 24;
} elseif ($triggerDuration->d > 0) {
$unit = "day";
$value = $triggerDuration->d;
}
$rel = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'end' : 'start';
$notifications[] = array(
"action" => strtolower($action),
"rel" => $rel,
"trigger_unit" => $unit,
"trigger_value" => $value,
);
}
if ($component->select("RRULE")) $recurrence = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->__get("UID"));
else $recurrence = null;
} elseif (isset($_REQUEST["start"]) && $_REQUEST["start"] > 0) {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
//$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
$event = array(
"id" => 0,
"Summary" => $_REQUEST["title"],
"StartTime" => InTVal($_REQUEST["start"]),
"EndTime" => IntVal($_REQUEST["end"]),
"IsAllDayEvent" => $_REQUEST["isallday"],
"Description" => "",
"Location" => "",
"Color" => null,
);
if ($_REQUEST["isallday"]) {
$notifications = array();
} else {
$notifications = array(array("action" => "email", "rel" => "start", "trigger_unit" => "hour", "trigger_value" => 1));
}
$recurrence = null;
$recurrentce_exdates = array();
} else {
$calendars = dav_get_current_user_calendars($server, DAV_ACL_WRITE);
//$calendar = dav_get_current_user_calendar_by_id($server, $calendar_id, DAV_ACL_WRITE);
$event = array(
"id" => 0,
"Summary" => "",
"StartTime" => time(),
"EndTime" => time() + 3600,
"IsAllDayEvent" => "0",
"Description" => "",
"Location" => "",
"Color" => null,
);
$notifications = array(array("action" => "email", "rel" => "start", "trigger_unit" => "hour", "trigger_value" => 1));
$recurrence = null;
$recurrentce_exdates = array();
}
$postto = $baseurl . "/dav/wdcal/" . ($uri == 0 ? "new/" : $calendar_id . "/" . $uri . "/edit/");
$out = "<a href='" . $baseurl . "/dav/wdcal/'>" . L10n::t("Go back to the calendar") . "</a><br><br>";
$out .= "<form method='POST' action='$postto'>
<input type='hidden' name='form_security_token' value='" . get_form_security_token('caledit') . "'>\n";
$out .= "<h2>" . L10n::t("Event data") . "</h2>";
$out .= "<label for='calendar' class='block'>" . L10n::t("Calendar") . ":</label><select id='calendar' name='calendar' size='1'>";
$found = false;
$cal_col = "aaaaaa";
foreach ($calendars as $cal) {
$prop = $cal->getProperties(array("id", DAV_DISPLAYNAME, DAV_CALENDARCOLOR));
$out .= "<option value='" . $prop["id"] . "' ";
if ($prop["id"] == $calendar_id) {
$out .= "selected";
$cal_col = $prop[DAV_CALENDARCOLOR];
$found = true;
} elseif (!$found) $cal_col = $prop[DAV_CALENDARCOLOR];
$out .= ">" . escape_tags($prop[DAV_DISPLAYNAME]) . "</option>\n";
}
$out .= "</select>";
$out .= "&nbsp; &nbsp; <label class='plain'><input type='checkbox' name='color_override' id='color_override' ";
if (!is_null($event["Color"])) $out .= "checked";
$out .= "> " . L10n::t("Special color") . ":</label>";
$out .= "<span id='cal_color_holder' ";
if (is_null($event["Color"])) $out .= "style='display: none;'";
$out .= "><input name='color' id='cal_color' value='" . (is_null($event["Color"]) ? "#" . $cal_col : escape_tags($event["Color"])) . "'></span>";
$out .= "<br>\n";
$out .= "<label class='block' for='cal_summary'>" . L10n::t("Subject") . ":</label>
<input name='summary' id='cal_summary' value=\"" . escape_tags($event["Summary"]) . "\"><br>\n";
$out .= "<label class='block' for='cal_allday'>Is All-Day event:</label><input type='checkbox' name='allday' id='cal_allday' " . ($event["IsAllDayEvent"] ? "checked" : "") . "><br>\n";
$out .= "<label class='block' for='cal_start_date'>" . L10n::t("Starts") . ":</label>";
$out .= "<input name='start_date' value='" . $localization->dateformat_datepicker_php($event["StartTime"]) . "' id='cal_start_date'>";
$out .= "<input name='start_time' value='" . date("H:i", $event["StartTime"]) . "' id='cal_start_time'>";
$out .= "<br>\n";
$out .= "<label class='block' for='cal_end_date'>" . L10n::t("Ends") . ":</label>";
$out .= "<input name='end_date' value='" . $localization->dateformat_datepicker_php($event["EndTime"]) . "' id='cal_end_date'>";
$out .= "<input name='end_time' value='" . date("H:i", $event["EndTime"]) . "' id='cal_end_time'>";
$out .= "<br>\n";
$out .= "<label class='block' for='cal_location'>" . L10n::t("Location") . ":</label><input name='location' id='cal_location' value=\"" . escape_tags($event["Location"]) . "\"><br>\n";
$out .= "<label class='block' for='event-desc-textarea'>" . L10n::t("Description") . ":</label> <textarea id='event-desc-textarea' name='wdcal_desc' style='vertical-align: top; width: 400px; height: 100px;'>" . escape_tags($event["Description"]) . "</textarea>";
$out .= "<br style='clear: both;'>";
$out .= "<h2>" . L10n::t("Recurrence") . "</h2>";
$out .= "<label class='block' for='rec_frequency'>" . L10n::t("Frequency") . ":</label> <select id='rec_frequency' name='rec_frequency' size='1'>";
$out .= "<option value=''>" . L10n::t("None") . "</option>\n";
$out .= "<option value='daily' ";
if ($recurrence && $recurrence->frequency == "daily") $out .= "selected";
$out .= ">" . L10n::t("Daily") . "</option>\n";
$out .= "<option value='weekly' ";
if ($recurrence && $recurrence->frequency == "weekly") $out .= "selected";
$out .= ">" . L10n::t("Weekly") . "</option>\n";
$out .= "<option value='monthly' ";
if ($recurrence && $recurrence->frequency == "monthly") $out .= "selected";
$out .= ">" . L10n::t("Monthly") . "</option>\n";
$out .= "<option value='yearly' ";
if ($recurrence && $recurrence->frequency == "yearly") $out .= "selected";
$out .= ">" . L10n::t("Yearly") . "</option>\n";
$out .= "</select><br>\n";
$out .= "<div id='rec_details'>";
$select = "<select id='rec_interval' name='rec_interval' size='1'>";
for ($i = 1; $i < 50; $i++) {
$select .= "<option value='$i' ";
if ($recurrence && $i == $recurrence->interval) $select .= "selected";
$select .= ">$i</option>\n";
}
$select .= "</select>";
$time = "<span class='rec_daily'>" . L10n::t("days") . "</span>";
$time .= "<span class='rec_weekly'>" . L10n::t("weeks") . "</span>";
$time .= "<span class='rec_monthly'>" . L10n::t("months") . "</span>";
$time .= "<span class='rec_yearly'>" . L10n::t("years") . "</span>";
$out .= "<label class='block'>" . L10n::t("Interval") . ":</label> " . str_replace(array("%select%", "%time%"), array($select, $time), L10n::t("All %select% %time%")) . "<br>";
$out .= "<div class='rec_daily'>";
$out .= "<label class='block'>" . L10n::t("Days") . ":</label>";
if ($recurrence && $recurrence->byDay) {
$byday = $recurrence->byDay;
} else {
$byday = array("MO", "TU", "WE", "TH", "FR", "SA", "SU");
}
if ($localization->getFirstDayOfWeek() == 0) {
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' ";
if (in_array("SU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Sunday") . "</label> &nbsp; ";
}
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='MO' ";
if (in_array("MO", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Monday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TU' ";
if (in_array("TU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Tuesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='WE' ";
if (in_array("WE", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Wednesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='TH' ";
if (in_array("TH", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Thursday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='FR' ";
if (in_array("FR", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Friday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SA' ";
if (in_array("SA", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Saturday") . "</label> &nbsp; ";
if ($localization->getFirstDayOfWeek() != 0) {
$out .= "<label class='plain'><input class='rec_daily_byday' type='checkbox' name='rec_daily_byday[]' value='SU' ";
if (in_array("SU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Sunday") . "</label> &nbsp; ";
}
$out .= "</div>";
$out .= "<div class='rec_weekly'>";
$out .= "<label class='block'>" . L10n::t("Days") . ":</label>";
if ($recurrence && $recurrence->byDay) {
$byday = $recurrence->byDay;
} else {
$days = array("MO", "TU", "WE", "TH", "FR", "SA", "SU");
$byday = array($days[date("N", $event["StartTime"]) - 1]);
}
if ($localization->getFirstDayOfWeek() == 0) {
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' ";
if (in_array("SU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Sunday") . "</label> &nbsp; ";
}
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='MO' ";
if (in_array("MO", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Monday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TU' ";
if (in_array("TU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Tuesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='WE' ";
if (in_array("WE", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Wednesday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='TH' ";
if (in_array("TH", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Thursday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='FR' ";
if (in_array("FR", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Friday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SA' ";
if (in_array("SA", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Saturday") . "</label> &nbsp; ";
if ($localization->getFirstDayOfWeek() != 0) {
$out .= "<label class='plain'><input class='rec_weekly_byday' type='checkbox' name='rec_weekly_byday[]' value='SU' ";
if (in_array("SU", $byday)) $out .= "checked";
$out .= ">" . L10n::t("Sunday") . "</label> &nbsp; ";
}
$out .= "<br>";
$out .= "<label class='block'>" . L10n::t("First day of week:") . "</label>";
if ($recurrence && $recurrence->weekStart != "") $wkst = $recurrence->weekStart;
else {
if ($localization->getFirstDayOfWeek() == 0) $wkst = "SU";
else $wkst = "MO";
}
$out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='SU' ";
if ($wkst == "SU") $out .= "checked";
$out .= ">" . L10n::t("Sunday") . "</label> &nbsp; ";
$out .= "<label class='plain'><input type='radio' name='rec_weekly_wkst' value='MO' ";
if ($wkst == "MO") $out .= "checked";
$out .= ">" . L10n::t("Monday") . "</label><br>\n";
$out .= "</div>";
$monthly_rule = "";
if ($recurrence && ($recurrence->frequency == "monthly" || $recurrence->frequency == "yearly")) {
if (is_null($recurrence->byDay) && !is_null($recurrence->byMonthDay) && count($recurrence->byMonthDay) == 1) {
$day = date("j", $event["StartTime"]);
if ($recurrence->byMonthDay[0] == $day) $monthly_rule = "bymonthday";
else {
$lastday = date("t", $event["StartTime"]);
if ($recurrence->byMonthDay[0] == -1 * ($lastday - $day + 1)) $monthly_rule = "bymonthday_neg";
}
}
if (is_null($recurrence->byMonthDay) && !is_null($recurrence->byDay) && count($recurrence->byDay) == 1) {
$num = IntVal($recurrence->byDay[0]);
/*
$dayMap = array(
'SU' => 0,
'MO' => 1,
'TU' => 2,
'WE' => 3,
'TH' => 4,
'FR' => 5,
'SA' => 6,
);
if ($num == 0) {
$num = 1;
$weekday = $dayMap[$recurrence->byDay[0]];
} else {
$weekday = $dayMap[substr($recurrence->byDay[0], strlen($num))];
}
echo $num . " - " . $weekday;
*/
if ($num > 0) $monthly_rule = "byday";
if ($num < 0) $monthly_rule = "byday_neg";
}
if ($monthly_rule == "") notice("The recurrence of this event cannot be parsed");
}
$out .= "<div class='rec_monthly'>";
$out .= "<label class='block' for='rec_monthly_day'>" . L10n::t("Day of month") . ":</label>";
$out .= "<select id='rec_monthly_day' name='rec_monthly_day' size='1'>";
$out .= "<option value='bymonthday' ";
if ($monthly_rule == "bymonthday") $out .= "selected";
$out .= ">" . L10n::t("#num#th of each month") . "</option>\n";
$out .= "<option value='bymonthday_neg' ";
if ($monthly_rule == "bymonthday_neg") $out .= "selected";
$out .= ">" . L10n::t("#num#th-last of each month") . "</option>\n";
$out .= "<option value='byday' ";
if ($monthly_rule == "byday") $out .= "selected";
$out .= ">" . L10n::t("#num#th #wkday# of each month") . "</option>\n";
$out .= "<option value='byday_neg' ";
if ($monthly_rule == "byday_neg") $out .= "selected";
$out .= ">" . L10n::t("#num#th-last #wkday# of each month") . "</option>\n";
$out .= "</select>";
$out .= "</div>\n";
if ($recurrence && $recurrence->frequency == "yearly") {
if (count($recurrence->byMonth) != 1 || $recurrence->byMonth[0] != date("n", $event["StartTime"])) notice("The recurrence of this event cannot be parsed!");
}
$out .= "<div class='rec_yearly'>";
$out .= "<label class='block'>" . L10n::t("Month") . ":</label> <span class='rec_month_name'>#month#</span><br>\n";
$out .= "<label class='block' for='rec_yearly_day'>" . L10n::t("Day of month") . ":</label>";
$out .= "<select id='rec_yearly_day' name='rec_yearly_day' size='1'>";
$out .= "<option value='bymonthday' ";
if ($monthly_rule == "bymonthday") $out .= "selected";
$out .= ">" . L10n::t("#num#th of the given month") . "</option>\n";
$out .= "<option value='bymonthday_neg' ";
if ($monthly_rule == "bymonthday_neg") $out .= "selected";
$out .= ">" . L10n::t("#num#th-last of the given month") . "</option>\n";
$out .= "<option value='byday' ";
if ($monthly_rule == "byday") $out .= "selected";
$out .= ">" . L10n::t("#num#th #wkday# of the given month") . "</option>\n";
$out .= "<option value='byday_neg' ";
if ($monthly_rule == "byday_neg") $out .= "selected";
$out .= ">" . L10n::t("#num#th-last #wkday# of the given month") . "</option>\n";
$out .= "</select>";
$out .= "</div>\n";
if ($recurrence) {
$until = $recurrence->until;
$count = $recurrence->count;
if (is_a($until, "DateTime")) {
/** @var DateTime $until */
$rule_type = "date";
$rule_until_date = $until->getTimestamp();
$rule_until_count = 1;
} elseif ($count > 0) {
$rule_type = "count";
$rule_until_date = time();
$rule_until_count = $count;
} else {
$rule_type = "infinite";
$rule_until_date = time();
$rule_until_count = 1;
}
} else {
$rule_type = "infinite";
$rule_until_date = time();
$rule_until_count = 1;
}
$out .= "<label class='block' for='rec_until_type'>" . L10n::t("Repeat until") . ":</label> ";
$out .= "<select name='rec_until_type' id='rec_until_type' size='1'>";
$out .= "<option value='infinite' ";
if ($rule_type == "infinite") $out .= "selected";
$out .= ">" . L10n::t("Infinite") . "</option>\n";
$out .= "<option value='date' ";
if ($rule_type == "date") $out .= "selected";
$out .= ">" . L10n::t("Until the following date") . ":</option>\n";
$out .= "<option value='count' ";
if ($rule_type == "count") $out .= "selected";
$out .= ">" . L10n::t("Number of times") . ":</option>\n";
$out .= "</select>";
$out .= "<input name='rec_until_date' value='" . $localization->dateformat_datepicker_php($rule_until_date) . "' id='rec_until_date'>";
$out .= "<input name='rec_until_count' value='$rule_until_count' id='rec_until_count'><br>";
$out .= "<label class='block'>" . L10n::t("Exceptions") . ":</label><div class='rec_exceptions'>";
$out .= "<div class='rec_exceptions_none' ";
if (count($recurrentce_exdates) > 0) $out .= "style='display: none;'";
$out .= ">" . L10n::t("none") . "</div>";
$out .= "<div class='rec_exceptions_holder' ";
if (count($recurrentce_exdates) == 0) $out .= "style='display: none;'";
$out .= ">";
foreach ($recurrentce_exdates as $exdate) {
$out .= "<div data-timestamp='$exdate' class='except'><input type='hidden' class='rec_exception' name='rec_exceptions[]' value='$exdate'>";
$out .= "<a href='#' class='exception_remover'>[remove]</a> ";
$out .= $localization->date_timestamp2localDate($exdate);
$out .= "</div>\n";
}
$out .= "</div><div><a href='#' class='exception_adder'>[add]</a></div>";
$out .= "</div>\n";
$out .= "<br>\n";
$out .= "</div><br>";
$out .= "<h2>" . L10n::t("Notification") . "</h2>";
if (!$notifications) $notifications = array();
$notifications["new"] = array(
"action" => "email",
"trigger_value" => 60,
"trigger_unit" => "minute",
"rel" => "start",
);
foreach ($notifications as $index => $noti) {
$unparsable = false;
if (!in_array($noti["action"], array("email", "display"))) $unparsable = true;
$out .= "<div class='noti_holder' ";
if (!is_numeric($index) && $index == "new") $out .= "style='display: none;' id='noti_new_row'";
$out .= "><label class='block' for='noti_type_" . $index . "'>" . L10n::t("Notify by") . ":</label>";
$out .= "<select name='noti_type[$index]' size='1' id='noti_type_" . $index . "'>";
$out .= "<option value=''>- " . L10n::t("Remove") . " -</option>\n";
$out .= "<option value='email' "; if (!$unparsable && $noti["action"] == "email") $out .= "selected"; $out .= ">" . L10n::t("E-Mail") . "</option>\n";
$out .= "<option value='display' "; if (!$unparsable && $noti["action"] == "display") $out .= "selected"; $out .= ">" . L10n::t("On Friendica / Display") . "</option>\n";
//$out .= "<option value='other' "; if ($unparsable) $out .= "selected"; $out .= ">- " . L10n::t("other (leave it untouched)") . " -</option>\n"; // @TODO
$out .= "</select><br>";
$out .= "<label class='block'>" . L10n::t("Time") . ":</label>";
$out .= "<input name='noti_value[$index]' size='5' style='width: 5em;' value='" . $noti["trigger_value"] . "'>";
$out .= "<select name='noti_unit[$index]' size='1'>";
$out .= "<option value='H' "; if ($noti["trigger_unit"] == "hour") $out .= "selected"; $out .= ">" . L10n::t("Hours") . "</option>\n";
$out .= "<option value='M' "; if ($noti["trigger_unit"] == "minute") $out .= "selected"; $out .= ">" . L10n::t("Minutes") . "</option>\n";
$out .= "<option value='S' "; if ($noti["trigger_unit"] == "second") $out .= "selected"; $out .= ">" . L10n::t("Seconds") . "</option>\n";
$out .= "<option value='D' "; if ($noti["trigger_unit"] == "day") $out .= "selected"; $out .= ">" . L10n::t("Days") . "</option>\n";
$out .= "<option value='W' "; if ($noti["trigger_unit"] == "week") $out .= "selected"; $out .= ">" . L10n::t("Weeks") . "</option>\n";
$out .= "</select>";
$out .= " <label class='plain'>" . L10n::t("before the") . " <select name='noti_ref[$index]' size='1'>";
$out .= "<option value='start' "; if ($noti["rel"] == "start") $out .= "selected"; $out .= ">" . L10n::t("start of the event") . "</option>\n";
$out .= "<option value='end' "; if ($noti["rel"] == "end") $out .= "selected"; $out .= ">" . L10n::t("end of the event") . "</option>\n";
$out .= "</select></label>\n";
$out .= "</div>";
}
$out .= "<input type='hidden' name='new_alarm' id='new_alarm' value='0'><div id='new_alarm_adder'><a href='#'>" . L10n::t("Add a notification") . "</a></div>";
$out .= "<script>\$(function() {
wdcal_edit_init('" . $localization->dateformat_datepicker_js() . "', '${baseurl}/dav/');
});</script>";
$out .= "<br><input type='submit' name='save' value='Save'></form>";
return $out;
}
/**
* @param Sabre_VObject_Component_VEvent $component
* @param wdcal_local $localization
* @return int
*/
function wdcal_set_component_date(&$component, &$localization)
{
if (isset($_REQUEST["allday"])) {
$ts_start = $localization->date_local2timestamp($_REQUEST["start_date"] . " 00:00");
$ts_end = $localization->date_local2timestamp($_REQUEST["end_date"] . " 00:00");
$type = Sabre\VObject\Property\DateTime::DATE;
} else {
$ts_start = $localization->date_local2timestamp($_REQUEST["start_date"] . " " . $_REQUEST["start_time"]);
$ts_end = $localization->date_local2timestamp($_REQUEST["end_date"] . " " . $_REQUEST["end_time"]);
$type = Sabre\VObject\Property\DateTime::LOCALTZ;
}
$datetime_start = new Sabre\VObject\Property\DateTime("DTSTART");
$datetime_start->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, $ts_start)), $type);
$datetime_end = new Sabre\VObject\Property\DateTime("DTEND");
$datetime_end->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, $ts_end)), $type);
$component->__unset("DTSTART");
$component->__unset("DTEND");
$component->add($datetime_start);
$component->add($datetime_end);
return $ts_start;
}
/**
* @param Sabre_VObject_Component_VEvent $component
* @param string $str
* @return string
*/
function wdcal_set_component_recurrence_special(&$component, $str) {
$ret = "";
/** @var Sabre\VObject\Property\DateTime $start */
$start = $component->__get("DTSTART");
$dayMap = array(
0 => 'SU',
1 => 'MO',
2 => 'TU',
3 => 'WE',
4 => 'TH',
5 => 'FR',
6 => 'SA',
);
switch ($str) {
case "bymonthday":
$day = $start->getDateTime()->format("j");
$ret = ";BYMONTHDAY=" . $day;
break;
case "bymonthday_neg":
$day = $start->getDateTime()->format("j");
$day_max = $start->getDateTime()->format("t");
$ret = ";BYMONTHDAY=" . (-1 * ($day_max - $day + 1));
break;
case "byday":
$day = $start->getDateTime()->format("j");
$weekday = $dayMap[$start->getDateTime()->format("w")];
$num = IntVal(ceil($day / 7));
$ret = ";BYDAY=${num}${weekday}";
break;
case "byday_neg":
$day = $start->getDateTime()->format("j");
$weekday = $dayMap[$start->getDateTime()->format("w")];
$day_max = $start->getDateTime()->format("t");
$day_last = ($day_max - $day + 1);
$num = IntVal(ceil($day_last / 7));
$ret = ";BYDAY=-${num}${weekday}";
break;
}
return $ret;
}
/**
* @param Sabre_VObject_Component_VEvent $component
* @param wdcal_local $localization
*/
function wdcal_set_component_recurrence(&$component, &$localization)
{
$component->__unset("RRULE");
$component->__unset("EXRULE");
$component->__unset("EXDATE");
$component->__unset("RDATE");
$part_until = "";
switch ($_REQUEST["rec_until_type"]) {
case "date":
$date = $localization->date_local2timestamp($_REQUEST["rec_until_date"]);
$part_until = ";UNTIL=" . date("Ymd", $date);
$datetime_until = new Sabre\VObject\Property\DateTime("UNTIL");
$datetime_until->setDateTime(new DateTime(date(DateTimeFormat::MYSQL, $date)), Sabre\VObject\Property\DateTime::DATE);
break;
case "count":
$part_until = ";COUNT=" . IntVal($_REQUEST["rec_until_count"]);
break;
}
switch ($_REQUEST["rec_frequency"]) {
case "daily":
$part_freq = "FREQ=DAILY";
if (isset($_REQUEST["rec_daily_byday"])) {
$days = array();
foreach ($_REQUEST["rec_daily_byday"] as $x) if (in_array($x, array("MO", "TU", "WE", "TH", "FR", "SA", "SU"))) $days[] = $x;
if (count($days) > 0) $part_freq .= ";BYDAY=" . implode(",", $days);
}
break;
case "weekly":
$part_freq = "FREQ=WEEKLY";
if (isset($_REQUEST["rec_weekly_wkst"]) && in_array($_REQUEST["rec_weekly_wkst"], array("MO", "SU"))) $part_freq .= ";WKST=" . $_REQUEST["rec_weekly_wkst"];
if (isset($_REQUEST["rec_weekly_byday"])) {
$days = array();
foreach ($_REQUEST["rec_weekly_byday"] as $x) if (in_array($x, array("MO", "TU", "WE", "TH", "FR", "SA", "SU"))) $days[] = $x;
if (count($days) > 0) $part_freq .= ";BYDAY=" . implode(",", $days);
}
break;
case "monthly":
$part_freq = "FREQ=MONTHLY";
$part_freq .= wdcal_set_component_recurrence_special($component, $_REQUEST["rec_monthly_day"]);
break;
case "yearly":
/** @var Sabre\VObject\Property\DateTime $start */
$start = $component->__get("DTSTART");
$part_freq = "FREQ=YEARLY";
$part_freq .= ";BYMONTH=" . $start->getDateTime()->format("n");
$part_freq .= wdcal_set_component_recurrence_special($component, $_REQUEST["rec_yearly_day"]);
break;
default:
$part_freq = "";
}
if ($part_freq == "") return;
if (isset($_REQUEST["rec_interval"])) $part_freq .= ";INTERVAL=" . InTVal($_REQUEST["rec_interval"]);
if (isset($_REQUEST["rec_exceptions"])) {
$arr = array();
foreach ($_REQUEST["rec_exceptions"] as $except) {
$arr[] = new DateTime(date(DateTimeFormat::MYSQL, $except));
}
/** @var Sabre\VObject\Property\MultiDateTime $prop */
$prop = Sabre\VObject\Property::create("EXDATE");
$prop->setDateTimes($arr);
$component->add($prop);
}
$rrule = $part_freq . $part_until;
$component->add(new Sabre\VObject\Property("RRULE", $rrule));
}
/**
* @param Sabre\VObject\Component\VEvent $component
* @param wdcal_local $localization
* @param string $summary
* @param int $dtstart
*/
function wdcal_set_component_alerts(&$component, &$localization, $summary, $dtstart)
{
$a = get_app();
$prev_alarms = $component->select("VALARM");
$component->__unset("VALARM");
foreach ($prev_alarms as $al) {
/** @var Sabre\VObject\Component\VAlarm $al */
// @TODO Parse notifications that have been there before; e.g. from Lightning
}
foreach (array_keys($_REQUEST["noti_type"]) as $key) if (is_numeric($key) || ($key == "new" && $_REQUEST["new_alarm"] == 1)) {
$alarm = new Sabre\VObject\Component\VAlarm("VALARM");
switch ($_REQUEST["noti_type"][$key]) {
case "email":
$mailtext = str_replace(array(
"#date#", "#name",
), array(
$localization->date_timestamp2local($dtstart), $summary,
), L10n::t("The event #name# will start at #date"));
$alarm->add(new Sabre\VObject\Property("ACTION", "EMAIL"));
$alarm->add(new Sabre\VObject\Property("SUMMARY", $summary));
$alarm->add(new Sabre\VObject\Property("DESCRIPTION", $mailtext));
$alarm->add(new Sabre\VObject\Property("ATTENDEE", "MAILTO:" . $a->user["email"]));
break;
case "display":
$alarm->add(new Sabre\VObject\Property("ACTION", "DISPLAY"));
$text = str_replace("#name#", $summary, L10n::t("#name# is about to begin."));
$alarm->add(new Sabre\VObject\Property("DESCRIPTION", $text));
break;
default:
continue;
}
$trigger_name = "TRIGGER";
$trigger_val = ""; // @TODO Bugfix : und ; sind evtl. vertauscht vgl. http://www.kanzaki.com/docs/ical/trigger.html
if ($_REQUEST["noti_ref"][$key] == "end") $trigger_name .= ";RELATED=END";
$trigger_val .= "-P";
if (in_array($_REQUEST["noti_unit"][$key], array("H", "M", "S"))) $trigger_val .= "T";
$trigger_val .= IntVal($_REQUEST["noti_value"][$key]) . $_REQUEST["noti_unit"][$key];
$alarm->add(new Sabre\VObject\Property($trigger_name, $trigger_val));
$component->add($alarm);
}
}
/**
* @param string $uri
* @param int $uid
* @param string $timezone
* @param string $goaway_url
* @return array
*/
function wdcal_postEditPage($uri, $uid = 0, $timezone = "", $goaway_url = "")
{
$uid = IntVal($uid);
$localization = wdcal_local::getInstanceByUser($uid);
$server = dav_create_server(true, true, false);
if ($uri > 0) {
$calendar = dav_get_current_user_calendar_by_id($server, $_REQUEST["calendar"], DAV_ACL_READ);
$obj_uri = Sabre_CalDAV_Backend_Common::loadCalendarobjectById($uri);
$obj_uri = $obj_uri["uri"];
$vObject = dav_get_current_user_calendarobject($server, $calendar, $obj_uri, DAV_ACL_WRITE);
$component = dav_get_eventComponent($vObject);
if ($component == null) return array("ok" => false, "msg" => L10n::t('Could not open component for editing'));
} else {
$calendar = dav_get_current_user_calendar_by_id($server, $_REQUEST["calendar"], DAV_ACL_WRITE);
$vObject = dav_create_empty_vevent();
$component = dav_get_eventComponent($vObject);
$obj_uri = $component->__get("UID");
}
$ts_start = wdcal_set_component_date($component, $localization);
wdcal_set_component_recurrence($component, $localization);
wdcal_set_component_alerts($component, $localization, icalendar_sanitize_string(dav_compat_parse_text_serverside("summary")), $ts_start);
$component->__unset("LOCATION");
$component->__unset("SUMMARY");
$component->__unset("DESCRIPTION");
$component->__unset("X-ANIMEXX-COLOR");
$component->add("SUMMARY", icalendar_sanitize_string(dav_compat_parse_text_serverside("summary")));
$component->add("LOCATION", icalendar_sanitize_string(dav_compat_parse_text_serverside("location")));
$component->add("DESCRIPTION", icalendar_sanitize_string(dav_compat_parse_text_serverside("wdcal_desc")));
if (isset($_REQUEST["color_override"])) {
$component->add("X-ANIMEXX-COLOR", $_REQUEST["color"]);
}
$data = $vObject->serialize();
if ($uri == 0) {
$calendar->createFile($obj_uri . ".ics", $data);
} else {
$obj = $calendar->getChild($obj_uri);
$obj->put($data);
}
return array("ok" => false, "msg" => L10n::t("Saved"));
}
/**
* @return string
*/
function wdcal_getEditPage_exception_selector()
{
header("Content-type: application/json");
$a = get_app();
$localization = wdcal_local::getInstanceByUser($a->user["uid"]);
$vObject = dav_create_empty_vevent();
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') break;
}
/** @var Sabre\VObject\Component\VEvent $component */
wdcal_set_component_date($component, $localization);
wdcal_set_component_recurrence($component, $localization);
$it = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
$max_ts = mktime(0, 0, 0, 1, 1, CALDAV_MAX_YEAR + 1);
$last_start = 0;
$o = "<ul>";
$i = 0;
while ($it->valid() && $last_start < $max_ts && $i++ < 1000) {
$last_start = $it->getDtStart()->getTimestamp();
$o .= "<li><a href='#' class='exception_selector_link' data-timestamp='$last_start'>" . $localization->date_timestamp2localDate($last_start) . "</a></li>\n";
$it->next();
}
$o .= "</ul>\n";
return $o;
}