Merge pull request #19852 from nextcloud/dav/provide-dav-setting-for-default-calendar

Provide dav setting for user's default calendar
This commit is contained in:
John Molakvoæ 2020-04-13 17:00:04 +02:00 committed by GitHub
commit 3ea9dd8487
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 268 additions and 15 deletions

View File

@ -92,7 +92,7 @@ if ($debugging) {
$server->addPlugin(new \Sabre\DAV\Sync\Plugin()); $server->addPlugin(new \Sabre\DAV\Sync\Plugin());
$server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); $server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
$server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); $server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig()));
if ($sendInvitations) { if ($sendInvitations) {
$server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class)); $server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));

View File

@ -49,6 +49,7 @@ use OCA\DAV\HookManager;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\Calendar\IManager as ICalendarManager; use OCP\Calendar\IManager as ICalendarManager;
use OCP\Contacts\IManager as IContactsManager; use OCP\Contacts\IManager as IContactsManager;
use OCP\IConfig;
use OCP\IUser; use OCP\IUser;
use Symfony\Component\EventDispatcher\GenericEvent; use Symfony\Component\EventDispatcher\GenericEvent;
@ -244,6 +245,22 @@ class Application extends App {
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $listener);
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener);
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', $listener);
/**
* In case the user has set their default calendar to this one
*/
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', function (GenericEvent $event) {
/** @var IConfig $config */
$config = $this->getContainer()->getServer()->getConfig();
$principalUri = $event->getArgument('calendarData')['principaluri'];
if (strpos($principalUri, 'principals/users') === 0) {
list(, $UID) = \Sabre\Uri\split($principalUri);
$uri = $event->getArgument('calendarData')['uri'];
if ($config->getUserValue($UID, 'dav', 'defaultCalendar') === $uri) {
$config->deleteUserValue($UID, 'dav', 'defaultCalendar');
}
}
});
} }
public function getSyncService() { public function getSyncService() {

View File

@ -84,7 +84,7 @@ class InvitationResponseServer {
// calendar plugins // calendar plugins
$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin()); $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
$this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig()));
$this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin()); $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
//$this->server->addPlugin(new \OCA\DAV\DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest())); //$this->server->addPlugin(new \OCA\DAV\DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));

View File

@ -29,6 +29,7 @@ namespace OCA\DAV\CalDAV\Schedule;
use DateTimeZone; use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarHome; use OCA\DAV\CalDAV\CalendarHome;
use OCP\IConfig;
use Sabre\CalDAV\ICalendar; use Sabre\CalDAV\ICalendar;
use Sabre\DAV\INode; use Sabre\DAV\INode;
use Sabre\DAV\IProperties; use Sabre\DAV\IProperties;
@ -47,15 +48,31 @@ use Sabre\VObject\ITip;
use Sabre\VObject\Parameter; use Sabre\VObject\Parameter;
use Sabre\VObject\Property; use Sabre\VObject\Property;
use Sabre\VObject\Reader; use Sabre\VObject\Reader;
use function \Sabre\Uri\split;
class Plugin extends \Sabre\CalDAV\Schedule\Plugin { class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
/**
* @var IConfig
*/
private $config;
/** @var ITip\Message[] */ /** @var ITip\Message[] */
private $schedulingResponses = []; private $schedulingResponses = [];
/** @var string|null */ /** @var string|null */
private $pathOfCalendarObjectChange = null; private $pathOfCalendarObjectChange = null;
public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type';
public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL';
/**
* @param IConfig $config
*/
public function __construct(IConfig $config) {
$this->config = $config;
}
/** /**
* Initializes the plugin * Initializes the plugin
* *
@ -81,13 +98,12 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
public function propFind(PropFind $propFind, INode $node) { public function propFind(PropFind $propFind, INode $node) {
if ($node instanceof IPrincipal) { if ($node instanceof IPrincipal) {
// overwrite Sabre/Dav's implementation // overwrite Sabre/Dav's implementation
$propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function () use ($node) { $propFind->handle(self::CALENDAR_USER_TYPE, function () use ($node) {
if ($node instanceof IProperties) { if ($node instanceof IProperties) {
$calendarUserType = '{' . self::NS_CALDAV . '}calendar-user-type'; $props = $node->getProperties([self::CALENDAR_USER_TYPE]);
$props = $node->getProperties([$calendarUserType]);
if (isset($props[$calendarUserType])) { if (isset($props[self::CALENDAR_USER_TYPE])) {
return $props[$calendarUserType]; return $props[self::CALENDAR_USER_TYPE];
} }
} }
@ -261,7 +277,7 @@ EOF;
*/ */
public function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) { public function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) {
if ($node instanceof IPrincipal) { if ($node instanceof IPrincipal) {
$propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function () use ($node) { $propFind->handle(self::SCHEDULE_DEFAULT_CALENDAR_URL, function () use ($node) {
/** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */ /** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
$caldavPlugin = $this->server->getPlugin('caldav'); $caldavPlugin = $this->server->getPlugin('caldav');
$principalUrl = $node->getPrincipalUrl(); $principalUrl = $node->getPrincipalUrl();
@ -272,12 +288,13 @@ EOF;
} }
if (strpos($principalUrl, 'principals/users') === 0) { if (strpos($principalUrl, 'principals/users') === 0) {
$uri = CalDavBackend::PERSONAL_CALENDAR_URI; list(, $userId) = split($principalUrl);
$displayname = CalDavBackend::PERSONAL_CALENDAR_NAME; $uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
$displayName = CalDavBackend::PERSONAL_CALENDAR_NAME;
} elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 || } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
strpos($principalUrl, 'principals/calendar-rooms') === 0) { strpos($principalUrl, 'principals/calendar-rooms') === 0) {
$uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI; $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
$displayname = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME; $displayName = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
} else { } else {
// How did we end up here? // How did we end up here?
// TODO - throw exception or just ignore? // TODO - throw exception or just ignore?
@ -288,7 +305,7 @@ EOF;
$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath); $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
if (!$calendarHome->childExists($uri)) { if (!$calendarHome->childExists($uri)) {
$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
'{DAV:}displayname' => $displayname, '{DAV:}displayname' => $displayName,
]); ]);
} }

View File

@ -151,7 +151,7 @@ class Server {
if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) { if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) {
$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
$this->server->addPlugin(new \OCA\DAV\CalDAV\ICSExportPlugin\ICSExportPlugin(\OC::$server->getConfig(), \OC::$server->getLogger())); $this->server->addPlugin(new \OCA\DAV\CalDAV\ICSExportPlugin\ICSExportPlugin(\OC::$server->getConfig(), \OC::$server->getLogger()));
$this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin()); $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig()));
if (\OC::$server->getConfig()->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') { if (\OC::$server->getConfig()->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') {
$this->server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class)); $this->server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
} }

View File

@ -25,28 +25,68 @@
namespace OCA\DAV\Tests\unit\CalDAV\Schedule; namespace OCA\DAV\Tests\unit\CalDAV\Schedule;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\Plugin as CalDAVPlugin;
use OCA\DAV\CalDAV\Schedule\Plugin; use OCA\DAV\CalDAV\Schedule\Plugin;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server; use Sabre\DAV\Server;
use Sabre\DAV\Tree;
use Sabre\DAV\Xml\Property\Href; use Sabre\DAV\Xml\Property\Href;
use Sabre\DAV\Xml\Property\LocalHref;
use Sabre\DAVACL\IPrincipal;
use Sabre\HTTP\ResponseInterface;
use Sabre\VObject\Parameter; use Sabre\VObject\Parameter;
use Sabre\VObject\Property\ICalendar\CalAddress; use Sabre\VObject\Property\ICalendar\CalAddress;
use Sabre\Xml\Service;
use Test\TestCase; use Test\TestCase;
class PluginTest extends TestCase { class PluginTest extends TestCase {
/** @var Plugin */ /** @var Plugin */
private $plugin; private $plugin;
/** @var Server|\PHPUnit_Framework_MockObject_MockObject */ /** @var Server|MockObject */
private $server; private $server;
/** @var IConfig|MockObject */
private $config;
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->server = $this->createMock(Server::class); $this->server = $this->createMock(Server::class);
$this->config = $this->createMock(IConfig::class);
$this->plugin = new Plugin(); $response = $this->getMockBuilder(ResponseInterface::class)
->disableOriginalConstructor()
->getMock();
$this->server->httpResponse = $response;
$this->server->xml = new Service();
$this->plugin = new Plugin($this->config);
$this->plugin->initialize($this->server); $this->plugin->initialize($this->server);
} }
public function testInitialize() {
$plugin = new Plugin($this->config);
$this->server->expects($this->at(7))
->method('on')
->with('propFind', [$plugin, 'propFindDefaultCalendarUrl'], 90);
$this->server->expects($this->at(8))
->method('on')
->with('afterWriteContent', [$plugin, 'dispatchSchedulingResponses']);
$this->server->expects($this->at(9))
->method('on')
->with('afterCreateFile', [$plugin, 'dispatchSchedulingResponses']);
$plugin->initialize($this->server);
}
public function testGetAddressesForPrincipal() { public function testGetAddressesForPrincipal() {
$href = $this->createMock(Href::class); $href = $this->createMock(Href::class);
$href $href
@ -125,4 +165,183 @@ class PluginTest extends TestCase {
$this->assertFalse($this->invokePrivate($this->plugin, 'getAttendeeRSVP', [$property2])); $this->assertFalse($this->invokePrivate($this->plugin, 'getAttendeeRSVP', [$property2]));
$this->assertFalse($this->invokePrivate($this->plugin, 'getAttendeeRSVP', [$property3])); $this->assertFalse($this->invokePrivate($this->plugin, 'getAttendeeRSVP', [$property3]));
} }
public function propFindDefaultCalendarUrlProvider(): array {
return [
[
'principals/users/myuser',
'calendars/myuser',
false,
CalDavBackend::PERSONAL_CALENDAR_URI,
CalDavBackend::PERSONAL_CALENDAR_NAME,
true
],
[
'principals/users/myuser',
'calendars/myuser',
false,
CalDavBackend::PERSONAL_CALENDAR_URI,
CalDavBackend::PERSONAL_CALENDAR_NAME,
false
],
[
'principals/users/myuser',
null,
false,
CalDavBackend::PERSONAL_CALENDAR_URI,
CalDavBackend::PERSONAL_CALENDAR_NAME,
true
],
[
'principals/users/myuser',
'calendars/myuser',
false,
CalDavBackend::PERSONAL_CALENDAR_URI,
CalDavBackend::PERSONAL_CALENDAR_NAME,
true,
false,
],
[
'principals/users/myuser',
'calendars/myuser',
false,
'my_other_calendar',
'My Other Calendar',
true
],
[
'principals/calendar-resources',
'system-calendars/calendar-resources/myuser',
true,
CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI,
CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME,
true
],
[
'principals/calendar-resources',
'system-calendars/calendar-resources/myuser',
true,
CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI,
CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME,
false
],
[
'principals/something-else',
'calendars/whatever',
false,
CalDavBackend::PERSONAL_CALENDAR_URI,
CalDavBackend::PERSONAL_CALENDAR_NAME,
true
],
];
}
/**
* @dataProvider propFindDefaultCalendarUrlProvider
* @param string $principalUri
* @param string $calendarHome
* @param bool $isResource
* @param string $calendarUri
* @param string $displayName
* @param bool $exists
* @param bool $propertiesForPath
*/
public function testPropFindDefaultCalendarUrl(string $principalUri, ?string $calendarHome, bool $isResource, string $calendarUri, string $displayName, bool $exists, bool $propertiesForPath = true) {
/** @var PropFind $propFind */
$propFind = new PropFind(
$principalUri,
[
Plugin::SCHEDULE_DEFAULT_CALENDAR_URL
],
0
);
/** @var IPrincipal|MockObject $node */
$node = $this->getMockBuilder(IPrincipal::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->once())
->method('getPrincipalUrl')
->with()
->willReturn($principalUri);
$calDAVPlugin = $this->getMockBuilder(CalDAVPlugin::class)
->disableOriginalConstructor()
->getMock();
$calDAVPlugin->expects($this->once())
->method('getCalendarHomeForPrincipal')
->willReturn($calendarHome);
$this->server->expects($this->once())
->method('getPlugin')
->with('caldav')
->willReturn($calDAVPlugin);
if (!$calendarHome) {
$this->plugin->propFindDefaultCalendarUrl($propFind, $node);
$this->assertNull($propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL));
return;
}
if ($principalUri === 'principals/something-else') {
$this->plugin->propFindDefaultCalendarUrl($propFind, $node);
$this->assertNull($propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL));
return;
}
if (!$isResource) {
$this->config->expects($this->once())
->method('getUserValue')
->with('myuser', 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI)
->willReturn($calendarUri);
}
$calendarHomeObject = $this->createMock(CalendarHome::class);
$calendarHomeObject->expects($this->once())
->method('childExists')
->with($calendarUri)
->willReturn($exists);
if (!$exists) {
$calendarBackend = $this->createMock(CalDavBackend::class);
$calendarBackend->expects($this->once())
->method('createCalendar')
->with($principalUri, $calendarUri, [
'{DAV:}displayname' => $displayName,
]);
$calendarHomeObject->expects($this->once())
->method('getCalDAVBackend')
->with()
->willReturn($calendarBackend);
}
/** @var Tree|MockObject $tree */
$tree = $this->createMock(Tree::class);
$tree->expects($this->once())
->method('getNodeForPath')
->with($calendarHome)
->willReturn($calendarHomeObject);
$this->server->tree = $tree;
$properties = $propertiesForPath ? [
['href' => '/remote.php/dav/' . $calendarHome . '/' . $calendarUri]
] : [];
$this->server->expects($this->once())
->method('getPropertiesForPath')
->with($calendarHome .'/' . $calendarUri, [], 1)
->willReturn($properties);
$this->plugin->propFindDefaultCalendarUrl($propFind, $node);
if (!$propertiesForPath) {
$this->assertNull($propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL));
return;
}
/** @var LocalHref $result */
$result = $propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL);
$this->assertEquals('/remote.php/dav/'. $calendarHome . '/' . $calendarUri, $result->getHref());
}
} }