Skip to content

Commit 702c3b1

Browse files
authored
Merge pull request #2626 from nextcloud/feat/excluded-users
feat: adding an option to exclude users from the acitivity log
2 parents 71e6871 + b664a91 commit 702c3b1

2 files changed

Lines changed: 167 additions & 4 deletions

File tree

lib/Data.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,51 @@ public function __construct(
3939
) {
4040
}
4141

42+
/**
43+
* Check if the event should be processed (not excluded and has valid target)
44+
*
45+
* @param IEvent $event
46+
* @return bool
47+
*/
48+
private function shouldSend(IEvent $event): bool {
49+
return $event->getAffectedUser() !== '' && !$this->isExcludedAuthor($event);
50+
}
51+
52+
/**
53+
* Check if the event's author is excluded from activity logging
54+
*
55+
* @param IEvent $event
56+
* @return bool
57+
*/
58+
private function isExcludedAuthor(IEvent $event): bool {
59+
$excludedUsers = $this->config->getSystemValue('activity_log_exclude_users', []);
60+
if (empty($excludedUsers)) {
61+
return false;
62+
}
63+
$author = $event->getAuthor();
64+
if ($author === '' || !isset($excludedUsers[$author])) {
65+
return false;
66+
}
67+
$rule = $excludedUsers[$author];
68+
if (is_array($rule)) {
69+
return in_array($event->getType(), $rule, true);
70+
} else {
71+
$this->logger->warning(
72+
'activity_log_exclude_users rule for user "{user}" is not an array, skipping!',
73+
['app' => 'activity', 'user' => $author]
74+
);
75+
}
76+
return false;
77+
}
78+
4279
/**
4380
* Send an event into the activity stream
4481
*
4582
* @param IEvent $event
4683
* @return int
4784
*/
4885
public function send(IEvent $event): int {
49-
if ($event->getAffectedUser() === '') {
86+
if (!$this->shouldSend($event)) {
5087
return 0;
5188
}
5289

@@ -104,6 +141,10 @@ public function send(IEvent $event): int {
104141
* @throws Exception
105142
*/
106143
public function bulkSend(IEvent $event, array $affectedUsers): array {
144+
if ($this->isExcludedAuthor($event)) {
145+
return [];
146+
}
147+
107148
$this->connection->beginTransaction();
108149

109150
$activityIds = [];
@@ -170,8 +211,7 @@ public function bulkSend(IEvent $event, array $affectedUsers): array {
170211
* @return bool
171212
*/
172213
public function storeMail(IEvent $event, int $latestSendTime): bool {
173-
$affectedUser = $event->getAffectedUser();
174-
if ($affectedUser === '') {
214+
if (!$this->shouldSend($event)) {
175215
return false;
176216
}
177217

@@ -195,7 +235,7 @@ public function storeMail(IEvent $event, int $latestSendTime): bool {
195235
'amq_appid' => $event->getApp(),
196236
'amq_subject' => $event->getSubject(),
197237
'amq_subjectparams' => json_encode($event->getSubjectParameters()),
198-
'amq_affecteduser' => $affectedUser,
238+
'amq_affecteduser' => $event->getAffectedUser(),
199239
'amq_timestamp' => $event->getTimestamp(),
200240
'amq_type' => $event->getType(),
201241
'amq_latest_send' => $latestSendTime,

tests/DataTest.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,129 @@ public function testDeleteAffectedUserActivities(): void {
347347
$this->deleteTestActivities();
348348
}
349349

350+
public static function dataExcludedAuthor(): array {
351+
return [
352+
// author+type match → blocked
353+
['alice', 'target', 'file_created', ['alice' => ['file_created']], false],
354+
// type mismatch → allowed
355+
['alice', 'target', 'file_created', ['alice' => ['file_deleted']], true],
356+
// different user → allowed
357+
['bob', 'target', 'file_created', ['alice' => ['file_created']], true],
358+
// empty config → allowed
359+
['alice', 'target', 'file_created', [], true],
360+
// non-array rule → allowed
361+
['alice', 'target', 'file_created', ['alice' => 'file_created'], true],
362+
];
363+
}
364+
365+
#[DataProvider('dataExcludedAuthor')]
366+
public function testSendWithExcludedAuthor(string $author, string $affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void {
367+
$this->deleteTestActivities();
368+
369+
$this->config->method('getSystemValue')
370+
->with('activity_log_exclude_users', [])
371+
->willReturn($excludedUsers);
372+
373+
$event = $this->realActivityManager->generateEvent();
374+
$event->setApp('test')
375+
->setType($type)
376+
->setAuthor($author)
377+
->setAffectedUser($affectedUser)
378+
->setSubject('subject');
379+
380+
$result = $this->data->send($event);
381+
$this->assertSame($expectedInsert, $result !== 0);
382+
383+
$qb = $this->dbConnection->getQueryBuilder();
384+
$row = $qb->select('user', 'affecteduser')
385+
->from('activity')
386+
->where($qb->expr()->eq('app', $qb->createNamedParameter('test')))
387+
->orderBy('activity_id', 'DESC')
388+
->executeQuery()
389+
->fetch();
390+
391+
if ($expectedInsert) {
392+
$this->assertEquals(['user' => $author, 'affecteduser' => $affectedUser], $row);
393+
} else {
394+
$this->assertFalse($row);
395+
}
396+
397+
$this->deleteTestActivities();
398+
}
399+
400+
#[DataProvider('dataExcludedAuthor')]
401+
public function testStoreMailWithExcludedAuthor(string $author, string $affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void {
402+
$this->deleteTestMails();
403+
404+
$this->config->method('getSystemValue')
405+
->with('activity_log_exclude_users', [])
406+
->willReturn($excludedUsers);
407+
408+
$time = time();
409+
$event = $this->realActivityManager->generateEvent();
410+
$event->setApp('test')
411+
->setType($type)
412+
->setAuthor($author)
413+
->setAffectedUser($affectedUser)
414+
->setSubject('subject')
415+
->setTimestamp($time);
416+
417+
$this->assertSame($expectedInsert, $this->data->storeMail($event, $time + 10));
418+
419+
$qb = $this->dbConnection->getQueryBuilder();
420+
$row = $qb->select('amq_latest_send', 'amq_affecteduser')
421+
->from('activity_mq')
422+
->where($qb->expr()->eq('amq_appid', $qb->createNamedParameter('test')))
423+
->orderBy('mail_id', 'DESC')
424+
->executeQuery()
425+
->fetch();
426+
427+
if ($expectedInsert) {
428+
$this->assertEquals(['amq_latest_send' => $time + 10, 'amq_affecteduser' => $affectedUser], $row);
429+
} else {
430+
$this->assertFalse($row);
431+
}
432+
433+
$this->deleteTestMails();
434+
}
435+
436+
#[DataProvider('dataExcludedAuthor')]
437+
public function testBulkSendWithExcludedAuthor(string $author, string $_affectedUser, string $type, array $excludedUsers, bool $expectedInsert): void {
438+
$this->deleteTestActivities();
439+
440+
$this->config->method('getSystemValue')
441+
->with('activity_log_exclude_users', [])
442+
->willReturn($excludedUsers);
443+
444+
$event = $this->realActivityManager->generateEvent();
445+
$event->setApp('test')
446+
->setType($type)
447+
->setAuthor($author)
448+
->setSubject('subject')
449+
->setTimestamp(time());
450+
451+
$bulkUsers = ['user1', 'user2'];
452+
$result = $this->data->bulkSend($event, $bulkUsers);
453+
454+
if ($expectedInsert) {
455+
$this->assertCount(2, $result);
456+
$this->assertEqualsCanonicalizing($bulkUsers, array_values($result));
457+
} else {
458+
$this->assertEmpty($result);
459+
}
460+
461+
$qb = $this->dbConnection->getQueryBuilder();
462+
$count = (int)$qb->select($qb->func()->count('activity_id', 'count'))
463+
->from('activity')
464+
->where($qb->expr()->eq('app', $qb->createNamedParameter('test')))
465+
->executeQuery()
466+
->fetch()['count'];
467+
468+
$this->assertSame($expectedInsert ? 2 : 0, $count);
469+
470+
$this->deleteTestActivities();
471+
}
472+
350473
/**
351474
* Delete all testing activities
352475
*/

0 commit comments

Comments
 (0)