Merge pull request #59610 from nextcloud/feat/versions-wfe

feat(files_versions): allow to block version creation using WFE
This commit is contained in:
Ferdinand Thiessen 2026-06-01 18:05:23 +02:00 committed by GitHub
commit f8f5e9e7f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 215 additions and 1 deletions

View file

@ -9,6 +9,7 @@ return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'OCA\\Files_Versions\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
'OCA\\Files_Versions\\BackgroundJob\\ExpireVersions' => $baseDir . '/../lib/BackgroundJob/ExpireVersions.php',
'OCA\\Files_Versions\\BlockVersioningOperation' => $baseDir . '/../lib/BlockVersioningOperation.php',
'OCA\\Files_Versions\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
'OCA\\Files_Versions\\Command\\CleanUp' => $baseDir . '/../lib/Command/CleanUp.php',
'OCA\\Files_Versions\\Command\\Expire' => $baseDir . '/../lib/Command/Expire.php',
@ -20,10 +21,12 @@ return array(
'OCA\\Files_Versions\\Events\\VersionCreatedEvent' => $baseDir . '/../lib/Events/VersionCreatedEvent.php',
'OCA\\Files_Versions\\Events\\VersionRestoredEvent' => $baseDir . '/../lib/Events/VersionRestoredEvent.php',
'OCA\\Files_Versions\\Expiration' => $baseDir . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Listener\\CreateVersionListenerForWorkflow' => $baseDir . '/../lib/Listener/CreateVersionListenerForWorkflow.php',
'OCA\\Files_Versions\\Listener\\FileEventsListener' => $baseDir . '/../lib/Listener/FileEventsListener.php',
'OCA\\Files_Versions\\Listener\\LegacyRollbackListener' => $baseDir . '/../lib/Listener/LegacyRollbackListener.php',
'OCA\\Files_Versions\\Listener\\LoadAdditionalListener' => $baseDir . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Versions\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Versions\\Listener\\RegisterWorkflowIntegrationListener' => $baseDir . '/../lib/Listener/RegisterWorkflowIntegrationListener.php',
'OCA\\Files_Versions\\Listener\\VersionAuthorListener' => $baseDir . '/../lib/Listener/VersionAuthorListener.php',
'OCA\\Files_Versions\\Listener\\VersionStorageMoveListener' => $baseDir . '/../lib/Listener/VersionStorageMoveListener.php',
'OCA\\Files_Versions\\Migration\\Version1020Date20221114144058' => $baseDir . '/../lib/Migration/Version1020Date20221114144058.php',

View file

@ -24,6 +24,7 @@ class ComposerStaticInitFiles_Versions
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'OCA\\Files_Versions\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
'OCA\\Files_Versions\\BackgroundJob\\ExpireVersions' => __DIR__ . '/..' . '/../lib/BackgroundJob/ExpireVersions.php',
'OCA\\Files_Versions\\BlockVersioningOperation' => __DIR__ . '/..' . '/../lib/BlockVersioningOperation.php',
'OCA\\Files_Versions\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
'OCA\\Files_Versions\\Command\\CleanUp' => __DIR__ . '/..' . '/../lib/Command/CleanUp.php',
'OCA\\Files_Versions\\Command\\Expire' => __DIR__ . '/..' . '/../lib/Command/Expire.php',
@ -35,10 +36,12 @@ class ComposerStaticInitFiles_Versions
'OCA\\Files_Versions\\Events\\VersionCreatedEvent' => __DIR__ . '/..' . '/../lib/Events/VersionCreatedEvent.php',
'OCA\\Files_Versions\\Events\\VersionRestoredEvent' => __DIR__ . '/..' . '/../lib/Events/VersionRestoredEvent.php',
'OCA\\Files_Versions\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Listener\\CreateVersionListenerForWorkflow' => __DIR__ . '/..' . '/../lib/Listener/CreateVersionListenerForWorkflow.php',
'OCA\\Files_Versions\\Listener\\FileEventsListener' => __DIR__ . '/..' . '/../lib/Listener/FileEventsListener.php',
'OCA\\Files_Versions\\Listener\\LegacyRollbackListener' => __DIR__ . '/..' . '/../lib/Listener/LegacyRollbackListener.php',
'OCA\\Files_Versions\\Listener\\LoadAdditionalListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalListener.php',
'OCA\\Files_Versions\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Versions\\Listener\\RegisterWorkflowIntegrationListener' => __DIR__ . '/..' . '/../lib/Listener/RegisterWorkflowIntegrationListener.php',
'OCA\\Files_Versions\\Listener\\VersionAuthorListener' => __DIR__ . '/..' . '/../lib/Listener/VersionAuthorListener.php',
'OCA\\Files_Versions\\Listener\\VersionStorageMoveListener' => __DIR__ . '/..' . '/../lib/Listener/VersionStorageMoveListener.php',
'OCA\\Files_Versions\\Migration\\Version1020Date20221114144058' => __DIR__ . '/..' . '/../lib/Migration/Version1020Date20221114144058.php',

View file

@ -12,11 +12,14 @@ use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Versions\Capabilities;
use OCA\Files_Versions\Events\CreateVersionEvent;
use OCA\Files_Versions\Events\VersionRestoredEvent;
use OCA\Files_Versions\Listener\CreateVersionListenerForWorkflow;
use OCA\Files_Versions\Listener\FileEventsListener;
use OCA\Files_Versions\Listener\LegacyRollbackListener;
use OCA\Files_Versions\Listener\LoadAdditionalListener;
use OCA\Files_Versions\Listener\LoadSidebarListener;
use OCA\Files_Versions\Listener\RegisterWorkflowIntegrationListener;
use OCA\Files_Versions\Listener\VersionAuthorListener;
use OCA\Files_Versions\Listener\VersionStorageMoveListener;
use OCA\Files_Versions\Versions\IVersionManager;
@ -37,6 +40,8 @@ use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeRenamedEvent;
use OCP\Files\Events\Node\NodeTouchedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\WorkflowEngine\Events\LoadSettingsScriptsEvent;
use OCP\WorkflowEngine\Events\RegisterOperationsEvent;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
@ -86,8 +91,12 @@ class Application extends App implements IBootstrap {
// we add the version author listener with lower priority to make sure new versions already are created by FileEventsListener
$context->registerEventListener(NodeWrittenEvent::class, VersionAuthorListener::class, -1);
$context->registerEventListener(VersionRestoredEvent::class, LegacyRollbackListener::class);
// WFE integration
$context->registerEventListener(RegisterOperationsEvent::class, RegisterWorkflowIntegrationListener::class);
$context->registerEventListener(LoadSettingsScriptsEvent::class, RegisterWorkflowIntegrationListener::class);
$context->registerEventListener(CreateVersionEvent::class, CreateVersionListenerForWorkflow::class);
}
#[\Override]

View file

@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions;
use OCA\Files_Versions\Events\CreateVersionEvent;
use OCA\WorkflowEngine\Entity\File as FileEntity;
use OCP\EventDispatcher\Event;
use OCP\Files\Folder;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\WorkflowEngine\IComplexOperation;
use OCP\WorkflowEngine\IManager;
use OCP\WorkflowEngine\IRuleMatcher;
use OCP\WorkflowEngine\ISpecificOperation;
use Psr\Log\LoggerInterface;
class BlockVersioningOperation implements ISpecificOperation, IComplexOperation {
public function __construct(
private readonly IL10N $l10n,
private readonly FileEntity $fileEntity,
private readonly LoggerInterface $logger,
private readonly IURLGenerator $urlGenerator,
) {
}
#[\Override]
public function getEntityId(): string {
return FileEntity::class;
}
#[\Override]
public function getDisplayName(): string {
return $this->l10n->t('Block file versioning');
}
#[\Override]
public function getDescription(): string {
return $this->l10n->t('Automatic tag based blocking of file version creation.');
}
#[\Override]
public function getIcon(): string {
return $this->urlGenerator->imagePath('files_versions', 'app.svg');
}
#[\Override]
public function isAvailableForScope(int $scope): bool {
return $scope === IManager::SCOPE_ADMIN;
}
#[\Override]
public function validateOperation(string $name, array $checks, string $operation): void {
if (empty($checks)) {
throw new \UnexpectedValueException($this->l10n->t('No rule given'));
}
}
#[\Override]
public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatcher): void {
if ($eventName !== CreateVersionEvent::class || !($event instanceof CreateVersionEvent)) {
return;
}
$node = $event->getNode();
$path = $node->getInternalPath();
$ruleMatcher->setFileInfo(
$node->getStorage(),
$path,
$node instanceof Folder,
);
$ruleMatcher->setEntitySubject($this->fileEntity, $node);
$ruleMatcher->setOperation($this);
$flows = $ruleMatcher->getFlows();
if ($flows !== []) {
$this->logger->debug('Blocking version creation due to matching workflow rules', [
'path' => $path,
]);
$event->disableVersions();
}
}
#[\Override]
public function getTriggerHint(): string {
return $this->l10n->t('A new version is created'); // TRANSLATORS: This will be shown as "When: " "A new version is created"
}
}

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions\Listener;
use OCA\Files_Versions\BlockVersioningOperation;
use OCA\Files_Versions\Events\CreateVersionEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\WorkflowEngine\IManager;
/** @template-implements IEventListener<CreateVersionEvent> */
class CreateVersionListenerForWorkflow implements IEventListener {
public function __construct(
private IManager $manager,
private BlockVersioningOperation $operation,
) {
}
#[\Override]
public function handle(Event $event): void {
if (!($event instanceof CreateVersionEvent)) {
return;
}
$this->operation->onEvent(
$event::class,
$event,
$this->manager->getRuleMatcher(),
);
}
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Files_Versions\Listener;
use OCA\Files_Versions\BlockVersioningOperation;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Util;
use OCP\WorkflowEngine\Events\LoadSettingsScriptsEvent;
use OCP\WorkflowEngine\Events\RegisterOperationsEvent;
/** @template-implements IEventListener<RegisterOperationsEvent|LoadSettingsScriptsEvent> */
class RegisterWorkflowIntegrationListener implements IEventListener {
public function __construct(
private readonly BlockVersioningOperation $operation,
) {
}
#[\Override]
public function handle(Event $event): void {
if ($event instanceof RegisterOperationsEvent) {
$event->registerOperation($this->operation);
} elseif ($event instanceof LoadSettingsScriptsEvent) {
Util::addScript('files_versions', 'workflow', 'workflowengine');
}
}
}

View file

@ -205,6 +205,7 @@ class Storage {
}
$event = new CreateVersionEvent($file);
$eventDispatcher->dispatchTyped($event);
$eventDispatcher->dispatch('OCA\Files_Versions::createVersion', $event);
if ($event->shouldCreateVersion() === false) {
return false;

View file

@ -0,0 +1,12 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
window.addEventListener('DOMContentLoaded', () => {
globalThis.OCA.WorkflowEngine.registerOperator({
id: 'OCA\\Files_Versions\\BlockVersioningOperation',
color: '#ff5900',
operation: 'deny',
})
})

View file

@ -48,6 +48,7 @@ const modules = {
},
files_versions: {
'sidebar-tab': resolve(import.meta.dirname, 'apps/files_versions/src', 'sidebar_tab.ts'),
workflow: resolve(import.meta.dirname, 'apps/files_versions/src', 'workflow.ts'),
},
oauth2: {
'settings-admin': resolve(import.meta.dirname, 'apps/oauth2/src', 'settings-admin.ts'),

1
dist/files_versions-workflow.css vendored Normal file
View file

@ -0,0 +1 @@
/* extracted by css-entry-points-plugin */

2
dist/files_versions-workflow.mjs vendored Normal file
View file

@ -0,0 +1,2 @@
window.addEventListener("DOMContentLoaded",()=>{globalThis.OCA.WorkflowEngine.registerOperator({id:"OCA\\Files_Versions\\BlockVersioningOperation",color:"#ff5900",operation:"deny"})});
//# sourceMappingURL=files_versions-workflow.mjs.map

View file

@ -0,0 +1,7 @@
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
This file is generated from multiple sources. Included packages:
- nextcloud-ui
- version: 1.0.0
- license: AGPL-3.0-or-later

1
dist/files_versions-workflow.mjs.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"file":"files_versions-workflow.mjs","sources":["../build/frontend/apps/files_versions/src/workflow.ts"],"sourcesContent":["/*!\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nwindow.addEventListener('DOMContentLoaded', () => {\n\tglobalThis.OCA.WorkflowEngine.registerOperator({\n\t\tid: 'OCA\\\\Files_Versions\\\\BlockVersioningOperation',\n\t\tcolor: '#ff5900',\n\t\toperation: 'deny',\n\t})\n})\n"],"names":[],"mappings":"AAKA,OAAO,iBAAiB,mBAAoB,IAAM,CACjD,WAAW,IAAI,eAAe,iBAAiB,CAC9C,GAAI,gDACJ,MAAO,UACP,UAAW,MAAA,CACX,CACF,CAAC"}

View file

@ -0,0 +1,7 @@
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
This file is generated from multiple sources. Included packages:
- nextcloud-ui
- version: 1.0.0
- license: AGPL-3.0-or-later