aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCôme Chilliet <91878298+come-nc@users.noreply.github.com>2025-02-13 10:24:05 +0100
committerGitHub <noreply@github.com>2025-02-13 10:24:05 +0100
commitee48cafd200233203a1444dba797ef3eb89a35ca (patch)
tree1f37b64545f4ab80ffb14c442753539777e9cd98
parent6e1d9a26209ec5524fbc2fb9c7cbb53315e64d72 (diff)
parent355fef6ff92b3ce1206bbbed17947e588ff89436 (diff)
downloadnextcloud-server-chore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca.tar.gz
nextcloud-server-chore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca.zip
Merge pull request #50689 from nextcloud/fix/migrate-dav-to-eventschore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca
fix(dav): Migrate from hooks to user events
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php2
-rw-r--r--apps/dav/composer/composer/autoload_static.php2
-rw-r--r--apps/dav/lib/AppInfo/Application.php37
-rw-r--r--apps/dav/lib/HookManager.php155
-rw-r--r--apps/dav/lib/Listener/UserEventsListener.php151
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php11
-rw-r--r--apps/dav/tests/unit/DAV/HookManagerTest.php222
-rw-r--r--apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php140
-rw-r--r--build/psalm-baseline.xml6
-rw-r--r--tests/lib/User/UserTest.php6
10 files changed, 326 insertions, 406 deletions
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index a70aba9f842..09eb4ba17bd 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -284,7 +284,6 @@ return array(
'OCA\\DAV\\Files\\RootCollection' => $baseDir . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => $baseDir . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
- 'OCA\\DAV\\HookManager' => $baseDir . '/../lib/HookManager.php',
'OCA\\DAV\\Listener\\ActivityUpdaterListener' => $baseDir . '/../lib/Listener/ActivityUpdaterListener.php',
'OCA\\DAV\\Listener\\AddMissingIndicesListener' => $baseDir . '/../lib/Listener/AddMissingIndicesListener.php',
'OCA\\DAV\\Listener\\AddressbookListener' => $baseDir . '/../lib/Listener/AddressbookListener.php',
@@ -299,6 +298,7 @@ return array(
'OCA\\DAV\\Listener\\OutOfOfficeListener' => $baseDir . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => $baseDir . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => $baseDir . '/../lib/Listener/TrustedServerRemovedListener.php',
+ 'OCA\\DAV\\Listener\\UserEventsListener' => $baseDir . '/../lib/Listener/UserEventsListener.php',
'OCA\\DAV\\Listener\\UserPreferenceListener' => $baseDir . '/../lib/Listener/UserPreferenceListener.php',
'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndex.php',
'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 2d00105b548..a2048fcdf05 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -299,7 +299,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Files\\RootCollection' => __DIR__ . '/..' . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
- 'OCA\\DAV\\HookManager' => __DIR__ . '/..' . '/../lib/HookManager.php',
'OCA\\DAV\\Listener\\ActivityUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/ActivityUpdaterListener.php',
'OCA\\DAV\\Listener\\AddMissingIndicesListener' => __DIR__ . '/..' . '/../lib/Listener/AddMissingIndicesListener.php',
'OCA\\DAV\\Listener\\AddressbookListener' => __DIR__ . '/..' . '/../lib/Listener/AddressbookListener.php',
@@ -314,6 +313,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Listener\\OutOfOfficeListener' => __DIR__ . '/..' . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => __DIR__ . '/..' . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => __DIR__ . '/..' . '/../lib/Listener/TrustedServerRemovedListener.php',
+ 'OCA\\DAV\\Listener\\UserEventsListener' => __DIR__ . '/..' . '/../lib/Listener/UserEventsListener.php',
'OCA\\DAV\\Listener\\UserPreferenceListener' => __DIR__ . '/..' . '/../lib/Listener/UserPreferenceListener.php',
'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndex.php',
'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php',
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index ae587c98965..a01125a1103 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -20,7 +20,6 @@ use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
use OCA\DAV\CalDAV\Reminder\Notifier;
use OCA\DAV\Capabilities;
-
use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\PhotoCache;
use OCA\DAV\CardDAV\SyncService;
@@ -47,7 +46,6 @@ use OCA\DAV\Events\CardDeletedEvent;
use OCA\DAV\Events\CardUpdatedEvent;
use OCA\DAV\Events\SubscriptionCreatedEvent;
use OCA\DAV\Events\SubscriptionDeletedEvent;
-use OCA\DAV\HookManager;
use OCA\DAV\Listener\ActivityUpdaterListener;
use OCA\DAV\Listener\AddMissingIndicesListener;
use OCA\DAV\Listener\AddressbookListener;
@@ -62,6 +60,7 @@ use OCA\DAV\Listener\ClearPhotoCacheListener;
use OCA\DAV\Listener\OutOfOfficeListener;
use OCA\DAV\Listener\SubscriptionListener;
use OCA\DAV\Listener\TrustedServerRemovedListener;
+use OCA\DAV\Listener\UserEventsListener;
use OCA\DAV\Listener\UserPreferenceListener;
use OCA\DAV\Search\ContactsSearchProvider;
use OCA\DAV\Search\EventsSearchProvider;
@@ -81,15 +80,21 @@ use OCP\Config\BeforePreferenceDeletedEvent;
use OCP\Config\BeforePreferenceSetEvent;
use OCP\Contacts\IManager as IContactsManager;
use OCP\DB\Events\AddMissingIndicesEvent;
-use OCP\EventDispatcher\GenericEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\Events\TrustedServerRemovedEvent;
use OCP\Files\AppData\IAppDataFactory;
-use OCP\IUser;
use OCP\Server;
+use OCP\User\Events\BeforeUserDeletedEvent;
+use OCP\User\Events\BeforeUserIdUnassignedEvent;
use OCP\User\Events\OutOfOfficeChangedEvent;
use OCP\User\Events\OutOfOfficeClearedEvent;
use OCP\User\Events\OutOfOfficeScheduledEvent;
+use OCP\User\Events\UserChangedEvent;
+use OCP\User\Events\UserCreatedEvent;
+use OCP\User\Events\UserDeletedEvent;
+use OCP\User\Events\UserFirstTimeLoggedInEvent;
+use OCP\User\Events\UserIdAssignedEvent;
+use OCP\User\Events\UserIdUnassignedEvent;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Throwable;
@@ -187,6 +192,15 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(OutOfOfficeClearedEvent::class, OutOfOfficeListener::class);
$context->registerEventListener(OutOfOfficeScheduledEvent::class, OutOfOfficeListener::class);
+ $context->registerEventListener(UserFirstTimeLoggedInEvent::class, UserEventsListener::class);
+ $context->registerEventListener(UserIdAssignedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(BeforeUserIdUnassignedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(UserIdUnassignedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(BeforeUserDeletedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(UserDeletedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(UserCreatedEvent::class, UserEventsListener::class);
+ $context->registerEventListener(UserChangedEvent::class, UserEventsListener::class);
+
$context->registerNotifierService(Notifier::class);
$context->registerCalendarProvider(CalendarProvider::class);
@@ -209,18 +223,10 @@ class Application extends App implements IBootstrap {
$context->injectFn([$this, 'registerCalendarReminders']);
}
- public function registerHooks(HookManager $hm,
+ public function registerHooks(
IEventDispatcher $dispatcher,
- IAppContainer $container) {
- $hm->setup();
-
- // first time login event setup
- $dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm): void {
- if ($event instanceof GenericEvent) {
- $hm->firstLogin($event->getSubject());
- }
- });
-
+ IAppContainer $container,
+ ): void {
$dispatcher->addListener(UserUpdatedEvent::class, function (UserUpdatedEvent $event) use ($container): void {
/** @var SyncService $syncService */
$syncService = Server::get(SyncService::class);
@@ -240,7 +246,6 @@ class Application extends App implements IBootstrap {
// Here we should recalculate if reminders should be sent to new or old sharees
});
-
}
public function registerContactsManager(IContactsManager $cm, IAppContainer $container): void {
diff --git a/apps/dav/lib/HookManager.php b/apps/dav/lib/HookManager.php
deleted file mode 100644
index 9f4bc95d89b..00000000000
--- a/apps/dav/lib/HookManager.php
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-namespace OCA\DAV;
-
-use OCA\DAV\CalDAV\CalDavBackend;
-use OCA\DAV\CardDAV\CardDavBackend;
-use OCA\DAV\CardDAV\SyncService;
-use OCP\Defaults;
-use OCP\IUser;
-use OCP\IUserManager;
-use OCP\Util;
-use Psr\Log\LoggerInterface;
-
-class HookManager {
-
- /** @var IUser[] */
- private $usersToDelete = [];
-
- /** @var array */
- private $calendarsToDelete = [];
-
- /** @var array */
- private $subscriptionsToDelete = [];
-
- /** @var array */
- private $addressBooksToDelete = [];
-
- public function __construct(
- private IUserManager $userManager,
- private SyncService $syncService,
- private CalDavBackend $calDav,
- private CardDavBackend $cardDav,
- private Defaults $themingDefaults,
- ) {
- }
-
- public function setup() {
- Util::connectHook('OC_User',
- 'post_createUser',
- $this,
- 'postCreateUser');
- \OC::$server->getUserManager()->listen('\OC\User', 'assignedUserId', function ($uid): void {
- $this->postCreateUser(['uid' => $uid]);
- });
- Util::connectHook('OC_User',
- 'pre_deleteUser',
- $this,
- 'preDeleteUser');
- \OC::$server->getUserManager()->listen('\OC\User', 'preUnassignedUserId', [$this, 'preUnassignedUserId']);
- Util::connectHook('OC_User',
- 'post_deleteUser',
- $this,
- 'postDeleteUser');
- \OC::$server->getUserManager()->listen('\OC\User', 'postUnassignedUserId', function ($uid): void {
- $this->postDeleteUser(['uid' => $uid]);
- });
- \OC::$server->getUserManager()->listen('\OC\User', 'postUnassignedUserId', [$this, 'postUnassignedUserId']);
- Util::connectHook('OC_User', 'changeUser', $this, 'changeUser');
- }
-
- public function postCreateUser($params) {
- $user = $this->userManager->get($params['uid']);
- if ($user instanceof IUser) {
- $this->syncService->updateUser($user);
- }
- }
-
- public function preDeleteUser($params) {
- $uid = $params['uid'];
- $userPrincipalUri = 'principals/users/' . $uid;
- $this->usersToDelete[$uid] = $this->userManager->get($uid);
- $this->calendarsToDelete = $this->calDav->getUsersOwnCalendars($userPrincipalUri);
- $this->subscriptionsToDelete = $this->calDav->getSubscriptionsForUser($userPrincipalUri);
- $this->addressBooksToDelete = $this->cardDav->getUsersOwnAddressBooks($userPrincipalUri);
- }
-
- public function preUnassignedUserId($uid) {
- $this->usersToDelete[$uid] = $this->userManager->get($uid);
- }
-
- public function postDeleteUser($params) {
- $uid = $params['uid'];
- if (isset($this->usersToDelete[$uid])) {
- $this->syncService->deleteUser($this->usersToDelete[$uid]);
- }
-
- foreach ($this->calendarsToDelete as $calendar) {
- $this->calDav->deleteCalendar(
- $calendar['id'],
- true // Make sure the data doesn't go into the trashbin, a new user with the same UID would later see it otherwise
- );
- }
-
- foreach ($this->subscriptionsToDelete as $subscription) {
- $this->calDav->deleteSubscription(
- $subscription['id'],
- );
- }
- $this->calDav->deleteAllSharesByUser('principals/users/' . $uid);
-
- foreach ($this->addressBooksToDelete as $addressBook) {
- $this->cardDav->deleteAddressBook($addressBook['id']);
- }
- }
-
- public function postUnassignedUserId($uid) {
- if (isset($this->usersToDelete[$uid])) {
- $this->syncService->deleteUser($this->usersToDelete[$uid]);
- }
- }
-
- public function changeUser($params) {
- $user = $params['user'];
- $feature = $params['feature'];
- // This case is already covered by the account manager firing up a signal
- // later on
- if ($feature !== 'eMailAddress' && $feature !== 'displayName') {
- $this->syncService->updateUser($user);
- }
- }
-
- /**
- * @return void
- */
- public function firstLogin(?IUser $user = null) {
- if (!is_null($user)) {
- $principal = 'principals/users/' . $user->getUID();
- if ($this->calDav->getCalendarsForUserCount($principal) === 0) {
- try {
- $this->calDav->createCalendar($principal, CalDavBackend::PERSONAL_CALENDAR_URI, [
- '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME,
- '{http://apple.com/ns/ical/}calendar-color' => $this->themingDefaults->getColorPrimary(),
- 'components' => 'VEVENT'
- ]);
- } catch (\Exception $e) {
- \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
- }
- }
- if ($this->cardDav->getAddressBooksForUserCount($principal) === 0) {
- try {
- $this->cardDav->createAddressBook($principal, CardDavBackend::PERSONAL_ADDRESSBOOK_URI, [
- '{DAV:}displayname' => CardDavBackend::PERSONAL_ADDRESSBOOK_NAME,
- ]);
- } catch (\Exception $e) {
- \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
- }
- }
- }
- }
-}
diff --git a/apps/dav/lib/Listener/UserEventsListener.php b/apps/dav/lib/Listener/UserEventsListener.php
new file mode 100644
index 00000000000..c8811f26d9b
--- /dev/null
+++ b/apps/dav/lib/Listener/UserEventsListener.php
@@ -0,0 +1,151 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Listener;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\SyncService;
+use OCP\Defaults;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\User\Events\BeforeUserDeletedEvent;
+use OCP\User\Events\BeforeUserIdUnassignedEvent;
+use OCP\User\Events\UserChangedEvent;
+use OCP\User\Events\UserCreatedEvent;
+use OCP\User\Events\UserDeletedEvent;
+use OCP\User\Events\UserFirstTimeLoggedInEvent;
+use OCP\User\Events\UserIdAssignedEvent;
+use OCP\User\Events\UserIdUnassignedEvent;
+use Psr\Log\LoggerInterface;
+
+/** @template-implements IEventListener<UserFirstTimeLoggedInEvent|UserIdAssignedEvent|BeforeUserIdUnassignedEvent|UserIdUnassignedEvent|BeforeUserDeletedEvent|UserDeletedEvent|UserCreatedEvent|UserChangedEvent> */
+class UserEventsListener implements IEventListener {
+
+ /** @var IUser[] */
+ private array $usersToDelete = [];
+
+ private array $calendarsToDelete = [];
+ private array $subscriptionsToDelete = [];
+ private array $addressBooksToDelete = [];
+
+ public function __construct(
+ private IUserManager $userManager,
+ private SyncService $syncService,
+ private CalDavBackend $calDav,
+ private CardDavBackend $cardDav,
+ private Defaults $themingDefaults,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof UserCreatedEvent) {
+ $this->postCreateUser($event->getUser());
+ } elseif ($event instanceof UserIdAssignedEvent) {
+ $user = $this->userManager->get($event->getUserId());
+ if ($user !== null) {
+ $this->postCreateUser($user);
+ }
+ } elseif ($event instanceof BeforeUserDeletedEvent) {
+ $this->preDeleteUser($event->getUser());
+ } elseif ($event instanceof BeforeUserIdUnassignedEvent) {
+ $this->preUnassignedUserId($event->getUserId());
+ } elseif ($event instanceof UserDeletedEvent) {
+ $this->postDeleteUser($event->getUid());
+ } elseif ($event instanceof UserIdUnassignedEvent) {
+ $this->postDeleteUser($event->getUserId());
+ } elseif ($event instanceof UserChangedEvent) {
+ $this->changeUser($event->getUser(), $event->getFeature());
+ } elseif ($event instanceof UserFirstTimeLoggedInEvent) {
+ $this->firstLogin($event->getUser());
+ }
+ }
+
+ public function postCreateUser(IUser $user): void {
+ $this->syncService->updateUser($user);
+ }
+
+ public function preDeleteUser(IUser $user): void {
+ $uid = $user->getUID();
+ $userPrincipalUri = 'principals/users/' . $uid;
+ $this->usersToDelete[$uid] = $user;
+ $this->calendarsToDelete[$uid] = $this->calDav->getUsersOwnCalendars($userPrincipalUri);
+ $this->subscriptionsToDelete[$uid] = $this->calDav->getSubscriptionsForUser($userPrincipalUri);
+ $this->addressBooksToDelete[$uid] = $this->cardDav->getUsersOwnAddressBooks($userPrincipalUri);
+ }
+
+ public function preUnassignedUserId(string $uid): void {
+ $user = $this->userManager->get($uid);
+ if ($user !== null) {
+ $this->usersToDelete[$uid] = $user;
+ }
+ }
+
+ public function postDeleteUser(string $uid): void {
+ if (isset($this->usersToDelete[$uid])) {
+ $this->syncService->deleteUser($this->usersToDelete[$uid]);
+ }
+
+ foreach ($this->calendarsToDelete[$uid] as $calendar) {
+ $this->calDav->deleteCalendar(
+ $calendar['id'],
+ true // Make sure the data doesn't go into the trashbin, a new user with the same UID would later see it otherwise
+ );
+ }
+
+ foreach ($this->subscriptionsToDelete[$uid] as $subscription) {
+ $this->calDav->deleteSubscription(
+ $subscription['id'],
+ );
+ }
+ $this->calDav->deleteAllSharesByUser('principals/users/' . $uid);
+
+ foreach ($this->addressBooksToDelete[$uid] as $addressBook) {
+ $this->cardDav->deleteAddressBook($addressBook['id']);
+ }
+
+ unset($this->calendarsToDelete[$uid]);
+ unset($this->subscriptionsToDelete[$uid]);
+ unset($this->addressBooksToDelete[$uid]);
+ }
+
+ public function changeUser(IUser $user, string $feature): void {
+ // This case is already covered by the account manager firing up a signal
+ // later on
+ if ($feature !== 'eMailAddress' && $feature !== 'displayName') {
+ $this->syncService->updateUser($user);
+ }
+ }
+
+ public function firstLogin(IUser $user): void {
+ $principal = 'principals/users/' . $user->getUID();
+ if ($this->calDav->getCalendarsForUserCount($principal) === 0) {
+ try {
+ $this->calDav->createCalendar($principal, CalDavBackend::PERSONAL_CALENDAR_URI, [
+ '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME,
+ '{http://apple.com/ns/ical/}calendar-color' => $this->themingDefaults->getColorPrimary(),
+ 'components' => 'VEVENT'
+ ]);
+ } catch (\Exception $e) {
+ \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
+ }
+ }
+ if ($this->cardDav->getAddressBooksForUserCount($principal) === 0) {
+ try {
+ $this->cardDav->createAddressBook($principal, CardDavBackend::PERSONAL_ADDRESSBOOK_URI, [
+ '{DAV:}displayname' => CardDavBackend::PERSONAL_ADDRESSBOOK_NAME,
+ ]);
+ } catch (\Exception $e) {
+ \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
+ }
+ }
+ }
+}
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index 96968909d1c..8f89283e94d 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -526,7 +526,8 @@ class CardDavBackendTest extends TestCase {
$query = $this->db->getQueryBuilder();
$query->select('*')
- ->from('cards_properties');
+ ->from('cards_properties')
+ ->orderBy('name');
$qResult = $query->execute();
$result = $qResult->fetchAll();
@@ -534,13 +535,13 @@ class CardDavBackendTest extends TestCase {
$this->assertSame(2, count($result));
- $this->assertSame('UID', $result[0]['name']);
- $this->assertSame($cardUri, $result[0]['value']);
+ $this->assertSame('FN', $result[0]['name']);
+ $this->assertSame('John Doe', $result[0]['value']);
$this->assertSame($bookId, (int)$result[0]['addressbookid']);
$this->assertSame($cardId, (int)$result[0]['cardid']);
- $this->assertSame('FN', $result[1]['name']);
- $this->assertSame('John Doe', $result[1]['value']);
+ $this->assertSame('UID', $result[1]['name']);
+ $this->assertSame($cardUri, $result[1]['value']);
$this->assertSame($bookId, (int)$result[1]['addressbookid']);
$this->assertSame($cardId, (int)$result[1]['cardid']);
diff --git a/apps/dav/tests/unit/DAV/HookManagerTest.php b/apps/dav/tests/unit/DAV/HookManagerTest.php
deleted file mode 100644
index 746727cd2b3..00000000000
--- a/apps/dav/tests/unit/DAV/HookManagerTest.php
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-namespace OCA\DAV\Tests\unit\DAV;
-
-use OCA\DAV\CalDAV\CalDavBackend;
-use OCA\DAV\CardDAV\CardDavBackend;
-use OCA\DAV\CardDAV\SyncService;
-use OCA\DAV\HookManager;
-use OCP\Defaults;
-use OCP\IL10N;
-use OCP\IUser;
-use OCP\IUserManager;
-use PHPUnit\Framework\MockObject\MockObject;
-use Test\TestCase;
-
-class HookManagerTest extends TestCase {
- /** @var IL10N */
- private $l10n;
-
- protected function setUp(): void {
- parent::setUp();
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10n
- ->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- }
-
- public function test(): void {
- $user = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
- $user->expects($this->once())->method('getUID')->willReturn('newUser');
-
- /** @var IUserManager | MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var SyncService | MockObject $syncService */
- $syncService = $this->getMockBuilder(SyncService::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var Defaults | MockObject $syncService */
- $defaults = $this->getMockBuilder(Defaults::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca');
-
- /** @var CalDavBackend | MockObject $cal */
- $cal = $this->getMockBuilder(CalDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0);
- $cal->expects($this->once())->method('createCalendar')->with(
- 'principals/users/newUser',
- 'personal', [
- '{DAV:}displayname' => 'Personal',
- '{http://apple.com/ns/ical/}calendar-color' => '#745bca',
- 'components' => 'VEVENT'
- ]);
-
- /** @var CardDavBackend | MockObject $card */
- $card = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0);
- $card->expects($this->once())->method('createAddressBook')->with(
- 'principals/users/newUser',
- 'contacts', ['{DAV:}displayname' => 'Contacts']);
-
- $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults);
- $hm->firstLogin($user);
- }
-
- public function testWithExisting(): void {
- $user = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
- $user->expects($this->once())->method('getUID')->willReturn('newUser');
-
- /** @var IUserManager | MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var SyncService | MockObject $syncService */
- $syncService = $this->getMockBuilder(SyncService::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var Defaults | MockObject $syncService */
- $defaults = $this->getMockBuilder(Defaults::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var CalDavBackend | MockObject $cal */
- $cal = $this->getMockBuilder(CalDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1);
- $cal->expects($this->never())->method('createCalendar');
-
- /** @var CardDavBackend | MockObject $card */
- $card = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1);
- $card->expects($this->never())->method('createAddressBook');
-
- $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults);
- $hm->firstLogin($user);
- }
-
- public function testWithBirthdayCalendar(): void {
- $user = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
- $user->expects($this->once())->method('getUID')->willReturn('newUser');
-
- /** @var IUserManager | MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var SyncService | MockObject $syncService */
- $syncService = $this->getMockBuilder(SyncService::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var Defaults | MockObject $syncService */
- $defaults = $this->getMockBuilder(Defaults::class)
- ->disableOriginalConstructor()
- ->getMock();
- $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca');
-
- /** @var CalDavBackend | MockObject $cal */
- $cal = $this->getMockBuilder(CalDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0);
- $cal->expects($this->once())->method('createCalendar')->with(
- 'principals/users/newUser',
- 'personal', [
- '{DAV:}displayname' => 'Personal',
- '{http://apple.com/ns/ical/}calendar-color' => '#745bca',
- 'components' => 'VEVENT'
- ]);
-
- /** @var CardDavBackend | MockObject $card */
- $card = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0);
- $card->expects($this->once())->method('createAddressBook')->with(
- 'principals/users/newUser',
- 'contacts', ['{DAV:}displayname' => 'Contacts']);
-
- $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults);
- $hm->firstLogin($user);
- }
-
- public function testDeleteCalendar(): void {
- $user = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var IUserManager | MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $userManager->expects($this->once())->method('get')->willReturn($user);
-
- /** @var SyncService | MockObject $syncService */
- $syncService = $this->getMockBuilder(SyncService::class)
- ->disableOriginalConstructor()
- ->getMock();
- $syncService->expects($this->once())
- ->method('deleteUser');
-
- /** @var Defaults | MockObject $syncService */
- $defaults = $this->getMockBuilder(Defaults::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var CalDavBackend | MockObject $cal */
- $cal = $this->getMockBuilder(CalDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $cal->expects($this->once())->method('getUsersOwnCalendars')->willReturn([
- ['id' => 'personal']
- ]);
- $cal->expects($this->once())->method('getSubscriptionsForUser')->willReturn([
- ['id' => 'some-subscription']
- ]);
- $cal->expects($this->once())->method('deleteCalendar')->with('personal');
- $cal->expects($this->once())->method('deleteSubscription')->with('some-subscription');
- $cal->expects($this->once())->method('deleteAllSharesByUser');
-
- /** @var CardDavBackend | MockObject $card */
- $card = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $card->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([
- ['id' => 'personal']
- ]);
- $card->expects($this->once())->method('deleteAddressBook');
-
- $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults);
- $hm->preDeleteUser(['uid' => 'newUser']);
- $hm->postDeleteUser(['uid' => 'newUser']);
- }
-}
diff --git a/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php
new file mode 100644
index 00000000000..ce91a2a7fa6
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php
@@ -0,0 +1,140 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+namespace OCA\DAV\Tests\unit\DAV\Listener;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\SyncService;
+use OCA\DAV\Listener\UserEventsListener;
+use OCP\Defaults;
+use OCP\IUser;
+use OCP\IUserManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class UserEventsListenerTest extends TestCase {
+ private IUserManager&MockObject $userManager;
+ private SyncService&MockObject $syncService;
+ private CalDavBackend&MockObject $calDavBackend;
+ private CardDavBackend&MockObject $cardDavBackend;
+ private Defaults&MockObject $defaults;
+
+ private UserEventsListener $userEventsListener;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->syncService = $this->createMock(SyncService::class);
+ $this->calDavBackend = $this->createMock(CalDavBackend::class);
+ $this->cardDavBackend = $this->createMock(CardDavBackend::class);
+ $this->defaults = $this->createMock(Defaults::class);
+ $this->userEventsListener = new UserEventsListener(
+ $this->userManager,
+ $this->syncService,
+ $this->calDavBackend,
+ $this->cardDavBackend,
+ $this->defaults,
+ );
+ }
+
+ public function test(): void {
+ $user = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())->method('getUID')->willReturn('newUser');
+
+ $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca');
+
+ $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0);
+ $this->calDavBackend->expects($this->once())->method('createCalendar')->with(
+ 'principals/users/newUser',
+ 'personal', [
+ '{DAV:}displayname' => 'Personal',
+ '{http://apple.com/ns/ical/}calendar-color' => '#745bca',
+ 'components' => 'VEVENT'
+ ]);
+
+ $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0);
+ $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with(
+ 'principals/users/newUser',
+ 'contacts', ['{DAV:}displayname' => 'Contacts']);
+
+ $this->userEventsListener->firstLogin($user);
+ }
+
+ public function testWithExisting(): void {
+ $user = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())->method('getUID')->willReturn('newUser');
+
+ $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1);
+ $this->calDavBackend->expects($this->never())->method('createCalendar');
+
+ $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1);
+ $this->cardDavBackend->expects($this->never())->method('createAddressBook');
+
+ $this->userEventsListener->firstLogin($user);
+ }
+
+ public function testWithBirthdayCalendar(): void {
+ $user = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())->method('getUID')->willReturn('newUser');
+
+ $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca');
+
+ $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0);
+ $this->calDavBackend->expects($this->once())->method('createCalendar')->with(
+ 'principals/users/newUser',
+ 'personal', [
+ '{DAV:}displayname' => 'Personal',
+ '{http://apple.com/ns/ical/}calendar-color' => '#745bca',
+ 'components' => 'VEVENT'
+ ]);
+
+ $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0);
+ $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with(
+ 'principals/users/newUser',
+ 'contacts', ['{DAV:}displayname' => 'Contacts']);
+
+ $this->userEventsListener->firstLogin($user);
+ }
+
+ public function testDeleteCalendar(): void {
+ $user = $this->getMockBuilder(IUser::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())->method('getUID')->willReturn('newUser');
+
+ $this->syncService->expects($this->once())
+ ->method('deleteUser');
+
+ $this->calDavBackend->expects($this->once())->method('getUsersOwnCalendars')->willReturn([
+ ['id' => 'personal']
+ ]);
+ $this->calDavBackend->expects($this->once())->method('getSubscriptionsForUser')->willReturn([
+ ['id' => 'some-subscription']
+ ]);
+ $this->calDavBackend->expects($this->once())->method('deleteCalendar')->with('personal');
+ $this->calDavBackend->expects($this->once())->method('deleteSubscription')->with('some-subscription');
+ $this->calDavBackend->expects($this->once())->method('deleteAllSharesByUser');
+
+ $this->cardDavBackend->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([
+ ['id' => 'personal']
+ ]);
+ $this->cardDavBackend->expects($this->once())->method('deleteAddressBook');
+
+ $this->userEventsListener->preDeleteUser($user);
+ $this->userEventsListener->postDeleteUser('newUser');
+ }
+}
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 2824f99f79d..18df1f058da 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -543,12 +543,6 @@
<code><![CDATA[bool]]></code>
</InvalidReturnType>
</file>
- <file src="apps/dav/lib/HookManager.php">
- <InvalidPropertyAssignmentValue>
- <code><![CDATA[$this->usersToDelete]]></code>
- <code><![CDATA[$this->usersToDelete]]></code>
- </InvalidPropertyAssignmentValue>
- </file>
<file src="apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php">
<ParamNameMismatch>
<code><![CDATA[$arguments]]></code>
diff --git a/tests/lib/User/UserTest.php b/tests/lib/User/UserTest.php
index e79aaadd593..44d28481ed5 100644
--- a/tests/lib/User/UserTest.php
+++ b/tests/lib/User/UserTest.php
@@ -929,6 +929,12 @@ class UserTest extends TestCase {
$this->equalTo('enabled'),
'true'
);
+ /* dav event listener gets the manager list from config */
+ $config->expects(self::any())
+ ->method('getUserValue')
+ ->willReturnCallback(
+ fn ($user, $app, $key, $default) => ($key === 'enabled' ? 'false' : $default)
+ );
$user = new User('foo', $backend, $this->dispatcher, null, $config);
$user->setEnabled(true);