Browse Source

fix(caldav): Correctly handle calendar recreation for invitations when the current calendar is in the trashbin

Follow-up to #32361, see https://github.com/nextcloud/calendar/issues/4098 for details

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
Thomas Citharel 1 year ago
parent
commit
4fd58aa45c

+ 29 - 8
apps/dav/lib/CalDAV/Schedule/Plugin.php

@@ -48,7 +48,6 @@ use Sabre\VObject\Component;
 use Sabre\VObject\Component\VCalendar;
 use Sabre\VObject\Component\VEvent;
 use Sabre\VObject\DateTimeParser;
-use Sabre\VObject\Document;
 use Sabre\VObject\FreeBusyGenerator;
 use Sabre\VObject\ITip;
 use Sabre\VObject\Parameter;
@@ -329,12 +328,12 @@ EOF;
 
 				/** @var CalendarHome $calendarHome */
 				$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
-				if (!$calendarHome->childExists($uri)) {
+				$currentCalendarDeleted = false;
+				if (!$calendarHome->childExists($uri) || $currentCalendarDeleted = $this->isCalendarDeleted($calendarHome, $uri)) {
 					// If the default calendar doesn't exist
 					if ($isResourceOrRoom) {
-						$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
-							'{DAV:}displayname' => $displayName,
-						]);
+						// Resources or rooms can't be in the trashbin, so we're fine
+						$this->createCalendar($calendarHome, $principalUrl, $uri, $displayName);
 					} else {
 						// And we're not handling scheduling on resource/room booking
 						$userCalendars = [];
@@ -359,9 +358,16 @@ EOF;
 							$uri = $userCalendars[0]->getName();
 						} else {
 							// Otherwise if we have really nothing, create a new calendar
-							$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
-								'{DAV:}displayname' => $displayName,
-							]);
+							if ($currentCalendarDeleted) {
+								// If the calendar exists but is deleted, we need to purge it first
+								// This may cause some issues in a non synchronous database setup
+								$calendar = $this->getCalendar($calendarHome, $uri);
+								if ($calendar instanceof Calendar) {
+									$calendar->disableTrashbin();
+									$calendar->delete();
+								}
+							}
+							$this->createCalendar($calendarHome, $principalUrl, $uri, $displayName);
 						}
 					}
 				}
@@ -609,4 +615,19 @@ EOF;
 
 		return $email;
 	}
+
+	private function getCalendar(CalendarHome $calendarHome, string $uri): INode {
+		return $calendarHome->getChild($uri);
+	}
+
+	private function isCalendarDeleted(CalendarHome $calendarHome, string $uri): bool {
+		$calendar = $this->getCalendar($calendarHome, $uri);
+		return $calendar instanceof Calendar && $calendar->isDeleted();
+	}
+
+	private function createCalendar(CalendarHome $calendarHome, string $principalUri, string $uri, string $displayName): void {
+		$calendarHome->getCalDAVBackend()->createCalendar($principalUri, $uri, [
+			'{DAV:}displayname' => $displayName,
+		]);
+	}
 }

+ 19 - 10
apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php

@@ -197,6 +197,16 @@ class PluginTest extends TestCase {
 				false,
 				CalDavBackend::PERSONAL_CALENDAR_URI,
 				CalDavBackend::PERSONAL_CALENDAR_NAME,
+				true,
+				true
+			],
+			[
+				'principals/users/myuser',
+				'calendars/myuser',
+				false,
+				CalDavBackend::PERSONAL_CALENDAR_URI,
+				CalDavBackend::PERSONAL_CALENDAR_NAME,
+				false,
 				false,
 				true
 			],
@@ -225,6 +235,7 @@ class PluginTest extends TestCase {
 				true,
 				false,
 				false,
+				false,
 			],
 			[
 				'principals/users/myuser',
@@ -263,16 +274,8 @@ class PluginTest extends TestCase {
 
 	/**
 	 * @dataProvider propFindDefaultCalendarUrlProvider
-	 * @param string $principalUri
-	 * @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 $hasExistingCalendars = false, bool $propertiesForPath = true): void {
-		/** @var PropFind $propFind */
+	public function testPropFindDefaultCalendarUrl(string $principalUri, ?string $calendarHome, bool $isResource, string $calendarUri, string $displayName, bool $exists, bool $deleted = false, bool $hasExistingCalendars = false, bool $propertiesForPath = true): void {
 		$propFind = new PropFind(
 			$principalUri,
 			[
@@ -328,6 +331,12 @@ class PluginTest extends TestCase {
 			->with($calendarUri)
 			->willReturn($exists);
 
+		if ($exists) {
+			$calendar = $this->createMock(Calendar::class);
+			$calendar->expects($this->once())->method('isDeleted')->willReturn($deleted);
+			$calendarHomeObject->expects($deleted && !$hasExistingCalendars ? $this->exactly(2) : $this->once())->method('getChild')->with($calendarUri)->willReturn($calendar);
+		}
+
 		$calendarBackend = $this->createMock(CalDavBackend::class);
 		$calendarUri = $hasExistingCalendars ? 'custom' : $calendarUri;
 		$displayName = $hasExistingCalendars ? 'Custom Calendar' : $displayName;
@@ -349,7 +358,7 @@ class PluginTest extends TestCase {
 			)
 		] : [];
 
-		if (!$exists) {
+		if (!$exists || $deleted) {
 			if (!$hasExistingCalendars) {
 				$calendarBackend->expects($this->once())
 				->method('createCalendar')