refactor(comment): Port away from deprecated event comment constant

Create new events to replace deprecated CommentsEvent constant and use
them when creating CommentsEvents.

On the listener side, we can't yet use these events as deck still send
the old events.

Also fixes some issues reported by psalm level 3 on the comment app.

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
pull/56076/head
Carl Schwan 2025-10-29 10:44:33 +07:00
parent 86f0cbf1e8
commit 3c9b937e28
No known key found for this signature in database
GPG Key ID: 02325448204E452A
16 changed files with 217 additions and 110 deletions

@ -18,8 +18,6 @@ use OCP\IUserManager;
use OCP\L10N\IFactory;
class Provider implements IProvider {
protected ?IL10N $l = null;
public function __construct(
protected IFactory $languageFactory,
protected IURLGenerator $url,
@ -42,9 +40,9 @@ class Provider implements IProvider {
throw new UnknownActivityException();
}
$this->l = $this->languageFactory->get('comments', $language);
if ($event->getSubject() === 'add_comment_subject') {
$l = $this->languageFactory->get('comments', $language);
$this->parseMessage($event);
if ($this->activityManager->getRequirePNG()) {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.png')));
@ -54,13 +52,13 @@ class Provider implements IProvider {
if ($this->activityManager->isFormattingFilteredObject()) {
try {
return $this->parseShortVersion($event);
return $this->parseShortVersion($event, $l);
} catch (UnknownActivityException) {
// Ignore and simply use the long version...
}
}
return $this->parseLongVersion($event);
return $this->parseLongVersion($event, $l);
}
throw new UnknownActivityException();
@ -69,15 +67,15 @@ class Provider implements IProvider {
/**
* @throws UnknownActivityException
*/
protected function parseShortVersion(IEvent $event): IEvent {
protected function parseShortVersion(IEvent $event, IL10N $l): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setRichSubject($this->l->t('You commented'), []);
$event->setRichSubject($l->t('You commented'), []);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setRichSubject($this->l->t('{author} commented'), [
$event->setRichSubject($l->t('{author} commented'), [
'author' => $author,
]);
}
@ -91,24 +89,24 @@ class Provider implements IProvider {
/**
* @throws UnknownActivityException
*/
protected function parseLongVersion(IEvent $event): IEvent {
protected function parseLongVersion(IEvent $event, IL10N $l): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setParsedSubject($this->l->t('You commented on %1$s', [
$event->setParsedSubject($l->t('You commented on %1$s', [
$subjectParameters['filePath'],
]))
->setRichSubject($this->l->t('You commented on {file}'), [
->setRichSubject($l->t('You commented on {file}'), [
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setParsedSubject($this->l->t('%1$s commented on %2$s', [
$event->setParsedSubject($l->t('%1$s commented on %2$s', [
$author['name'],
$subjectParameters['filePath'],
]))
->setRichSubject($this->l->t('{author} commented on {file}'), [
->setRichSubject($l->t('{author} commented on {file}'), [
'author' => $author,
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);

@ -11,7 +11,7 @@ use OCP\IL10N;
class Setting extends ActivitySettings {
public function __construct(
protected IL10N $l,
protected readonly IL10N $l,
) {
}
@ -23,11 +23,11 @@ class Setting extends ActivitySettings {
return $this->l->t('<strong>Comments</strong> for files');
}
public function getGroupIdentifier() {
public function getGroupIdentifier(): string {
return 'files';
}
public function getGroupName() {
public function getGroupName(): string {
return $this->l->t('Files');
}

@ -27,6 +27,10 @@ class CommentsEntityEventListener implements IEventListener {
return;
}
if ($this->userId === null) {
return;
}
$event->addEntityCollection('files', function ($name): bool {
$nodes = $this->rootFolder->getUserFolder($this->userId)->getById((int)$name);
return !empty($nodes);

@ -35,8 +35,7 @@ class CommentsEventListener implements IEventListener {
}
$eventType = $event->getEvent();
if ($eventType === CommentsEvent::EVENT_ADD
) {
if ($eventType === CommentsEvent::EVENT_ADD) {
$this->notificationHandler($event);
$this->activityHandler($event);
return;

@ -12,7 +12,7 @@ use OCA\Comments\Activity\Listener;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\App\IAppManager;
use OCP\Comments\CommentsEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\IComment;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\IMountProviderCollection;
@ -66,14 +66,7 @@ class ListenerTest extends TestCase {
->method('getObjectType')
->willReturn('files');
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->any())
->method('getComment')
->willReturn($comment);
$event->expects($this->any())
->method('getEvent')
->willReturn(CommentsEvent::EVENT_ADD);
$event = new CommentAddedEvent($comment);
/** @var IUser|MockObject $ownerUser */
$ownerUser = $this->createMock(IUser::class);

@ -12,6 +12,10 @@ use OCA\Comments\Activity\Listener as ActivityListener;
use OCA\Comments\Listener\CommentsEventListener;
use OCA\Comments\Notification\Listener as NotificationListener;
use OCP\Comments\CommentsEvent;
use OCP\Comments\Events\BeforeCommentUpdatedEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\Events\CommentDeletedEvent;
use OCP\Comments\Events\CommentUpdatedEvent;
use OCP\Comments\IComment;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
@ -50,10 +54,10 @@ class EventHandlerTest extends TestCase {
public static function handledProvider(): array {
return [
[CommentsEvent::EVENT_DELETE],
[CommentsEvent::EVENT_UPDATE],
[CommentsEvent::EVENT_PRE_UPDATE],
[CommentsEvent::EVENT_ADD]
['delete'],
['update'],
['pre_update'],
['add']
];
}
@ -65,14 +69,12 @@ class EventHandlerTest extends TestCase {
->method('getObjectType')
->willReturn('files');
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->atLeastOnce())
->method('getComment')
->willReturn($comment);
$event->expects($this->atLeastOnce())
->method('getEvent')
->willReturn($eventType);
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
$this->notificationListener->expects($this->once())
->method('evaluate')

@ -8,7 +8,10 @@
namespace OCA\Comments\Tests\Unit\Notification;
use OCA\Comments\Notification\Listener;
use OCP\Comments\CommentsEvent;
use OCP\Comments\Events\BeforeCommentUpdatedEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\Events\CommentDeletedEvent;
use OCP\Comments\Events\CommentUpdatedEvent;
use OCP\Comments\IComment;
use OCP\IURLGenerator;
use OCP\IUserManager;
@ -37,10 +40,10 @@ class ListenerTest extends TestCase {
public static function eventProvider(): array {
return [
[CommentsEvent::EVENT_ADD, 'notify'],
[CommentsEvent::EVENT_UPDATE, 'notify'],
[CommentsEvent::EVENT_PRE_UPDATE, 'markProcessed'],
[CommentsEvent::EVENT_DELETE, 'markProcessed']
['add', 'notify'],
['update', 'notify'],
['pre_update', 'markProcessed'],
['delete', 'markProcessed']
];
}
@ -49,7 +52,7 @@ class ListenerTest extends TestCase {
* @param string $notificationMethod
*/
#[\PHPUnit\Framework\Attributes\DataProvider('eventProvider')]
public function testEvaluate($eventType, $notificationMethod): void {
public function testEvaluate(string $eventType, $notificationMethod): void {
/** @var IComment|MockObject $comment */
$comment = $this->createMock(IComment::class);
$comment->expects($this->any())
@ -72,14 +75,12 @@ class ListenerTest extends TestCase {
->method('getId')
->willReturn('1234');
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn($eventType);
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
/** @var INotification|MockObject $notification */
$notification = $this->createMock(INotification::class);
@ -124,14 +125,12 @@ class ListenerTest extends TestCase {
->method('getMentions')
->willReturn([]);
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn($eventType);
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
$this->notificationManager->expects($this->never())
->method('createNotification');
@ -162,14 +161,7 @@ class ListenerTest extends TestCase {
->method('getId')
->willReturn('1234');
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn(CommentsEvent::EVENT_ADD);
$event = new CommentAddedEvent($comment);
/** @var INotification|MockObject $notification */
$notification = $this->createMock(INotification::class);

@ -268,6 +268,10 @@ return array(
'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php',
'OCP\\Comments\\CommentsEvent' => $baseDir . '/lib/public/Comments/CommentsEvent.php',
'OCP\\Comments\\Events\\BeforeCommentUpdatedEvent' => $baseDir . '/lib/public/Comments/Events/BeforeCommentUpdatedEvent.php',
'OCP\\Comments\\Events\\CommentAddedEvent' => $baseDir . '/lib/public/Comments/Events/CommentAddedEvent.php',
'OCP\\Comments\\Events\\CommentDeletedEvent' => $baseDir . '/lib/public/Comments/Events/CommentDeletedEvent.php',
'OCP\\Comments\\Events\\CommentUpdatedEvent' => $baseDir . '/lib/public/Comments/Events/CommentUpdatedEvent.php',
'OCP\\Comments\\IComment' => $baseDir . '/lib/public/Comments/IComment.php',
'OCP\\Comments\\ICommentsEventHandler' => $baseDir . '/lib/public/Comments/ICommentsEventHandler.php',
'OCP\\Comments\\ICommentsManager' => $baseDir . '/lib/public/Comments/ICommentsManager.php',

@ -309,6 +309,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php',
'OCP\\Comments\\CommentsEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEvent.php',
'OCP\\Comments\\Events\\BeforeCommentUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/Events/BeforeCommentUpdatedEvent.php',
'OCP\\Comments\\Events\\CommentAddedEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/Events/CommentAddedEvent.php',
'OCP\\Comments\\Events\\CommentDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/Events/CommentDeletedEvent.php',
'OCP\\Comments\\Events\\CommentUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/Events/CommentUpdatedEvent.php',
'OCP\\Comments\\IComment' => __DIR__ . '/../../..' . '/lib/public/Comments/IComment.php',
'OCP\\Comments\\ICommentsEventHandler' => __DIR__ . '/../../..' . '/lib/public/Comments/ICommentsEventHandler.php',
'OCP\\Comments\\ICommentsManager' => __DIR__ . '/../../..' . '/lib/public/Comments/ICommentsManager.php',

@ -9,6 +9,10 @@ namespace OC\Comments;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\CommentsEvent;
use OCP\Comments\Events\BeforeCommentUpdatedEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\Events\CommentDeletedEvent;
use OCP\Comments\Events\CommentUpdatedEvent;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsEventHandler;
use OCP\Comments\ICommentsManager;
@ -902,7 +906,7 @@ class Manager implements ICommentsManager {
if ($comment->getVerb() === 'reaction_deleted') {
$this->deleteReaction($comment);
}
$this->sendEvent(CommentsEvent::EVENT_DELETE, $comment);
$this->sendEvent(new CommentDeletedEvent($comment));
}
return ($affectedRows > 0);
@ -1147,7 +1151,7 @@ class Manager implements ICommentsManager {
if ($comment->getVerb() === 'reaction') {
$this->addReaction($comment);
}
$this->sendEvent(CommentsEvent::EVENT_ADD, $comment);
$this->sendEvent(new CommentAddedEvent($comment));
}
return $affectedRows > 0;
@ -1233,11 +1237,11 @@ class Manager implements ICommentsManager {
* @return bool
* @throws NotFoundException
*/
protected function update(IComment $comment) {
protected function update(IComment $comment): bool {
// for properly working preUpdate Events we need the old comments as is
// in the DB and overcome caching. Also avoid that outdated information stays.
$this->uncache($comment->getId());
$this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId()));
$this->sendEvent(new BeforeCommentUpdatedEvent($this->get($comment->getId())));
$this->uncache($comment->getId());
$result = $this->updateQuery($comment);
@ -1246,7 +1250,7 @@ class Manager implements ICommentsManager {
$this->deleteReaction($comment);
}
$this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);
$this->sendEvent(new CommentUpdatedEvent($comment));
return $result;
}
@ -1528,14 +1532,10 @@ class Manager implements ICommentsManager {
}
/**
* sends notifications to the registered entities
*
* @param $eventType
* @param IComment $comment
* Sends notifications to the registered entities
*/
private function sendEvent($eventType, IComment $comment) {
private function sendEvent(CommentsEvent $event): void {
$entities = $this->getEventHandlers();
$event = new CommentsEvent($eventType, $comment);
foreach ($entities as $entity) {
$entity->handle($event);
}

@ -7,6 +7,7 @@
*/
namespace OCP\Comments;
use OCP\AppFramework\Attribute\Consumable;
use OCP\EventDispatcher\Event;
/**
@ -15,6 +16,7 @@ use OCP\EventDispatcher\Event;
* @since 9.1.0
* @since 28.0.0 Dispatched as a typed event
*/
#[Consumable(since: '9.1.0')]
class CommentsEntityEvent extends Event {
/**
* @since 9.1.0
@ -22,8 +24,8 @@ class CommentsEntityEvent extends Event {
*/
public const EVENT_ENTITY = 'OCP\Comments\ICommentsManager::registerEntity';
/** @var \Closure[] */
protected $collections;
/** @var (\Closure(string $id): bool)[] */
protected array $collections = [];
/**
* DispatcherEvent constructor.
@ -32,19 +34,18 @@ class CommentsEntityEvent extends Event {
*/
public function __construct() {
parent::__construct();
$this->collections = [];
}
/**
* @param string $name
* @param \Closure $entityExistsFunction The closure should take one
* @param \Closure(string $id):bool $entityExistsFunction The closure should take one
* argument, which is the id of the entity, that comments
* should be handled for. The return should then be bool,
* depending on whether comments are allowed (true) or not.
* @throws \OutOfBoundsException when the entity name is already taken
* @since 9.1.0
*/
public function addEntityCollection($name, \Closure $entityExistsFunction) {
public function addEntityCollection(string $name, \Closure $entityExistsFunction): void {
if (isset($this->collections[$name])) {
throw new \OutOfBoundsException('Duplicate entity name "' . $name . '"');
}
@ -53,10 +54,10 @@ class CommentsEntityEvent extends Event {
}
/**
* @return \Closure[]
* @return (\Closure(string $id): bool)[]
* @since 9.1.0
*/
public function getEntityCollections() {
public function getEntityCollections(): array {
return $this->collections;
}
}

@ -7,68 +7,66 @@
*/
namespace OCP\Comments;
use OCP\AppFramework\Attribute\Consumable;
use OCP\EventDispatcher\Event;
/**
* Class CommentsEvent
*
* @since 9.0.0
*
* In the future, once the deprecated methods are removed, this class will be abstract.
*/
#[Consumable(since: '9.0.0')]
class CommentsEvent extends Event {
/**
* @since 11.0.0
* @deprecated 22.0.0
* @deprecated 33.0.0 Use \OCP\Comments\Events\CommentAddedEvent instead.
*/
public const EVENT_ADD = 'OCP\Comments\ICommentsManager::addComment';
/**
* @since 11.0.0
* @deprecated 22.0.0
* @deprecated 33.0.0 Use \OCP\Comments\Events\BeforeCommentUpdatedEvent instead.
*/
public const EVENT_PRE_UPDATE = 'OCP\Comments\ICommentsManager::preUpdateComment';
/**
* @since 11.0.0
* @deprecated 22.0.0
* @deprecated 33.0.0 Use \OCP\Comments\Events\CommentUpdatedEvent instead.
*/
public const EVENT_UPDATE = 'OCP\Comments\ICommentsManager::updateComment';
/**
* @since 11.0.0
* @deprecated 22.0.0
* @deprecated 33.0.0 Use \OCP\Comments\Events\CommentDeletedEvent instead.
*/
public const EVENT_DELETE = 'OCP\Comments\ICommentsManager::deleteComment';
/** @var string */
protected $event;
/** @var IComment */
protected $comment;
/**
* DispatcherEvent constructor.
* CommentsEvent constructor.
*
* @param string $event
* @param IComment $comment
* @since 9.0.0
*/
public function __construct($event, IComment $comment) {
$this->event = $event;
$this->comment = $comment;
public function __construct(
protected readonly string $event,
protected readonly IComment $comment,
) {
parent::__construct();
}
/**
* @return string
* @since 9.0.0
* @depreacted Since 33.0.0 use instanceof CommentAddedEvent, CommentRemovedEvent, CommentUpdatedEvent or BeforeCommentUpdatedEvent instead.
*/
public function getEvent() {
public function getEvent(): string {
return $this->event;
}
/**
* @return IComment
* @since 9.0.0
*/
public function getComment() {
public function getComment(): IComment {
return $this->comment;
}
}

@ -0,0 +1,28 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCP\Comments\Events;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
/**
* Class BeforeCommentAddedEvent
*
* @since 33.0.0
*/
#[Consumable(since: '33.0.0')]
final class BeforeCommentUpdatedEvent extends CommentsEvent {
/**
* CommentEvent constructor.
*/
public function __construct(IComment $comment) {
/** @psalm-suppress DeprecatedConstant */
parent::__construct(CommentsEvent::EVENT_PRE_UPDATE, $comment);
}
}

@ -0,0 +1,28 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCP\Comments\Events;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
/**
* Class CommentAddedEvent
*
* @since 33.0.0
*/
#[Consumable(since: '33.0.0')]
final class CommentAddedEvent extends CommentsEvent {
/**
* CommentAddedEvent constructor.
*/
public function __construct(IComment $comment) {
/** @psalm-suppress DeprecatedConstant */
parent::__construct(CommentsEvent::EVENT_ADD, $comment);
}
}

@ -0,0 +1,28 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCP\Comments\Events;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
/**
* Class CommentRemovedEvent
*
* @since 33.0.0
*/
#[Consumable(since: '33.0.0')]
final class CommentDeletedEvent extends CommentsEvent {
/**
* CommentRemovedEvent constructor.
*/
public function __construct(IComment $comment) {
/** @psalm-suppress DeprecatedConstant */
parent::__construct(CommentsEvent::EVENT_DELETE, $comment);
}
}

@ -0,0 +1,28 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCP\Comments\Events;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
/**
* Class CommentUpdatedEvent
*
* @since 33.0.0
*/
#[Consumable(since: '33.0.0')]
final class CommentUpdatedEvent extends CommentsEvent {
/**
* CommentUpdatedEvent constructor.
*/
public function __construct(IComment $comment) {
/** @psalm-suppress DeprecatedConstant */
parent::__construct(CommentsEvent::EVENT_UPDATE, $comment);
}
}