diff --git a/appinfo/info.xml b/appinfo/info.xml
index 99675579..b41b25d9 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).
]]>
- 3.3.1
+ 3.3.2-dev.0
agpl
Julien Veyssier
Approval
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 88aa49d8..af7e4df0 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;
use OCA\DAV\Events\SabrePluginAddEvent;
@@ -24,6 +25,7 @@
use OCP\EventDispatcher\IEventDispatcher;
use OCP\FilesMetadata\Event\MetadataBackgroundEvent;
use OCP\SystemTag\TagAssignedEvent;
+use OCP\User\Events\UserDeletedEvent;
use Override;
class Application extends App implements IBootstrap {
@@ -68,6 +70,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);
}
#[Override]
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/Version030302Date20260820101934.php b/lib/Migration/Version030302Date20260820101934.php
new file mode 100644
index 00000000..b2150b5e
--- /dev/null
+++ b/lib/Migration/Version030302Date20260820101934.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/ApprovalService.php b/lib/Service/ApprovalService.php
index e32b2d31..6bb4f5a1 100644
--- a/lib/Service/ApprovalService.php
+++ b/lib/Service/ApprovalService.php
@@ -853,7 +853,6 @@ public function propFind(int $nodeId): int {
return $state['state'];
}
-
/**
* Get approval state for multiple files and loads all the tags at once
*
diff --git a/lib/Service/RuleService.php b/lib/Service/RuleService.php
index d5d091c4..d975432a 100644
--- a/lib/Service/RuleService.php
+++ b/lib/Service/RuleService.php
@@ -572,6 +572,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 84aabeca..40da6fea 100644
--- a/tests/unit/Service/ApprovalServiceTest.php
+++ b/tests/unit/Service/ApprovalServiceTest.php
@@ -240,6 +240,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(): void {
$uf = $this->root->getUserFolder('user1');
$file1 = $uf->newFile('file1.txt', 'content');