From d773baab536c928093ec15d3450e7426941b1846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Bari?= Date: Tue, 26 May 2026 22:18:14 +0200 Subject: [PATCH 1/5] feat: adding an option to exclude users from the acitivity log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tamás Bari --- lib/Data.php | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/Data.php b/lib/Data.php index fa856f26f..dae603a3d 100644 --- a/lib/Data.php +++ b/lib/Data.php @@ -39,6 +39,44 @@ public function __construct( ) { } + /** + * Check if the event should be processed (not excluded and has valid target) + * + * @param IEvent $event + * @return bool + */ + private function shouldSend(IEvent $event): bool { + return $event->getAffectedUser() !== '' && !$this->isExcludedAuthor($event); + } + + /** + * Check if the event's author is excluded from activity logging + * + * @param IEvent $event + * @return bool + */ + private function isExcludedAuthor(IEvent $event): bool { + $excludedUsers = $this->config->getSystemValue('activity_log_exclude_users', []); + if (empty($excludedUsers)) { + return false; + } + $author = $event->getAuthor(); + if ($author === null || $author === '') { + return false; + } + if (!isset($excludedUsers[$author])) { + return false; + } + $rule = $excludedUsers[$author]; + if ($rule === 'all') { + return true; + } + if (is_array($rule)) { + return in_array($event->getType(), $rule, true); + } + return false; + } + /** * Send an event into the activity stream * @@ -46,7 +84,7 @@ public function __construct( * @return int */ public function send(IEvent $event): int { - if ($event->getAffectedUser() === '') { + if (!$this->shouldSend($event)) { return 0; } @@ -104,6 +142,10 @@ public function send(IEvent $event): int { * @throws Exception */ public function bulkSend(IEvent $event, array $affectedUsers): array { + if ($this->isExcludedAuthor($event)) { + return []; + } + $this->connection->beginTransaction(); $activityIds = []; @@ -170,8 +212,7 @@ public function bulkSend(IEvent $event, array $affectedUsers): array { * @return bool */ public function storeMail(IEvent $event, int $latestSendTime): bool { - $affectedUser = $event->getAffectedUser(); - if ($affectedUser === '') { + if (!$this->shouldSend($event)) { return false; } From a9fb760a74840e48fb7d3190371144352812a60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Bari?= Date: Wed, 27 May 2026 16:10:14 +0200 Subject: [PATCH 2/5] fix: Fixing missing $affectedUser in storeMail() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tamás Bari --- lib/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Data.php b/lib/Data.php index dae603a3d..1684ce6ff 100644 --- a/lib/Data.php +++ b/lib/Data.php @@ -236,7 +236,7 @@ public function storeMail(IEvent $event, int $latestSendTime): bool { 'amq_appid' => $event->getApp(), 'amq_subject' => $event->getSubject(), 'amq_subjectparams' => json_encode($event->getSubjectParameters()), - 'amq_affecteduser' => $affectedUser, + 'amq_affecteduser' => $event->getAffectedUser(), 'amq_timestamp' => $event->getTimestamp(), 'amq_type' => $event->getType(), 'amq_latest_send' => $latestSendTime, From bf3489588d5a63e9b5554d7571d859c657376150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Bari?= Date: Wed, 27 May 2026 16:11:02 +0200 Subject: [PATCH 3/5] fix: Removing 'all' as an even type filter option for excluded users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tamás Bari --- lib/Data.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/Data.php b/lib/Data.php index 1684ce6ff..fb44f686a 100644 --- a/lib/Data.php +++ b/lib/Data.php @@ -61,16 +61,10 @@ private function isExcludedAuthor(IEvent $event): bool { return false; } $author = $event->getAuthor(); - if ($author === null || $author === '') { - return false; - } - if (!isset($excludedUsers[$author])) { + if ($author === null || $author === '' || !isset($excludedUsers[$author])) { return false; } $rule = $excludedUsers[$author]; - if ($rule === 'all') { - return true; - } if (is_array($rule)) { return in_array($event->getType(), $rule, true); } From 2c44647968b81132a5bae0432a9b15b906867138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Bari?= Date: Thu, 25 Jun 2026 13:29:58 +0200 Subject: [PATCH 4/5] chore: Adding tests for excluded users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tamás Bari --- tests/DataTest.php | 123 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/tests/DataTest.php b/tests/DataTest.php index 1ff577a49..a227ea194 100644 --- a/tests/DataTest.php +++ b/tests/DataTest.php @@ -347,6 +347,129 @@ public function testDeleteAffectedUserActivities(): void { $this->deleteTestActivities(); } + public static function dataExcludedAuthor(): array { + return [ + // author+type match → blocked + ['alice', 'target', 'file_created', ['alice' => ['file_created']], false], + // type mismatch → allowed + ['alice', 'target', 'file_created', ['alice' => ['file_deleted']], true], + // different user → allowed + ['bob', 'target', 'file_created', ['alice' => ['file_created']], true], + // empty config → allowed + ['alice', 'target', 'file_created', [], true], + // non-array rule → allowed + ['alice', 'target', 'file_created', ['alice' => 'file_created'], true], + ]; + } + + #[DataProvider('dataExcludedAuthor')] + public function testSendWithExcludedAuthor(string $author, string $affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void { + $this->deleteTestActivities(); + + $this->config->method('getSystemValue') + ->with('activity_log_exclude_users', []) + ->willReturn($excludedUsers); + + $event = $this->realActivityManager->generateEvent(); + $event->setApp('test') + ->setType($type) + ->setAuthor($author) + ->setAffectedUser($affectedUser) + ->setSubject('subject'); + + $result = $this->data->send($event); + $this->assertSame($expectedInsert, $result !== 0); + + $qb = $this->dbConnection->getQueryBuilder(); + $row = $qb->select('user', 'affecteduser') + ->from('activity') + ->where($qb->expr()->eq('app', $qb->createNamedParameter('test'))) + ->orderBy('activity_id', 'DESC') + ->executeQuery() + ->fetch(); + + if ($expectedInsert) { + $this->assertEquals(['user' => $author, 'affecteduser' => $affectedUser], $row); + } else { + $this->assertFalse($row); + } + + $this->deleteTestActivities(); + } + + #[DataProvider('dataExcludedAuthor')] + public function testStoreMailWithExcludedAuthor(string $author, string $affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void { + $this->deleteTestMails(); + + $this->config->method('getSystemValue') + ->with('activity_log_exclude_users', []) + ->willReturn($excludedUsers); + + $time = time(); + $event = $this->realActivityManager->generateEvent(); + $event->setApp('test') + ->setType($type) + ->setAuthor($author) + ->setAffectedUser($affectedUser) + ->setSubject('subject') + ->setTimestamp($time); + + $this->assertSame($expectedInsert, $this->data->storeMail($event, $time + 10)); + + $qb = $this->dbConnection->getQueryBuilder(); + $row = $qb->select('amq_latest_send', 'amq_affecteduser') + ->from('activity_mq') + ->where($qb->expr()->eq('amq_appid', $qb->createNamedParameter('test'))) + ->orderBy('mail_id', 'DESC') + ->executeQuery() + ->fetch(); + + if ($expectedInsert) { + $this->assertEquals(['amq_latest_send' => $time + 10, 'amq_affecteduser' => $affectedUser], $row); + } else { + $this->assertFalse($row); + } + + $this->deleteTestMails(); + } + + #[DataProvider('dataExcludedAuthor')] + public function testBulkSendWithExcludedAuthor(string $author, string $_affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void { + $this->deleteTestActivities(); + + $this->config->method('getSystemValue') + ->with('activity_log_exclude_users', []) + ->willReturn($excludedUsers); + + $event = $this->realActivityManager->generateEvent(); + $event->setApp('test') + ->setType($type) + ->setAuthor($author) + ->setSubject('subject') + ->setTimestamp(time()); + + $bulkUsers = ['user1', 'user2']; + $result = $this->data->bulkSend($event, $bulkUsers); + + if ($expectedInsert) { + $this->assertCount(2, $result); + $this->assertEqualsCanonicalizing($bulkUsers, array_values($result)); + } else { + $this->assertEmpty($result); + } + + $qb = $this->dbConnection->getQueryBuilder(); + $count = (int)$qb->select($qb->func()->count('activity_id', 'count')) + ->from('activity') + ->where($qb->expr()->eq('app', $qb->createNamedParameter('test'))) + ->executeQuery() + ->fetch()['count']; + + $this->assertSame($expectedInsert ? 2 : 0, $count); + + $this->deleteTestActivities(); + } + /** * Delete all testing activities */ From dea9886782f770c9889d04e8fe939cf2c6321b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Bari?= Date: Thu, 25 Jun 2026 13:37:13 +0200 Subject: [PATCH 5/5] fix: Removing dead code from condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tamás Bari --- lib/Data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Data.php b/lib/Data.php index fb44f686a..357c2d857 100644 --- a/lib/Data.php +++ b/lib/Data.php @@ -61,7 +61,7 @@ private function isExcludedAuthor(IEvent $event): bool { return false; } $author = $event->getAuthor(); - if ($author === null || $author === '' || !isset($excludedUsers[$author])) { + if ($author === '' || !isset($excludedUsers[$author])) { return false; } $rule = $excludedUsers[$author];