Skip to content

Commit 6a3a244

Browse files
committed
feat(activity): add bulk activity option
Signed-off-by: Anna Larch <anna@nextcloud.com>
1 parent 055b5dd commit 6a3a244

8 files changed

Lines changed: 91 additions & 8 deletions

File tree

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'OCP\\Activity\\Exceptions\\InvalidValueException' => $baseDir . '/lib/public/Activity/Exceptions/InvalidValueException.php',
5050
'OCP\\Activity\\Exceptions\\SettingNotFoundException' => $baseDir . '/lib/public/Activity/Exceptions/SettingNotFoundException.php',
5151
'OCP\\Activity\\Exceptions\\UnknownActivityException' => $baseDir . '/lib/public/Activity/Exceptions/UnknownActivityException.php',
52+
'OCP\\Activity\\IBulkConsumer' => $baseDir . '/lib/public/Activity/IBulkConsumer.php',
5253
'OCP\\Activity\\IConsumer' => $baseDir . '/lib/public/Activity/IConsumer.php',
5354
'OCP\\Activity\\IEvent' => $baseDir . '/lib/public/Activity/IEvent.php',
5455
'OCP\\Activity\\IEventMerger' => $baseDir . '/lib/public/Activity/IEventMerger.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
9090
'OCP\\Activity\\Exceptions\\InvalidValueException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/InvalidValueException.php',
9191
'OCP\\Activity\\Exceptions\\SettingNotFoundException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/SettingNotFoundException.php',
9292
'OCP\\Activity\\Exceptions\\UnknownActivityException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/UnknownActivityException.php',
93+
'OCP\\Activity\\IBulkConsumer' => __DIR__ . '/../../..' . '/lib/public/Activity/IBulkConsumer.php',
9394
'OCP\\Activity\\IConsumer' => __DIR__ . '/../../..' . '/lib/public/Activity/IConsumer.php',
9495
'OCP\\Activity\\IEvent' => __DIR__ . '/../../..' . '/lib/public/Activity/IEvent.php',
9596
'OCP\\Activity\\IEventMerger' => __DIR__ . '/../../..' . '/lib/public/Activity/IEventMerger.php',

lib/private/Activity/Event.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,6 @@ protected function isValidCommon(): bool {
450450
return
451451
$this->getApp() !== ''
452452
&& $this->getType() !== ''
453-
&& $this->getAffectedUser() !== ''
454453
&& $this->getTimestamp() !== 0
455454
/**
456455
* Disabled for BC with old activities

lib/private/Activity/Manager.php

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
use OCP\Activity\Exceptions\FilterNotFoundException;
1212
use OCP\Activity\Exceptions\IncompleteActivityException;
1313
use OCP\Activity\Exceptions\SettingNotFoundException;
14+
use OCP\Activity\IBulkConsumer;
1415
use OCP\Activity\IConsumer;
1516
use OCP\Activity\IEvent;
1617
use OCP\Activity\IFilter;
1718
use OCP\Activity\IManager;
1819
use OCP\Activity\IProvider;
1920
use OCP\Activity\ISetting;
21+
use OCP\AppFramework\Utility\ITimeFactory;
2022
use OCP\IConfig;
2123
use OCP\IL10N;
2224
use OCP\IRequest;
@@ -46,6 +48,7 @@ public function __construct(
4648
protected IValidator $validator,
4749
protected IRichTextFormatter $richTextFormatter,
4850
protected IL10N $l10n,
51+
protected ITimeFactory $timeFactory,
4952
) {
5053
}
5154

@@ -96,25 +99,57 @@ public function generateEvent(): IEvent {
9699
* {@inheritDoc}
97100
*/
98101
public function publish(IEvent $event): void {
102+
if ($event->getAuthor() === '' && $this->session->getUser() instanceof IUser) {
103+
$event->setAuthor($this->session->getUser()->getUID());
104+
}
105+
106+
if (!$event->getTimestamp()) {
107+
$event->setTimestamp($this->timeFactory->getTime());
108+
}
109+
110+
if ($event->getAffectedUser() === '' || !$event->isValid()) {
111+
throw new IncompleteActivityException('The given event is invalid');
112+
}
113+
114+
foreach ($this->getConsumers() as $c) {
115+
$c->receive($event);
116+
}
117+
}
118+
119+
/**
120+
* {@inheritDoc}
121+
*/
122+
public function bulkPublish(IEvent $event, array $affectedUserIds, ISetting $setting): void {
123+
if (empty($affectedUserIds)) {
124+
throw new IncompleteActivityException('The given event is invalid');
125+
}
126+
99127
if ($event->getAuthor() === '') {
100128
if ($this->session->getUser() instanceof IUser) {
101129
$event->setAuthor($this->session->getUser()->getUID());
102130
}
103131
}
104132

105133
if (!$event->getTimestamp()) {
106-
$event->setTimestamp(time());
134+
$event->setTimestamp($this->timeFactory->getTime());
107135
}
108136

109137
if (!$event->isValid()) {
110138
throw new IncompleteActivityException('The given event is invalid');
111139
}
112140

113141
foreach ($this->getConsumers() as $c) {
114-
$c->receive($event);
142+
if ($c instanceof IBulkConsumer) {
143+
$c->bulkReceive($event, $affectedUserIds, $setting);
144+
}
145+
foreach ($affectedUserIds as $affectedUserId) {
146+
$event->setAffectedUser($affectedUserId);
147+
$c->receive($event);
148+
}
115149
}
116150
}
117151

152+
118153
/**
119154
* In order to improve lazy loading a closure can be registered which will be called in case
120155
* activity consumers are actually requested

lib/private/Server.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,8 @@ public function __construct($webRoot, \OC\Config $config) {
657657
$c->get(\OCP\IConfig::class),
658658
$c->get(IValidator::class),
659659
$c->get(IRichTextFormatter::class),
660-
$l10n
660+
$l10n,
661+
$c->get(ITimeFactory::class),
661662
);
662663
});
663664

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCP\Activity;
9+
10+
/**
11+
* Interface IBulkConsumer
12+
*
13+
* @since 32.0.0
14+
*/
15+
interface IBulkConsumer extends IConsumer {
16+
/**
17+
* @param IEvent $event
18+
* @param array $affectedUserIds
19+
* @param ISetting $setting
20+
* @return void
21+
* @since 32.0.0
22+
*/
23+
public function bulkReceive(IEvent $event, array $affectedUserIds, ISetting $setting): void;
24+
}

lib/public/Activity/IManager.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ public function generateEvent(): IEvent;
5151
*/
5252
public function publish(IEvent $event): void;
5353

54+
/**
55+
* Bulk publish an event for multiple users
56+
* taking into account the app specific activity settings
57+
*
58+
* Make sure to call at least the following methods before sending an Event:
59+
* - setApp()
60+
* - setType()
61+
*
62+
* @param IEvent $event
63+
* @throws IncompleteActivityException if required values have not been set
64+
* @since 32.0.0
65+
*/
66+
public function bulkPublish(IEvent $event, array $affectedUserIds, ISetting $setting): void;
67+
5468
/**
5569
* In order to improve lazy loading a closure can be registered which will be called in case
5670
* activity consumers are actually requested

tests/lib/Activity/ManagerTest.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use OCP\Activity\Exceptions\IncompleteActivityException;
1212
use OCP\Activity\IConsumer;
1313
use OCP\Activity\IEvent;
14+
use OCP\AppFramework\Utility\ITimeFactory;
1415
use OCP\IConfig;
1516
use OCP\IL10N;
1617
use OCP\IRequest;
@@ -30,6 +31,7 @@ class ManagerTest extends TestCase {
3031
protected IConfig&MockObject $config;
3132
protected IValidator&MockObject $validator;
3233
protected IRichTextFormatter&MockObject $richTextFormatter;
34+
private ITimeFactory&MockObject $time;
3335

3436
protected function setUp(): void {
3537
parent::setUp();
@@ -39,14 +41,16 @@ protected function setUp(): void {
3941
$this->config = $this->createMock(IConfig::class);
4042
$this->validator = $this->createMock(IValidator::class);
4143
$this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
44+
$this->time = $this->createMock(ITimeFactory::class);
4245

4346
$this->activityManager = new \OC\Activity\Manager(
4447
$this->request,
4548
$this->session,
4649
$this->config,
4750
$this->validator,
4851
$this->richTextFormatter,
49-
$this->createMock(IL10N::class)
52+
$this->createMock(IL10N::class),
53+
$this->time,
5054
);
5155

5256
$this->assertSame([], self::invokePrivate($this->activityManager, 'getConsumers'));
@@ -217,6 +221,11 @@ public function testPublish($author, $expected): void {
217221
->willReturn($authorObject);
218222
}
219223

224+
$time = time();
225+
$this->time
226+
->method('getTime')
227+
->willReturn($time);
228+
220229
$event = $this->activityManager->generateEvent();
221230
$event->setApp('test')
222231
->setType('test_type')
@@ -230,9 +239,8 @@ public function testPublish($author, $expected): void {
230239
$consumer->expects($this->once())
231240
->method('receive')
232241
->with($event)
233-
->willReturnCallback(function (IEvent $event) use ($expected): void {
234-
$this->assertLessThanOrEqual(time() + 2, $event->getTimestamp(), 'Timestamp not set correctly');
235-
$this->assertGreaterThanOrEqual(time() - 2, $event->getTimestamp(), 'Timestamp not set correctly');
242+
->willReturnCallback(function (IEvent $event) use ($expected, $time): void {
243+
$this->assertEquals($time, $event->getTimestamp(), 'Timestamp not set correctly');
236244
$this->assertSame($expected, $event->getAuthor(), 'Author name not set correctly');
237245
});
238246
$this->activityManager->registerConsumer(function () use ($consumer) {

0 commit comments

Comments
 (0)