From 8d7154318fb063d0e79bb344749e4be9ce26ef0b Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 12 May 2022 09:58:33 +0200 Subject: Put calendar invites into the user's first available calendar If there's no default calendar and we can't find anything with URI 'personal', instead of creating a new one, start by using the first "real personal calendar" available. If not, then we create the default one. Signed-off-by: Thomas Citharel --- apps/dav/lib/CalDAV/Calendar.php | 9 +++- apps/dav/lib/CalDAV/PublicCalendar.php | 2 +- apps/dav/lib/CalDAV/Schedule/Plugin.php | 44 ++++++++++++++-- apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php | 60 +++++++++++++++++++--- 4 files changed, 100 insertions(+), 15 deletions(-) (limited to 'apps') diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index 75c815c3b0a..79d2244b42c 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -400,7 +400,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable return isset($this->calendarInfo['{http://owncloud.org/ns}public']); } - protected function isShared() { + public function isShared() { if (!isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return false; } @@ -412,6 +412,13 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable return isset($this->calendarInfo['{http://calendarserver.org/ns/}source']); } + public function isDeleted(): bool { + if (!isset($this->calendarInfo[TrashbinPlugin::PROPERTY_DELETED_AT])) { + return false; + } + return $this->calendarInfo[TrashbinPlugin::PROPERTY_DELETED_AT] !== null; + } + /** * @inheritDoc */ diff --git a/apps/dav/lib/CalDAV/PublicCalendar.php b/apps/dav/lib/CalDAV/PublicCalendar.php index 4a29c8d237a..16c7f86917d 100644 --- a/apps/dav/lib/CalDAV/PublicCalendar.php +++ b/apps/dav/lib/CalDAV/PublicCalendar.php @@ -84,7 +84,7 @@ class PublicCalendar extends Calendar { * public calendars are always shared * @return bool */ - protected function isShared() { + public function isShared() { return true; } } diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php index 96bacce4454..74865297944 100644 --- a/apps/dav/lib/CalDAV/Schedule/Plugin.php +++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php @@ -30,6 +30,7 @@ namespace OCA\DAV\CalDAV\Schedule; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; use OCA\DAV\CalDAV\CalendarHome; use OCP\IConfig; use Sabre\CalDAV\ICalendar; @@ -299,12 +300,14 @@ EOF; return null; } + $isResourceOrRoom = strpos($principalUrl, 'principals/calendar-resources') === 0 || + strpos($principalUrl, 'principals/calendar-rooms') === 0; + if (strpos($principalUrl, 'principals/users') === 0) { [, $userId] = split($principalUrl); $uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI); $displayName = CalDavBackend::PERSONAL_CALENDAR_NAME; - } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 || - strpos($principalUrl, 'principals/calendar-rooms') === 0) { + } elseif ($isResourceOrRoom) { $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI; $displayName = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME; } else { @@ -316,9 +319,40 @@ EOF; /** @var CalendarHome $calendarHome */ $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath); if (!$calendarHome->childExists($uri)) { - $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ - '{DAV:}displayname' => $displayName, - ]); + // If the default calendar doesn't exist + if ($isResourceOrRoom) { + $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ + '{DAV:}displayname' => $displayName, + ]); + } else { + // And we're not handling scheduling on resource/room booking + $userCalendars = []; + /** + * If the default calendar of the user isn't set and the + * fallback doesn't match any of the user's calendar + * try to find the first "personal" calendar we can write to + * instead of creating a new one. + * A appropriate personal calendar to receive invites: + * - isn't a calendar subscription + * - user can write to it (no virtual/3rd-party calendars) + * - calendar isn't a share + */ + foreach ($calendarHome->getChildren() as $node) { + if ($node instanceof Calendar && !$node->isSubscription() && $node->canWrite() && !$node->isShared() && !$node->isDeleted()) { + $userCalendars[] = $node; + } + } + + if (count($userCalendars) > 0) { + // Calendar backend returns calendar by calendarorder property + $uri = $userCalendars[0]->getName(); + } else { + // Otherwise if we have really nothing, create a new calendar + $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ + '{DAV:}displayname' => $displayName, + ]); + } + } } $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1); diff --git a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php index 2518cc3d91a..93a98aa059f 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php @@ -27,11 +27,15 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; use OCA\DAV\CalDAV\CalendarHome; use OCA\DAV\CalDAV\Plugin as CalDAVPlugin; use OCA\DAV\CalDAV\Schedule\Plugin; +use OCA\DAV\CalDAV\Trashbin\Plugin as TrashbinPlugin; use OCP\IConfig; +use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Sabre\DAV\PropFind; use Sabre\DAV\Server; use Sabre\DAV\Tree; @@ -177,6 +181,15 @@ class PluginTest extends TestCase { CalDavBackend::PERSONAL_CALENDAR_NAME, true ], + [ + 'principals/users/myuser', + 'calendars/myuser', + false, + CalDavBackend::PERSONAL_CALENDAR_URI, + CalDavBackend::PERSONAL_CALENDAR_NAME, + false, + true + ], [ 'principals/users/myuser', 'calendars/myuser', @@ -201,6 +214,7 @@ class PluginTest extends TestCase { CalDavBackend::PERSONAL_CALENDAR_NAME, true, false, + false, ], [ 'principals/users/myuser', @@ -240,14 +254,14 @@ class PluginTest extends TestCase { /** * @dataProvider propFindDefaultCalendarUrlProvider * @param string $principalUri - * @param string $calendarHome + * @param string|null $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) { + public function testPropFindDefaultCalendarUrl(string $principalUri, ?string $calendarHome, bool $isResource, string $calendarUri, string $displayName, bool $exists, bool $hasExistingCalendars = false, bool $propertiesForPath = true) { /** @var PropFind $propFind */ $propFind = new PropFind( $principalUri, @@ -290,6 +304,7 @@ class PluginTest extends TestCase { $this->assertNull($propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL)); return; } + if (!$isResource) { $this->config->expects($this->once()) ->method('getUserValue') @@ -303,18 +318,47 @@ class PluginTest extends TestCase { ->with($calendarUri) ->willReturn($exists); + $calendarBackend = $this->createMock(CalDavBackend::class); + $calendarUri = $hasExistingCalendars ? 'custom' : $calendarUri; + $displayName = $hasExistingCalendars ? 'Custom Calendar' : $displayName; + + $existingCalendars = $hasExistingCalendars ? [ + new Calendar( + $calendarBackend, + ['uri' => 'deleted', '{DAV:}displayname' => 'A deleted calendar', TrashbinPlugin::PROPERTY_DELETED_AT => 42], + $this->createMock(IL10N::class), + $this->config, + $this->createMock(LoggerInterface::class) + ), + new Calendar( + $calendarBackend, + ['uri' => $calendarUri, '{DAV:}displayname' => $displayName], + $this->createMock(IL10N::class), + $this->config, + $this->createMock(LoggerInterface::class) + ) + ] : []; + if (!$exists) { - $calendarBackend = $this->createMock(CalDavBackend::class); - $calendarBackend->expects($this->once()) + if (!$hasExistingCalendars) { + $calendarBackend->expects($this->once()) ->method('createCalendar') ->with($principalUri, $calendarUri, [ '{DAV:}displayname' => $displayName, ]); - $calendarHomeObject->expects($this->once()) - ->method('getCalDAVBackend') - ->with() - ->willReturn($calendarBackend); + $calendarHomeObject->expects($this->once()) + ->method('getCalDAVBackend') + ->with() + ->willReturn($calendarBackend); + } + + if (!$isResource) { + $calendarHomeObject->expects($this->once()) + ->method('getChildren') + ->with() + ->willReturn($existingCalendars); + } } /** @var Tree|MockObject $tree */ -- cgit v1.2.3 From a7b2e8a593ef218ca4218d0633932b15a5bfca76 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 12 May 2022 15:12:25 +0200 Subject: Refactor CalDAV\Schedule\PluginTest for depreciated phpunit methods Signed-off-by: Thomas Citharel --- apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'apps') diff --git a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php index 93a98aa059f..b651379c2bd 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php @@ -77,17 +77,22 @@ class PluginTest extends TestCase { public function testInitialize() { $plugin = new Plugin($this->config); - $this->server->expects($this->at(7)) + $this->server->expects($this->exactly(10)) ->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']); + ->withConsecutive( + // Sabre\CalDAV\Schedule\Plugin events + ['method:POST', [$plugin, 'httpPost']], + ['propFind', [$plugin, 'propFind']], + ['propPatch', [$plugin, 'propPatch']], + ['calendarObjectChange', [$plugin, 'calendarObjectChange']], + ['beforeUnbind', [$plugin, 'beforeUnbind']], + ['schedule', [$plugin, 'scheduleLocalDelivery']], + ['getSupportedPrivilegeSet', [$plugin, 'getSupportedPrivilegeSet']], + // OCA\DAV\CalDAV\Schedule\Plugin events + ['propFind', [$plugin, 'propFindDefaultCalendarUrl'], 90], + ['afterWriteContent', [$plugin, 'dispatchSchedulingResponses']], + ['afterCreateFile', [$plugin, 'dispatchSchedulingResponses']] + ); $plugin->initialize($this->server); } -- cgit v1.2.3