<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Afterlogic\DAV\CalDAV;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2019, Afterlogic Corp.
*/
class Plugin extends \Sabre\CalDAV\Plugin
{
/**
* Returns the path to a principal's calendar home.
*
* The return url must not end with a slash.
* This function should return null in case a principal did not have
* a calendar home.
*
* @param string $principalUrl
* @return string
*/
public function getCalendarHomeForPrincipal($principalUrl)
{
return self::CALENDAR_ROOT;
}
protected function fixOrganizer(&$data, &$modified)
{
// If it's a stream, we convert it to a string first.
if (is_resource($data)) {
$data = stream_get_contents($data);
}
$vobj = \Sabre\VObject\Reader::read($data, \Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES);
if (isset($vobj->VEVENT->ORGANIZER)) {
$sOrganizer = $vobj->VEVENT->ORGANIZER->getNormalizedValue();
$iPos = strpos($sOrganizer, 'principals/');
if ($iPos !== false) {
$sOrganizer = 'mailto:' . \trim(substr($sOrganizer, $iPos + 11), '/');
$vobj->VEVENT->ORGANIZER->setValue($sOrganizer);
$data = $vobj->serialize();
$vobj->destroy();
$modified = true;
}
}
}
/**
* This method is triggered before a file gets updated with new content.
*
* This plugin uses this method to ensure that CalDAV objects receive
* valid calendar data.
*
* @param string $path
* @param DAV\IFile $node
* @param resource $data
* @param bool $modified Should be set to true, if this event handler
* changed &$data.
* @return void
*/
public function beforeWriteContent($path, \Sabre\DAV\IFile $node, &$data, &$modified)
{
if (!$node instanceof \Sabre\CalDAV\ICalendarObject) {
return;
}
// We're onyl interested in ICalendarObject nodes that are inside of a
// real calendar. This is to avoid triggering validation and scheduling
// for non-calendars (such as an inbox).
list($parent) = \Sabre\Uri\split($path);
$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof \Sabre\CalDAV\ICalendar) {
return;
}
$this->fixOrganizer($data, $modified);
try {
$this->validateICalendar(
$data,
$path,
$modified,
$this->server->httpRequest,
$this->server->httpResponse,
false
);
} catch (\Exception $oEx) {
}
}
/**
* This method is triggered before a new file is created.
*
* This plugin uses this method to ensure that newly created calendar
* objects contain valid calendar data.
*
* @param string $path
* @param resource $data
* @param DAV\ICollection $parentNode
* @param bool $modified Should be set to true, if this event handler
* changed &$data.
* @return void
*/
public function beforeCreateFile($path, &$data, \Sabre\DAV\ICollection $parentNode, &$modified)
{
if (!$parentNode instanceof \Sabre\CalDAV\ICalendar) {
return;
}
$this->fixOrganizer($data, $modified);
try {
$this->validateICalendar(
$data,
$path,
$modified,
$this->server->httpRequest,
$this->server->httpResponse,
true
);
} catch (\Exception $oEx) {
}
}
/**
* Use this method to tell the server this plugin defines additional
* HTTP methods.
*
* This method is passed a uri. It should only return HTTP methods that are
* available for the specified uri.
*
* @param string $uri
*
* @return array
*/
public function getHTTPMethods($uri)
{
$uri = '/'.\ltrim($uri, '/');
// The MKCALENDAR is only available on unmapped uri's, whose
// parents extend IExtendedCollection
list($parent, $name) = \Sabre\Uri\split($uri);
if (isset($parent)) {
$node = $this->server->tree->getNodeForPath($parent);
if ($node instanceof \Sabre\DAV\IExtendedCollection) {
try {
$node->getChild($name);
} catch (\Sabre\DAV\Exception\NotFound $e) {
return ['MKCALENDAR'];
}
}
}
return [];
}
}