diff --git a/apps/workflowengine/appinfo/info.xml b/apps/workflowengine/appinfo/info.xml
index c6fce1d533439..ff32c274991b4 100644
--- a/apps/workflowengine/appinfo/info.xml
+++ b/apps/workflowengine/appinfo/info.xml
@@ -9,7 +9,7 @@
Nextcloud workflow engine
Nextcloud workflow engine
Nextcloud workflow engine
- 2.16.0
+ 2.16.1
agpl
Arthur Schiwon
Julius Härtl
diff --git a/apps/workflowengine/composer/composer/autoload_classmap.php b/apps/workflowengine/composer/composer/autoload_classmap.php
index 0fd869905d4ee..de872ff9fa0a6 100644
--- a/apps/workflowengine/composer/composer/autoload_classmap.php
+++ b/apps/workflowengine/composer/composer/autoload_classmap.php
@@ -33,6 +33,7 @@
'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => $baseDir . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php',
'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => $baseDir . '/../lib/Migration/Version2000Date20190808074233.php',
'OCA\\WorkflowEngine\\Migration\\Version2200Date20210805101925' => $baseDir . '/../lib/Migration/Version2200Date20210805101925.php',
+ 'OCA\\WorkflowEngine\\Migration\\Version3400Date20260227000000' => $baseDir . '/../lib/Migration/Version3400Date20260227000000.php',
'OCA\\WorkflowEngine\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
'OCA\\WorkflowEngine\\Service\\Logger' => $baseDir . '/../lib/Service/Logger.php',
'OCA\\WorkflowEngine\\Service\\RuleMatcher' => $baseDir . '/../lib/Service/RuleMatcher.php',
diff --git a/apps/workflowengine/composer/composer/autoload_static.php b/apps/workflowengine/composer/composer/autoload_static.php
index dddcf5611dab1..35129e00e51c0 100644
--- a/apps/workflowengine/composer/composer/autoload_static.php
+++ b/apps/workflowengine/composer/composer/autoload_static.php
@@ -48,6 +48,7 @@ class ComposerStaticInitWorkflowEngine
'OCA\\WorkflowEngine\\Migration\\PopulateNewlyIntroducedDatabaseFields' => __DIR__ . '/..' . '/../lib/Migration/PopulateNewlyIntroducedDatabaseFields.php',
'OCA\\WorkflowEngine\\Migration\\Version2000Date20190808074233' => __DIR__ . '/..' . '/../lib/Migration/Version2000Date20190808074233.php',
'OCA\\WorkflowEngine\\Migration\\Version2200Date20210805101925' => __DIR__ . '/..' . '/../lib/Migration/Version2200Date20210805101925.php',
+ 'OCA\\WorkflowEngine\\Migration\\Version3400Date20260227000000' => __DIR__ . '/..' . '/../lib/Migration/Version3400Date20260227000000.php',
'OCA\\WorkflowEngine\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
'OCA\\WorkflowEngine\\Service\\Logger' => __DIR__ . '/..' . '/../lib/Service/Logger.php',
'OCA\\WorkflowEngine\\Service\\RuleMatcher' => __DIR__ . '/..' . '/../lib/Service/RuleMatcher.php',
diff --git a/apps/workflowengine/lib/Entity/File.php b/apps/workflowengine/lib/Entity/File.php
index 0dc929a325bca..455717ed5482f 100644
--- a/apps/workflowengine/lib/Entity/File.php
+++ b/apps/workflowengine/lib/Entity/File.php
@@ -9,8 +9,15 @@
namespace OCA\WorkflowEngine\Entity;
use OC\Files\Config\UserMountCache;
+use OC\SystemTag\Events\SingleTagAssignedEvent;
use OCP\EventDispatcher\Event;
-use OCP\EventDispatcher\GenericEvent;
+use OCP\Files\Events\Node\AbstractNodeEvent;
+use OCP\Files\Events\Node\NodeCopiedEvent;
+use OCP\Files\Events\Node\NodeCreatedEvent;
+use OCP\Files\Events\Node\NodeDeletedEvent;
+use OCP\Files\Events\Node\NodeRenamedEvent;
+use OCP\Files\Events\Node\NodeTouchedEvent;
+use OCP\Files\Events\Node\NodeUpdatedEvent;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
@@ -22,7 +29,6 @@
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\SystemTag\ISystemTagManager;
-use OCP\SystemTag\MapperEvent;
use OCP\WorkflowEngine\EntityContext\IContextPortation;
use OCP\WorkflowEngine\EntityContext\IDisplayText;
use OCP\WorkflowEngine\EntityContext\IIcon;
@@ -30,50 +36,57 @@
use OCP\WorkflowEngine\GenericEntityEvent;
use OCP\WorkflowEngine\IEntity;
use OCP\WorkflowEngine\IRuleMatcher;
+use Override;
class File implements IEntity, IDisplayText, IUrl, IIcon, IContextPortation {
- private const EVENT_NAMESPACE = '\OCP\Files::';
+ /** @var ?class-string $eventName */
protected ?string $eventName = null;
protected ?Event $event = null;
private ?Node $node = null;
private ?IUser $actingUser = null;
public function __construct(
- protected IL10N $l10n,
- protected IURLGenerator $urlGenerator,
- protected IRootFolder $root,
- private IUserSession $userSession,
- private ISystemTagManager $tagManager,
- private IUserManager $userManager,
- private UserMountCache $userMountCache,
- private IMountManager $mountManager,
+ protected readonly IL10N $l10n,
+ protected readonly IURLGenerator $urlGenerator,
+ protected readonly IRootFolder $root,
+ private readonly IUserSession $userSession,
+ private readonly ISystemTagManager $tagManager,
+ private readonly IUserManager $userManager,
+ private readonly UserMountCache $userMountCache,
+ private readonly IMountManager $mountManager,
) {
}
+ #[Override]
public function getName(): string {
return $this->l10n->t('File');
}
+ #[Override]
public function getIcon(): string {
return $this->urlGenerator->imagePath('core', 'categories/files.svg');
}
+ #[Override]
public function getEvents(): array {
return [
- new GenericEntityEvent($this->l10n->t('File created'), self::EVENT_NAMESPACE . 'postCreate'),
- new GenericEntityEvent($this->l10n->t('File updated'), self::EVENT_NAMESPACE . 'postWrite'),
- new GenericEntityEvent($this->l10n->t('File renamed'), self::EVENT_NAMESPACE . 'postRename'),
- new GenericEntityEvent($this->l10n->t('File deleted'), self::EVENT_NAMESPACE . 'postDelete'),
- new GenericEntityEvent($this->l10n->t('File accessed'), self::EVENT_NAMESPACE . 'postTouch'),
- new GenericEntityEvent($this->l10n->t('File copied'), self::EVENT_NAMESPACE . 'postCopy'),
- new GenericEntityEvent($this->l10n->t('Tag assigned'), MapperEvent::EVENT_ASSIGN),
+ new GenericEntityEvent($this->l10n->t('File created'), NodeCreatedEvent::class),
+ new GenericEntityEvent($this->l10n->t('File updated'), NodeUpdatedEvent::class),
+ new GenericEntityEvent($this->l10n->t('File renamed'), NodeRenamedEvent::class),
+ new GenericEntityEvent($this->l10n->t('File deleted'), NodeDeletedEvent::class),
+ new GenericEntityEvent($this->l10n->t('File accessed'), NodeTouchedEvent::class),
+ new GenericEntityEvent($this->l10n->t('File copied'), NodeCopiedEvent::class),
+ new GenericEntityEvent($this->l10n->t('Tag assigned'), SingleTagAssignedEvent::class),
];
}
+ #[Override]
public function prepareRuleMatcher(IRuleMatcher $ruleMatcher, string $eventName, Event $event): void {
- if (!$event instanceof GenericEvent && !$event instanceof MapperEvent) {
+ $isSupported = array_any($this->getEvents(), static fn (GenericEntityEvent $genericEvent): bool => is_a($event, $genericEvent->getEventName()));
+ if (!$isSupported) {
return;
}
+
$this->eventName = $eventName;
$this->event = $event;
$this->actingUser = $this->actingUser ?? $this->userSession->getUser();
@@ -81,11 +94,12 @@ public function prepareRuleMatcher(IRuleMatcher $ruleMatcher, string $eventName,
$node = $this->getNode();
$ruleMatcher->setEntitySubject($this, $node);
$ruleMatcher->setFileInfo($node->getStorage(), $node->getInternalPath());
- } catch (NotFoundException $e) {
+ } catch (NotFoundException) {
// pass
}
}
+ #[Override]
public function isLegitimatedForUserId(string $userId): bool {
try {
$node = $this->getNode();
@@ -93,7 +107,7 @@ public function isLegitimatedForUserId(string $userId): bool {
return true;
}
- if ($this->eventName === self::EVENT_NAMESPACE . 'postDelete') {
+ if ($this->eventName === NodeDeletedEvent::class) {
// At postDelete, the file no longer exists. Check for parent folder instead.
$fileId = $node->getParentId();
} else {
@@ -120,35 +134,27 @@ protected function getNode(): Node {
if ($this->node) {
return $this->node;
}
- if (!$this->event instanceof GenericEvent && !$this->event instanceof MapperEvent) {
+
+ if ($this->event instanceof AbstractNodeEvent) {
+ return $this->event->getNode();
+ }
+
+ if (!$this->event instanceof SingleTagAssignedEvent || $this->event->getObjectType() !== 'files') {
throw new NotFoundException();
}
- switch ($this->eventName) {
- case self::EVENT_NAMESPACE . 'postCreate':
- case self::EVENT_NAMESPACE . 'postWrite':
- case self::EVENT_NAMESPACE . 'postDelete':
- case self::EVENT_NAMESPACE . 'postTouch':
- return $this->event->getSubject();
- case self::EVENT_NAMESPACE . 'postRename':
- case self::EVENT_NAMESPACE . 'postCopy':
- return $this->event->getSubject()[1];
- case MapperEvent::EVENT_ASSIGN:
- if (!$this->event instanceof MapperEvent || $this->event->getObjectType() !== 'files') {
- throw new NotFoundException();
- }
- $this->node = $this->root->getFirstNodeById((int)$this->event->getObjectId());
- if ($this->node !== null) {
- return $this->node;
- }
- break;
+
+ $this->node = $this->root->getFirstNodeById((int)$this->event->getObjectId());
+ if ($this->node === null) {
+ throw new NotFoundException();
}
- throw new NotFoundException();
+
+ return $this->node;
}
public function getDisplayText(int $verbosity = 0): string {
try {
$node = $this->getNode();
- } catch (NotFoundException $e) {
+ } catch (NotFoundException) {
return '';
}
@@ -158,21 +164,21 @@ public function getDisplayText(int $verbosity = 0): string {
];
switch ($this->eventName) {
- case self::EVENT_NAMESPACE . 'postCreate':
+ case NodeCreatedEvent::class:
return $this->l10n->t('%s created %s', $options);
- case self::EVENT_NAMESPACE . 'postWrite':
+ case NodeUpdatedEvent::class:
return $this->l10n->t('%s modified %s', $options);
- case self::EVENT_NAMESPACE . 'postDelete':
+ case NodeDeletedEvent::class:
return $this->l10n->t('%s deleted %s', $options);
- case self::EVENT_NAMESPACE . 'postTouch':
+ case NodeTouchedEvent::class:
return $this->l10n->t('%s accessed %s', $options);
- case self::EVENT_NAMESPACE . 'postRename':
+ case NodeRenamedEvent::class:
return $this->l10n->t('%s renamed %s', $options);
- case self::EVENT_NAMESPACE . 'postCopy':
+ case NodeCopiedEvent::class:
return $this->l10n->t('%s copied %s', $options);
- case MapperEvent::EVENT_ASSIGN:
+ case SingleTagAssignedEvent::class:
$tagNames = [];
- if ($this->event instanceof MapperEvent) {
+ if ($this->event instanceof SingleTagAssignedEvent) {
$tagIDs = $this->event->getTags();
$tagObjects = $this->tagManager->getTagsByIds($tagIDs);
foreach ($tagObjects as $systemTag) {
@@ -201,9 +207,7 @@ public function getUrl(): string {
}
}
- /**
- * @inheritDoc
- */
+ #[Override]
public function exportContextIDs(): array {
$nodeOwner = $this->getNode()->getOwner();
$actingUserId = null;
@@ -215,14 +219,12 @@ public function exportContextIDs(): array {
return [
'eventName' => $this->eventName,
'nodeId' => $this->getNode()->getId(),
- 'nodeOwnerId' => $nodeOwner ? $nodeOwner->getUID() : null,
+ 'nodeOwnerId' => $nodeOwner?->getUID(),
'actingUserId' => $actingUserId,
];
}
- /**
- * @inheritDoc
- */
+ #[Override]
public function importContextIDs(array $contextIDs): void {
$this->eventName = $contextIDs['eventName'];
if ($contextIDs['nodeOwnerId'] !== null) {
@@ -237,9 +239,7 @@ public function importContextIDs(array $contextIDs): void {
}
}
- /**
- * @inheritDoc
- */
+ #[Override]
public function getIconUrl(): string {
return $this->getIcon();
}
diff --git a/apps/workflowengine/lib/Migration/Version3400Date20260227000000.php b/apps/workflowengine/lib/Migration/Version3400Date20260227000000.php
new file mode 100644
index 0000000000000..0c0cfd63458f5
--- /dev/null
+++ b/apps/workflowengine/lib/Migration/Version3400Date20260227000000.php
@@ -0,0 +1,94 @@
+connection->getQueryBuilder();
+ $lastId = null;
+ while (true) {
+ $qb->select('*')
+ ->from('flow_operations');
+ if ($lastId !== null) {
+ $qb->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($lastId)));
+ }
+ $qb->setMaxResults(1000);
+ $newMapping = [];
+ $result = $qb->executeQuery();
+ while ($row = $result->fetchAssociative()) {
+ $events = json_decode($row['events'], true);
+ $newEvents = array_map(function (string $eventName): string {
+ return match ($eventName) {
+ '\OCP\Files::postCreate' => NodeCreatedEvent::class,
+ '\OCP\Files::postUpdate' => NodeUpdatedEvent::class,
+ '\OCP\Files::postRename' => NodeRenamedEvent::class,
+ '\OCP\Files::postDelete' => NodeDeletedEvent::class,
+ '\OCP\Files::postTouch' => NodeTouchedEvent::class,
+ '\OCP\Files::postCopy' => NodeCopiedEvent::class,
+ 'OCP\SystemTag\ISystemTagObjectMapper::assignTags' => SingleTagAssignedEvent::class,
+ };
+ }, $events);
+
+ if ($newEvents !== $events) {
+ $newMapping[$row['id']] = json_encode($newEvents);
+ }
+ }
+ $result->closeCursor();
+
+ try {
+ if ($newMapping !== []) {
+ $this->connection->beginTransaction();
+ }
+ foreach ($newMapping as $id => $events) {
+ $update = $this->connection->getQueryBuilder();
+ $update->update('flow_operations')
+ ->set('events', $update->createNamedParameter($events))
+ ->where($qb->expr()->eq('id', $update->createNamedParameter($id)))
+ ->executeStatement();
+ }
+ if ($newMapping !== []) {
+ $this->connection->commit();
+ }
+ } catch (\Exception $e) {
+ $this->connection->rollback();
+ throw $e;
+ }
+
+ if ($row !== false) {
+ $lastId = $row['id'];
+ } else {
+ break;
+ }
+ }
+
+ return $schemaClosure();
+ }
+}
diff --git a/apps/workflowengine/tests/ManagerTest.php b/apps/workflowengine/tests/ManagerTest.php
index e5b32cd78f403..f4dba38429dba 100644
--- a/apps/workflowengine/tests/ManagerTest.php
+++ b/apps/workflowengine/tests/ManagerTest.php
@@ -15,6 +15,7 @@
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\Node\NodeCreatedEvent;
+use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\ICache;
@@ -441,12 +442,12 @@ public function testUpdateOperation(): void {
$check2 = ['class' => ICheck::class, 'operator' => 'eq', 'value' => 23456];
/** @noinspection PhpUnhandledExceptionInspection */
- $op = $this->manager->updateOperation($opId1, 'Test01a', [$check1, $check2], 'foohur', $adminScope, $entity, ['\OCP\Files::postDelete']);
+ $op = $this->manager->updateOperation($opId1, 'Test01a', [$check1, $check2], 'foohur', $adminScope, $entity, [NodeDeletedEvent::class]);
$this->assertSame('Test01a', $op['name']);
$this->assertSame('foohur', $op['operation']);
/** @noinspection PhpUnhandledExceptionInspection */
- $op = $this->manager->updateOperation($opId2, 'Test02a', [$check1], 'barfoo', $userScope, $entity, ['\OCP\Files::postDelete']);
+ $op = $this->manager->updateOperation($opId2, 'Test02a', [$check1], 'barfoo', $userScope, $entity, [NodeDeletedEvent::class]);
$this->assertSame('Test02a', $op['name']);
$this->assertSame('barfoo', $op['operation']);
diff --git a/apps/workflowengine/tests/Migration/Version3400Date20260227000000Test.php b/apps/workflowengine/tests/Migration/Version3400Date20260227000000Test.php
new file mode 100644
index 0000000000000..5877d269ece32
--- /dev/null
+++ b/apps/workflowengine/tests/Migration/Version3400Date20260227000000Test.php
@@ -0,0 +1,82 @@
+getQueryBuilder();
+ $qb->delete('flow_operations')
+ ->executeStatement();
+ }
+
+ public function testMigration(): void {
+ $db = Server::get(IDBConnection::class);
+ foreach (['\OCP\Files::postCreate', '\OCP\Files::postUpdate', '\OCP\Files::postRename', '\OCP\Files::postDelete',
+ '\OCP\Files::postTouch', '\OCP\Files::postCopy', 'OCP\SystemTag\ISystemTagObjectMapper::assignTags'] as $legacyEventName) {
+ $qb = $db->getQueryBuilder();
+ $qb->insert('flow_operations')
+ ->values([
+ 'class' => $qb->createNamedParameter('OCA\FlowNotifications\Flow\Operation'),
+ 'name' => $qb->createNamedParameter(''),
+ 'operation' => $qb->createNamedParameter('{"inscription":"Test"}'),
+ 'entity' => $qb->createNamedParameter(File::class),
+ 'events' => $qb->createNamedParameter(json_encode([$legacyEventName])),
+ ])->executeStatement();
+ }
+
+ $migration = Server::get(Version3400Date20260227000000::class);
+ $migration->postSchemaChange(
+ $this->createMock(IOutput::class),
+ fn () => $this->createMock(ISchemaWrapper::class),
+ []
+ );
+
+ $qb = $db->getQueryBuilder();
+ $events = $qb->select('events')
+ ->from('flow_operations')
+ ->executeQuery()
+ ->fetchFirstColumn();
+ foreach ($events as $eventGroup) {
+ $events = json_decode($eventGroup, true);
+ foreach ($events as $event) {
+ $this->assertTrue(in_array($event, [
+ NodeCreatedEvent::class,
+ NodeUpdatedEvent::class,
+ NodeDeletedEvent::class,
+ NodeRenamedEvent::class,
+ NodeTouchedEvent::class,
+ NodeCopiedEvent::class,
+ SingleTagAssignedEvent::class,
+ ]));
+ }
+ }
+ }
+}
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index 9936d3543d934..3304b937f9527 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -2739,17 +2739,6 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 5b6de5ff356d1..f0dcaac1f8a67 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -463,6 +463,7 @@
'OCP\\Files\\Events\\Node\\BeforeNodeReadEvent' => $baseDir . '/lib/public/Files/Events/Node/BeforeNodeReadEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeRenamedEvent' => $baseDir . '/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeTouchedEvent' => $baseDir . '/lib/public/Files/Events/Node/BeforeNodeTouchedEvent.php',
+ 'OCP\\Files\\Events\\Node\\BeforeNodeUpdatedEvent' => $baseDir . '/lib/public/Files/Events/Node/BeforeNodeUpdatedEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeWrittenEvent' => $baseDir . '/lib/public/Files/Events/Node/BeforeNodeWrittenEvent.php',
'OCP\\Files\\Events\\Node\\FilesystemTornDownEvent' => $baseDir . '/lib/public/Files/Events/Node/FilesystemTornDownEvent.php',
'OCP\\Files\\Events\\Node\\NodeCopiedEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeCopiedEvent.php',
@@ -470,6 +471,7 @@
'OCP\\Files\\Events\\Node\\NodeDeletedEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeDeletedEvent.php',
'OCP\\Files\\Events\\Node\\NodeRenamedEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeRenamedEvent.php',
'OCP\\Files\\Events\\Node\\NodeTouchedEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeTouchedEvent.php',
+ 'OCP\\Files\\Events\\Node\\NodeUpdatedEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeUpdatedEvent.php',
'OCP\\Files\\Events\\Node\\NodeWrittenEvent' => $baseDir . '/lib/public/Files/Events/Node/NodeWrittenEvent.php',
'OCP\\Files\\File' => $baseDir . '/lib/public/Files/File.php',
'OCP\\Files\\FileInfo' => $baseDir . '/lib/public/Files/FileInfo.php',
@@ -2202,6 +2204,7 @@
'OC\\Support\\Subscription\\Assertion' => $baseDir . '/lib/private/Support/Subscription/Assertion.php',
'OC\\Support\\Subscription\\Registry' => $baseDir . '/lib/private/Support/Subscription/Registry.php',
'OC\\SystemConfig' => $baseDir . '/lib/private/SystemConfig.php',
+ 'OC\\SystemTag\\Events\\SingleTagAssignedEvent' => $baseDir . '/lib/private/SystemTag/Events/SingleTagAssignedEvent.php',
'OC\\SystemTag\\ManagerFactory' => $baseDir . '/lib/private/SystemTag/ManagerFactory.php',
'OC\\SystemTag\\SystemTag' => $baseDir . '/lib/private/SystemTag/SystemTag.php',
'OC\\SystemTag\\SystemTagManager' => $baseDir . '/lib/private/SystemTag/SystemTagManager.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index fbee07dafc6b4..a4e3cb86aca88 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -504,6 +504,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Events\\Node\\BeforeNodeReadEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/BeforeNodeReadEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeRenamedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/BeforeNodeRenamedEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeTouchedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/BeforeNodeTouchedEvent.php',
+ 'OCP\\Files\\Events\\Node\\BeforeNodeUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/BeforeNodeUpdatedEvent.php',
'OCP\\Files\\Events\\Node\\BeforeNodeWrittenEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/BeforeNodeWrittenEvent.php',
'OCP\\Files\\Events\\Node\\FilesystemTornDownEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/FilesystemTornDownEvent.php',
'OCP\\Files\\Events\\Node\\NodeCopiedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeCopiedEvent.php',
@@ -511,6 +512,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Events\\Node\\NodeDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeDeletedEvent.php',
'OCP\\Files\\Events\\Node\\NodeRenamedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeRenamedEvent.php',
'OCP\\Files\\Events\\Node\\NodeTouchedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeTouchedEvent.php',
+ 'OCP\\Files\\Events\\Node\\NodeUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeUpdatedEvent.php',
'OCP\\Files\\Events\\Node\\NodeWrittenEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/Node/NodeWrittenEvent.php',
'OCP\\Files\\File' => __DIR__ . '/../../..' . '/lib/public/Files/File.php',
'OCP\\Files\\FileInfo' => __DIR__ . '/../../..' . '/lib/public/Files/FileInfo.php',
@@ -2243,6 +2245,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Support\\Subscription\\Assertion' => __DIR__ . '/../../..' . '/lib/private/Support/Subscription/Assertion.php',
'OC\\Support\\Subscription\\Registry' => __DIR__ . '/../../..' . '/lib/private/Support/Subscription/Registry.php',
'OC\\SystemConfig' => __DIR__ . '/../../..' . '/lib/private/SystemConfig.php',
+ 'OC\\SystemTag\\Events\\SingleTagAssignedEvent' => __DIR__ . '/../../..' . '/lib/private/SystemTag/Events/SingleTagAssignedEvent.php',
'OC\\SystemTag\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/SystemTag/ManagerFactory.php',
'OC\\SystemTag\\SystemTag' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTag.php',
'OC\\SystemTag\\SystemTagManager' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagManager.php',
diff --git a/lib/composer/composer/installed.php b/lib/composer/composer/installed.php
index ab3dd57b35611..1c86912b32d09 100644
--- a/lib/composer/composer/installed.php
+++ b/lib/composer/composer/installed.php
@@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '8c12590cf6f93ce7aa41f17817b3791e524da39e',
+ 'reference' => 'faf9ea9f2aa2deef96575c6d981821436013a58b',
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '8c12590cf6f93ce7aa41f17817b3791e524da39e',
+ 'reference' => 'faf9ea9f2aa2deef96575c6d981821436013a58b',
'type' => 'library',
'install_path' => __DIR__ . '/../../../',
'aliases' => array(),
diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php
index 1149951174cbf..3a551222fc877 100644
--- a/lib/private/Files/Node/HookConnector.php
+++ b/lib/private/Files/Node/HookConnector.php
@@ -19,12 +19,14 @@
use OCP\Files\Events\Node\BeforeNodeReadEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
use OCP\Files\Events\Node\BeforeNodeTouchedEvent;
+use OCP\Files\Events\Node\BeforeNodeUpdatedEvent;
use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeRenamedEvent;
use OCP\Files\Events\Node\NodeTouchedEvent;
+use OCP\Files\Events\Node\NodeUpdatedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
@@ -50,6 +52,9 @@ public function viewToNode() {
Util::connectHook('OC_Filesystem', 'create', $this, 'create');
Util::connectHook('OC_Filesystem', 'post_create', $this, 'postCreate');
+ Util::connectHook('OC_Filesystem', 'update', $this, 'update');
+ Util::connectHook('OC_Filesystem', 'post_update', $this, 'postUpdate');
+
Util::connectHook('OC_Filesystem', 'delete', $this, 'delete');
Util::connectHook('OC_Filesystem', 'post_delete', $this, 'postDelete');
@@ -101,6 +106,16 @@ public function postCreate($arguments) {
$this->dispatcher->dispatchTyped($event);
}
+ public function update(array $arguments): void {
+ $node = $this->getNodeForPath($arguments['path']);
+ $this->dispatcher->dispatchTyped(new BeforeNodeUpdatedEvent($node));
+ }
+
+ public function postUpdate(array $arguments): void {
+ $node = $this->getNodeForPath($arguments['path']);
+ $this->dispatcher->dispatchTyped(new NodeUpdatedEvent($node));
+ }
+
public function delete($arguments) {
$node = $this->getNodeForPath($arguments['path']);
$this->deleteMetaCache[$node->getPath()] = $node->getFileInfo();
diff --git a/lib/private/SystemTag/Events/SingleTagAssignedEvent.php b/lib/private/SystemTag/Events/SingleTagAssignedEvent.php
new file mode 100644
index 0000000000000..2a3746aa81238
--- /dev/null
+++ b/lib/private/SystemTag/Events/SingleTagAssignedEvent.php
@@ -0,0 +1,58 @@
+ $tags
+ */
+ public function __construct(
+ private readonly string $objectType,
+ private readonly string $objectId,
+ private readonly array $tags,
+ ) {
+ parent::__construct();
+ }
+
+ public function getObjectType(): string {
+ return $this->objectType;
+ }
+
+ public function getObjectId(): string {
+ return $this->objectId;
+ }
+
+ /**
+ * @return list
+ */
+ public function getTags(): array {
+ return $this->tags;
+ }
+
+ /**
+ * @return array{objectType: string, objectId: string, tagIds: list}
+ */
+ public function getWebhookSerializable(): array {
+ return [
+ 'objectType' => $this->getObjectType(),
+ 'objectId' => $this->getObjectId(),
+ 'tagIds' => $this->getTags(),
+ ];
+ }
+}
diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php
index 508adb8c88c6c..2882e807e16f7 100644
--- a/lib/private/SystemTag/SystemTagObjectMapper.php
+++ b/lib/private/SystemTag/SystemTagObjectMapper.php
@@ -9,6 +9,7 @@
*/
namespace OC\SystemTag;
+use OC\SystemTag\Events\SingleTagAssignedEvent;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
@@ -174,6 +175,7 @@ public function assignTags(string $objId, string $objectType, $tagIds): void {
$tagsAssigned,
));
$this->dispatcher->dispatchTyped(new TagAssignedEvent($objectType, [$objId], $tagsAssigned));
+ $this->dispatcher->dispatchTyped(new SingleTagAssignedEvent($objectType, $objId, $tagsAssigned));
}
#[Override]
@@ -344,6 +346,7 @@ public function setObjectIdsForTag(string $tagId, string $objectType, array $obj
(string)$objectId,
[(int)$tagId]
));
+ $this->dispatcher->dispatchTyped(new SingleTagAssignedEvent($objectType, $objectId, [(int)$tagId]));
}
if (!empty($addedObjectIds)) {
$this->dispatcher->dispatchTyped(new TagAssignedEvent($objectType, array_map(fn ($objectId) => (string)$objectId, $addedObjectIds), [(int)$tagId]));
diff --git a/lib/public/Files/Events/Node/BeforeNodeUpdatedEvent.php b/lib/public/Files/Events/Node/BeforeNodeUpdatedEvent.php
new file mode 100644
index 0000000000000..3259d3557f9ea
--- /dev/null
+++ b/lib/public/Files/Events/Node/BeforeNodeUpdatedEvent.php
@@ -0,0 +1,18 @@
+ $eventName
* @since 18.0.0
*/
- public function __construct(string $displayName, string $eventName) {
- if (trim($displayName) === '') {
- throw new \InvalidArgumentException('DisplayName must not be empty');
- }
- if (trim($eventName) === '') {
- throw new \InvalidArgumentException('EventName must not be empty');
- }
-
- $this->displayName = trim($displayName);
- $this->eventName = trim($eventName);
+ public function __construct(
+ private readonly string $displayName,
+ private readonly string $eventName,
+ ) {
}
- /**
- * returns a translated name to be presented in the web interface.
- *
- * Example: "created" (en), "kreita" (eo)
- *
- * @since 18.0.0
- */
+ #[Override]
public function getDisplayName(): string {
return $this->displayName;
}
- /**
- * returns the event name that is emitted by the EventDispatcher, e.g.:
- *
- * Example: "OCA\MyApp\Factory\Cats::postCreated"
- *
- * @since 18.0.0
- */
+ #[Override]
public function getEventName(): string {
return $this->eventName;
}
diff --git a/lib/public/WorkflowEngine/IEntityEvent.php b/lib/public/WorkflowEngine/IEntityEvent.php
index 66088c26b4784..f9cf15ba615d3 100644
--- a/lib/public/WorkflowEngine/IEntityEvent.php
+++ b/lib/public/WorkflowEngine/IEntityEvent.php
@@ -8,6 +8,8 @@
*/
namespace OCP\WorkflowEngine;
+use OCP\EventDispatcher\Event;
+
/**
* Interface IEntityEvent
*
@@ -27,10 +29,11 @@ interface IEntityEvent {
public function getDisplayName(): string;
/**
- * returns the event name that is emitted by the EventDispatcher, e.g.:
+ * Returns the event name that is emitted by the EventDispatcher, e.g.:
*
* Example: "OCA\MyApp\Factory\Cats::postCreated"
*
+ * @return class-string
* @since 18.0.0
*/
public function getEventName(): string;
diff --git a/tests/lib/SystemTag/SystemTagObjectMapperTest.php b/tests/lib/SystemTag/SystemTagObjectMapperTest.php
index 5542d046d88f2..d2625ea69832d 100644
--- a/tests/lib/SystemTag/SystemTagObjectMapperTest.php
+++ b/tests/lib/SystemTag/SystemTagObjectMapperTest.php
@@ -8,6 +8,7 @@
namespace Test\SystemTag;
+use OC\SystemTag\Events\SingleTagAssignedEvent;
use OC\SystemTag\SystemTag;
use OC\SystemTag\SystemTagManager;
use OC\SystemTag\SystemTagObjectMapper;
@@ -18,7 +19,6 @@
use OCP\SystemTag\ISystemTag;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
-use OCP\SystemTag\TagAssignedEvent;
use OCP\SystemTag\TagNotFoundException;
use OCP\SystemTag\TagUnassignedEvent;
use Test\TestCase;
@@ -234,20 +234,18 @@ public function testAssignUnassignTags(): void {
$this->tagMapper->assignTags('1', 'testtype', [$this->tag1->getId()]);
$this->assertNotNull($event);
- $this->assertEquals(TagAssignedEvent::class, $event::class);
+ $this->assertEquals(SingleTagAssignedEvent::class, $event::class);
$this->assertEquals('testtype', $event->getObjectType());
- $this->assertCount(1, $event->getObjectIds());
- $this->assertEquals('1', current($event->getObjectIds()));
+ $this->assertEquals('1', $event->getObjectId());
$this->assertCount(1, $event->getTags());
$this->assertEquals($this->tag1->getId(), current($event->getTags()));
$this->tagMapper->assignTags('1', 'testtype', $this->tag3->getId());
$this->assertNotNull($event);
- $this->assertEquals(TagAssignedEvent::class, $event::class);
+ $this->assertEquals(SingleTagAssignedEvent::class, $event::class);
$this->assertEquals('testtype', $event->getObjectType());
- $this->assertCount(1, $event->getObjectIds());
- $this->assertEquals('1', current($event->getObjectIds()));
+ $this->assertEquals('1', $event->getObjectId());
$this->assertCount(1, $event->getTags());
$this->assertEquals($this->tag3->getId(), current($event->getTags()));