Merge pull request #50689 from nextcloud/fix/migrate-dav-to-events

fix(dav): Migrate from hooks to user events
This commit is contained in:
Côme Chilliet 2025-02-13 10:24:05 +01:00 committed by GitHub
commit ee48cafd20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 326 additions and 406 deletions

View file

@ -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',

View file

@ -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',

View file

@ -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 {

View file

@ -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]);
}
}
}
}
}

View file

@ -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]);
}
}
}
}

View file

@ -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']);

View file

@ -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']);
}
}

View file

@ -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');
}
}

View file

@ -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>

View file

@ -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);