Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
'OCP\\Activity\\Exceptions\\InvalidValueException' => $baseDir . '/lib/public/Activity/Exceptions/InvalidValueException.php',
'OCP\\Activity\\Exceptions\\SettingNotFoundException' => $baseDir . '/lib/public/Activity/Exceptions/SettingNotFoundException.php',
'OCP\\Activity\\Exceptions\\UnknownActivityException' => $baseDir . '/lib/public/Activity/Exceptions/UnknownActivityException.php',
'OCP\\Activity\\IBulkConsumer' => $baseDir . '/lib/public/Activity/IBulkConsumer.php',
'OCP\\Activity\\IConsumer' => $baseDir . '/lib/public/Activity/IConsumer.php',
'OCP\\Activity\\IEvent' => $baseDir . '/lib/public/Activity/IEvent.php',
'OCP\\Activity\\IEventMerger' => $baseDir . '/lib/public/Activity/IEventMerger.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Activity\\Exceptions\\InvalidValueException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/InvalidValueException.php',
'OCP\\Activity\\Exceptions\\SettingNotFoundException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/SettingNotFoundException.php',
'OCP\\Activity\\Exceptions\\UnknownActivityException' => __DIR__ . '/../../..' . '/lib/public/Activity/Exceptions/UnknownActivityException.php',
'OCP\\Activity\\IBulkConsumer' => __DIR__ . '/../../..' . '/lib/public/Activity/IBulkConsumer.php',
'OCP\\Activity\\IConsumer' => __DIR__ . '/../../..' . '/lib/public/Activity/IConsumer.php',
'OCP\\Activity\\IEvent' => __DIR__ . '/../../..' . '/lib/public/Activity/IEvent.php',
'OCP\\Activity\\IEventMerger' => __DIR__ . '/../../..' . '/lib/public/Activity/IEventMerger.php',
Expand Down
1 change: 0 additions & 1 deletion lib/private/Activity/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,6 @@ protected function isValidCommon(): bool {
return
$this->getApp() !== ''
&& $this->getType() !== ''
&& $this->getAffectedUser() !== ''
&& $this->getTimestamp() !== 0
/**
* Disabled for BC with old activities
Expand Down
39 changes: 37 additions & 2 deletions lib/private/Activity/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
use OCP\Activity\Exceptions\FilterNotFoundException;
use OCP\Activity\Exceptions\IncompleteActivityException;
use OCP\Activity\Exceptions\SettingNotFoundException;
use OCP\Activity\IBulkConsumer;
use OCP\Activity\IConsumer;
use OCP\Activity\IEvent;
use OCP\Activity\IFilter;
use OCP\Activity\IManager;
use OCP\Activity\IProvider;
use OCP\Activity\ISetting;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
Expand Down Expand Up @@ -46,6 +48,7 @@ public function __construct(
protected IValidator $validator,
protected IRichTextFormatter $richTextFormatter,
protected IL10N $l10n,
protected ITimeFactory $timeFactory,
) {
}

Expand Down Expand Up @@ -96,25 +99,57 @@ public function generateEvent(): IEvent {
* {@inheritDoc}
*/
public function publish(IEvent $event): void {
if ($event->getAuthor() === '' && $this->session->getUser() instanceof IUser) {
$event->setAuthor($this->session->getUser()->getUID());
}

if (!$event->getTimestamp()) {
$event->setTimestamp($this->timeFactory->getTime());
}

if ($event->getAffectedUser() === '' || !$event->isValid()) {
throw new IncompleteActivityException('The given event is invalid');
}

foreach ($this->getConsumers() as $c) {
$c->receive($event);
}
}

/**
* {@inheritDoc}
*/
public function bulkPublish(IEvent $event, array $affectedUserIds, ISetting $setting): void {
if (empty($affectedUserIds)) {
throw new IncompleteActivityException('The given event is invalid');
}

if ($event->getAuthor() === '') {
if ($this->session->getUser() instanceof IUser) {
$event->setAuthor($this->session->getUser()->getUID());
}
}

if (!$event->getTimestamp()) {
$event->setTimestamp(time());
$event->setTimestamp($this->timeFactory->getTime());
}

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

foreach ($this->getConsumers() as $c) {
$c->receive($event);
if ($c instanceof IBulkConsumer) {
$c->bulkReceive($event, $affectedUserIds, $setting);
}
foreach ($affectedUserIds as $affectedUserId) {
$event->setAffectedUser($affectedUserId);
$c->receive($event);
}
}
}


/**
* In order to improve lazy loading a closure can be registered which will be called in case
* activity consumers are actually requested
Expand Down
3 changes: 2 additions & 1 deletion lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,8 @@ public function __construct($webRoot, \OC\Config $config) {
$c->get(\OCP\IConfig::class),
$c->get(IValidator::class),
$c->get(IRichTextFormatter::class),
$l10n
$l10n,
$c->get(ITimeFactory::class),
);
});

Expand Down
24 changes: 24 additions & 0 deletions lib/public/Activity/IBulkConsumer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCP\Activity;

/**
* Interface IBulkConsumer
*
* @since 32.0.0
*/
interface IBulkConsumer extends IConsumer {
/**
* @param IEvent $event
* @param array $affectedUserIds
* @param ISetting $setting
* @return void
* @since 32.0.0
*/
public function bulkReceive(IEvent $event, array $affectedUserIds, ISetting $setting): void;
}
14 changes: 14 additions & 0 deletions lib/public/Activity/IManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ public function generateEvent(): IEvent;
*/
public function publish(IEvent $event): void;

/**
* Bulk publish an event for multiple users
* taking into account the app specific activity settings
*
* Make sure to call at least the following methods before sending an Event:
* - setApp()
* - setType()
*
* @param IEvent $event
* @throws IncompleteActivityException if required values have not been set
* @since 32.0.0
*/
public function bulkPublish(IEvent $event, array $affectedUserIds, ISetting $setting): void;

/**
* In order to improve lazy loading a closure can be registered which will be called in case
* activity consumers are actually requested
Expand Down
16 changes: 12 additions & 4 deletions tests/lib/Activity/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use OCP\Activity\Exceptions\IncompleteActivityException;
use OCP\Activity\IConsumer;
use OCP\Activity\IEvent;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
Expand All @@ -30,6 +31,7 @@ class ManagerTest extends TestCase {
protected IConfig&MockObject $config;
protected IValidator&MockObject $validator;
protected IRichTextFormatter&MockObject $richTextFormatter;
private ITimeFactory&MockObject $time;

protected function setUp(): void {
parent::setUp();
Expand All @@ -39,14 +41,16 @@ protected function setUp(): void {
$this->config = $this->createMock(IConfig::class);
$this->validator = $this->createMock(IValidator::class);
$this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
$this->time = $this->createMock(ITimeFactory::class);

$this->activityManager = new \OC\Activity\Manager(
$this->request,
$this->session,
$this->config,
$this->validator,
$this->richTextFormatter,
$this->createMock(IL10N::class)
$this->createMock(IL10N::class),
$this->time,
);

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

$time = time();
$this->time
->method('getTime')
->willReturn($time);

$event = $this->activityManager->generateEvent();
$event->setApp('test')
->setType('test_type')
Expand All @@ -230,9 +239,8 @@ public function testPublish($author, $expected): void {
$consumer->expects($this->once())
->method('receive')
->with($event)
->willReturnCallback(function (IEvent $event) use ($expected): void {
$this->assertLessThanOrEqual(time() + 2, $event->getTimestamp(), 'Timestamp not set correctly');
$this->assertGreaterThanOrEqual(time() - 2, $event->getTimestamp(), 'Timestamp not set correctly');
->willReturnCallback(function (IEvent $event) use ($expected, $time): void {
$this->assertEquals($time, $event->getTimestamp(), 'Timestamp not set correctly');
$this->assertSame($expected, $event->getAuthor(), 'Author name not set correctly');
});
$this->activityManager->registerConsumer(function () use ($consumer) {
Expand Down
Loading