diff --git a/appinfo/info.xml b/appinfo/info.xml index 116a697c..cace2827 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -13,7 +13,7 @@ Approve/reject files based on workflows defined by admins. **Warning**: The DocuSign integration is no longer part of this app and can be installed with [this app](https://apps.nextcloud.com/apps/integration_docusign). ]]> - 2.8.0 + 2.8.1-dev.0 agpl Julien Veyssier Approval diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 493a8aab..85758401 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -12,6 +12,7 @@ use OCA\Approval\Listener\LoadAdditionalScriptsListener; use OCA\Approval\Listener\LoadSidebarScripts; use OCA\Approval\Listener\UpdateFilesListener; +use OCA\Approval\Listener\UserDeletedListener; use OCA\Approval\Notification\Notifier; use OCA\Approval\Service\ApprovalService; @@ -26,6 +27,7 @@ use OCP\FilesMetadata\Event\MetadataBackgroundEvent; use OCP\SabrePluginEvent; use OCP\SystemTag\MapperEvent; +use OCP\User\Events\UserDeletedEvent; class Application extends App implements IBootstrap { public const APP_ID = 'approval'; @@ -66,6 +68,7 @@ public function register(IRegistrationContext $context): void { $context->registerNotifierService(Notifier::class); $context->registerDashboardWidget(ApprovalPendingWidget::class); $context->registerEventListener(MetadataBackgroundEvent::class, UpdateFilesListener::class); + $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); } public function boot(IBootContext $context): void { diff --git a/lib/Listener/UserDeletedListener.php b/lib/Listener/UserDeletedListener.php new file mode 100644 index 00000000..9d4f6401 --- /dev/null +++ b/lib/Listener/UserDeletedListener.php @@ -0,0 +1,31 @@ + */ +class UserDeletedListener implements IEventListener { + public function __construct( + private RuleService $ruleService, + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + return; + } + + $this->ruleService->deleteUserFromRules($event->getUser()->getUID()); + } +} diff --git a/lib/Migration/Version020801Date20260917092831.php b/lib/Migration/Version020801Date20260917092831.php new file mode 100644 index 00000000..c5a5eeff --- /dev/null +++ b/lib/Migration/Version020801Date20260917092831.php @@ -0,0 +1,56 @@ +connection->getQueryBuilder(); + $qb->selectDistinct('entity_id') + ->from('approval_rule_' . $role) + ->where( + $qb->expr()->eq('entity_type', $qb->createNamedParameter(Application::TYPE_USER, IQueryBuilder::PARAM_INT)) + ); + + $result = $qb->executeQuery(); + $userIds = $result->fetchAll(\PDO::FETCH_COLUMN); + $result->closeCursor(); + + foreach ($userIds as $userId) { + if ($this->userManager->get($userId) === null) { + $deleteQb = $this->connection->getQueryBuilder(); + $deleteQb->delete('approval_rule_' . $role) + ->where( + $deleteQb->expr()->eq('entity_type', $deleteQb->createNamedParameter(Application::TYPE_USER, IQueryBuilder::PARAM_INT)) + ) + ->andWhere( + $deleteQb->expr()->eq('entity_id', $deleteQb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ); + $deleteQb->executeStatement(); + } + } + } + } +} diff --git a/lib/Service/RuleService.php b/lib/Service/RuleService.php index b78394ae..f58213fe 100644 --- a/lib/Service/RuleService.php +++ b/lib/Service/RuleService.php @@ -561,6 +561,28 @@ public function clearRuleCaches(): void { $cache->remove('approval_tags'); } + /** + * Remove a user from approval approvers and requesters. + */ + public function deleteUserFromRules(string $userId): void { + $this->cachedRules = null; + $qb = $this->db->getQueryBuilder(); + + foreach (['approvers', 'requesters'] as $role) { + $qb->delete('approval_rule_' . $role) + ->where( + $qb->expr()->eq('entity_type', $qb->createNamedParameter(Application::TYPE_USER, IQueryBuilder::PARAM_INT)) + ) + ->andWhere( + $qb->expr()->eq('entity_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ); + $qb->executeStatement(); + $qb = $qb->resetQueryParts(); + } + + $this->clearRuleCaches(); + } + /** * Checks that the approval of the file was after the time given. * This does not verify that the file was actually approved. diff --git a/tests/unit/Service/ApprovalServiceTest.php b/tests/unit/Service/ApprovalServiceTest.php index 266636c7..6d442473 100644 --- a/tests/unit/Service/ApprovalServiceTest.php +++ b/tests/unit/Service/ApprovalServiceTest.php @@ -271,6 +271,15 @@ public function testGetRuleAuthorizedUserIds() { $this->assertEquals('user1', $uidRequesters[0]); } + public function testDeleteUserFromRules(): void { + $this->ruleService->deleteUserFromRules('user1'); + + $rule = $this->ruleService->getRule($this->idRule1); + $this->assertNotNull($rule); + $this->assertCount(0, $rule['approvers']); + $this->assertCount(0, $rule['requesters']); + } + public function testGetApprovalState() { $uf = $this->root->getUserFolder('user1'); $file1 = $uf->newFile('file1.txt', 'content');