diff options
author | Roeland Jago Douma <rullzer@users.noreply.github.com> | 2019-09-11 13:14:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-11 13:14:06 +0200 |
commit | 2187f856ce4eadeee88bd8b9ff47719df52ea025 (patch) | |
tree | 9df95fb0db015c0f597237c105ee7c5838396a97 /lib | |
parent | 0cc780ec0a94e6ea2103ee667b111cc38fe81f12 (diff) | |
parent | 228cb240bcfe18410a2ce53a35531cdba98b014d (diff) | |
download | nextcloud-server-2187f856ce4eadeee88bd8b9ff47719df52ea025.tar.gz nextcloud-server-2187f856ce4eadeee88bd8b9ff47719df52ea025.zip |
Merge pull request #16682 from nextcloud/enh/12790/workflow-backend
workflow overhaul
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 8 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 8 | ||||
-rw-r--r-- | lib/private/Files/Node/File.php | 2 | ||||
-rw-r--r-- | lib/private/Files/Node/Folder.php | 14 | ||||
-rw-r--r-- | lib/private/Files/Node/HookConnector.php | 20 | ||||
-rw-r--r-- | lib/private/Files/Node/Node.php | 24 | ||||
-rw-r--r-- | lib/private/Server.php | 2 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/GenericEntityEvent.php | 79 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/ICheck.php | 35 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IComplexOperation.php | 56 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IEntity.php | 77 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IEntityCheck.php | 54 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IEntityEvent.php | 54 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IFileCheck.php | 42 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IManager.php | 43 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IOperation.php | 70 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/IRuleMatcher.php | 39 | ||||
-rw-r--r-- | lib/public/WorkflowEngine/ISpecificOperation.php | 50 |
18 files changed, 629 insertions, 48 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 045c5ef7475..48be2fc03fb 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -438,9 +438,17 @@ return array( 'OCP\\User\\Backend\\ISetDisplayNameBackend' => $baseDir . '/lib/public/User/Backend/ISetDisplayNameBackend.php', 'OCP\\User\\Backend\\ISetPasswordBackend' => $baseDir . '/lib/public/User/Backend/ISetPasswordBackend.php', 'OCP\\Util' => $baseDir . '/lib/public/Util.php', + 'OCP\\WorkflowEngine\\GenericEntityEvent' => $baseDir . '/lib/public/WorkflowEngine/GenericEntityEvent.php', 'OCP\\WorkflowEngine\\ICheck' => $baseDir . '/lib/public/WorkflowEngine/ICheck.php', + 'OCP\\WorkflowEngine\\IComplexOperation' => $baseDir . '/lib/public/WorkflowEngine/IComplexOperation.php', + 'OCP\\WorkflowEngine\\IEntity' => $baseDir . '/lib/public/WorkflowEngine/IEntity.php', + 'OCP\\WorkflowEngine\\IEntityCheck' => $baseDir . '/lib/public/WorkflowEngine/IEntityCheck.php', + 'OCP\\WorkflowEngine\\IEntityEvent' => $baseDir . '/lib/public/WorkflowEngine/IEntityEvent.php', + 'OCP\\WorkflowEngine\\IFileCheck' => $baseDir . '/lib/public/WorkflowEngine/IFileCheck.php', 'OCP\\WorkflowEngine\\IManager' => $baseDir . '/lib/public/WorkflowEngine/IManager.php', 'OCP\\WorkflowEngine\\IOperation' => $baseDir . '/lib/public/WorkflowEngine/IOperation.php', + 'OCP\\WorkflowEngine\\IRuleMatcher' => $baseDir . '/lib/public/WorkflowEngine/IRuleMatcher.php', + 'OCP\\WorkflowEngine\\ISpecificOperation' => $baseDir . '/lib/public/WorkflowEngine/ISpecificOperation.php', 'OC\\Accounts\\Account' => $baseDir . '/lib/private/Accounts/Account.php', 'OC\\Accounts\\AccountManager' => $baseDir . '/lib/private/Accounts/AccountManager.php', 'OC\\Accounts\\AccountProperty' => $baseDir . '/lib/private/Accounts/AccountProperty.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 600491e600e..0a5a1dc2fb6 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -472,9 +472,17 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\User\\Backend\\ISetDisplayNameBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/ISetDisplayNameBackend.php', 'OCP\\User\\Backend\\ISetPasswordBackend' => __DIR__ . '/../../..' . '/lib/public/User/Backend/ISetPasswordBackend.php', 'OCP\\Util' => __DIR__ . '/../../..' . '/lib/public/Util.php', + 'OCP\\WorkflowEngine\\GenericEntityEvent' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/GenericEntityEvent.php', 'OCP\\WorkflowEngine\\ICheck' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/ICheck.php', + 'OCP\\WorkflowEngine\\IComplexOperation' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IComplexOperation.php', + 'OCP\\WorkflowEngine\\IEntity' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IEntity.php', + 'OCP\\WorkflowEngine\\IEntityCheck' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IEntityCheck.php', + 'OCP\\WorkflowEngine\\IEntityEvent' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IEntityEvent.php', + 'OCP\\WorkflowEngine\\IFileCheck' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IFileCheck.php', 'OCP\\WorkflowEngine\\IManager' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IManager.php', 'OCP\\WorkflowEngine\\IOperation' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IOperation.php', + 'OCP\\WorkflowEngine\\IRuleMatcher' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/IRuleMatcher.php', + 'OCP\\WorkflowEngine\\ISpecificOperation' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/ISpecificOperation.php', 'OC\\Accounts\\Account' => __DIR__ . '/../../..' . '/lib/private/Accounts/Account.php', 'OC\\Accounts\\AccountManager' => __DIR__ . '/../../..' . '/lib/private/Accounts/AccountManager.php', 'OC\\Accounts\\AccountProperty' => __DIR__ . '/../../..' . '/lib/private/Accounts/AccountProperty.php', diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php index a3eabbcc446..b504c7a29da 100644 --- a/lib/private/Files/Node/File.php +++ b/lib/private/Files/Node/File.php @@ -119,7 +119,7 @@ class File extends Node implements \OCP\Files\File { $fileInfo = $this->getFileInfo(); $this->view->unlink($this->path); $nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo); - $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); + $this->sendHooks(['postDelete'], [$nonExisting]); $this->exists = false; $this->fileInfo = null; } else { diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 19f04048779..8b2a93ffdc2 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -156,14 +156,12 @@ class Folder extends Node implements \OCP\Files\Folder { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); - $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); + $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); if(!$this->view->mkdir($fullPath)) { throw new NotPermittedException('Could not create folder'); } $node = new Folder($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'postWrite', array($node)); - $this->root->emit('\OC\Files', 'postCreate', array($node)); + $this->sendHooks(['postWrite', 'postCreate'], [$node]); return $node; } else { throw new NotPermittedException('No create permission for folder'); @@ -179,14 +177,12 @@ class Folder extends Node implements \OCP\Files\Folder { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); - $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); + $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); if (!$this->view->touch($fullPath)) { throw new NotPermittedException('Could not create path'); } $node = new File($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'postWrite', array($node)); - $this->root->emit('\OC\Files', 'postCreate', array($node)); + $this->sendHooks(['postWrite', 'postCreate'], [$node]); return $node; } throw new NotPermittedException('No create permission for path'); @@ -341,7 +337,7 @@ class Folder extends Node implements \OCP\Files\Folder { $fileInfo = $this->getFileInfo(); $this->view->rmdir($this->path); $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo); - $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); + $this->sendHooks(['postDelete'], [$nonExisting]); $this->exists = false; } else { throw new NotPermittedException('No delete permission for path'); diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php index f5adcde4a00..4783a71b07b 100644 --- a/lib/private/Files/Node/HookConnector.php +++ b/lib/private/Files/Node/HookConnector.php @@ -26,6 +26,8 @@ use OCP\Files\FileInfo; use OC\Files\Filesystem; use OC\Files\View; use OCP\Util; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; class HookConnector { /** @@ -42,6 +44,8 @@ class HookConnector { * @var FileInfo[] */ private $deleteMetaCache = []; + /** @var EventDispatcherInterface */ + private $dispatcher; /** * HookConnector constructor. @@ -49,9 +53,10 @@ class HookConnector { * @param Root $root * @param View $view */ - public function __construct(Root $root, View $view) { + public function __construct(Root $root, View $view, EventDispatcherInterface $dispatcher) { $this->root = $root; $this->view = $view; + $this->dispatcher = $dispatcher; } public function viewToNode() { @@ -79,72 +84,85 @@ class HookConnector { public function write($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preWrite', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preWrite', new GenericEvent($node)); } public function postWrite($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postWrite', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postWrite', new GenericEvent($node)); } public function create($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preCreate', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preCreate', new GenericEvent($node)); } public function postCreate($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postCreate', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postCreate', new GenericEvent($node)); } public function delete($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->deleteMetaCache[$node->getPath()] = $node->getFileInfo(); $this->root->emit('\OC\Files', 'preDelete', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preDelete', new GenericEvent($node)); } public function postDelete($arguments) { $node = $this->getNodeForPath($arguments['path']); unset($this->deleteMetaCache[$node->getPath()]); $this->root->emit('\OC\Files', 'postDelete', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postDelete', new GenericEvent($node)); } public function touch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preTouch', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preTouch', new GenericEvent($node)); } public function postTouch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postTouch', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postTouch', new GenericEvent($node)); } public function rename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preRename', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::preRename', new GenericEvent([$source, $target])); } public function postRename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postRename', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::postRename', new GenericEvent([$source, $target])); } public function copy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preCopy', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target])); } public function postCopy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postCopy', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::postCopy', new GenericEvent([$source, $target])); } public function read($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'read', [$node]); + $this->dispatcher->dispatch('\OCP\Files::read', new GenericEvent([$node])); } private function getNodeForPath($path) { diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index dc025b79575..c440dd4a8f1 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -33,6 +33,7 @@ use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use Symfony\Component\EventDispatcher\GenericEvent; // FIXME: this class really should be abstract class Node implements \OCP\Files\Node { @@ -104,9 +105,12 @@ class Node implements \OCP\Files\Node { /** * @param string[] $hooks */ - protected function sendHooks($hooks) { + protected function sendHooks($hooks, array $args = null) { + $args = !empty($args) ? $args : [$this]; + $dispatcher = \OC::$server->getEventDispatcher(); foreach ($hooks as $hook) { - $this->root->emit('\OC\Files', $hook, array($this)); + $this->root->emit('\OC\Files', $hook, $args); + $dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args)); } } @@ -394,14 +398,14 @@ class Node implements \OCP\Files\Node { $parent = $this->root->get(dirname($targetPath)); if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) { $nonExisting = $this->createNonExistingNode($targetPath); - $this->root->emit('\OC\Files', 'preCopy', [$this, $nonExisting]); - $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); + $this->sendHooks(['preCopy'], [$this, $nonExisting]); + $this->sendHooks(['preWrite'], [$nonExisting]); if (!$this->view->copy($this->path, $targetPath)) { throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); - $this->root->emit('\OC\Files', 'postCopy', [$this, $targetNode]); - $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); + $this->sendHooks(['postCopy'], [$this, $targetNode]); + $this->sendHooks(['postWrite'], [$targetNode]); return $targetNode; } else { throw new NotPermittedException('No permission to copy to path ' . $targetPath); @@ -425,14 +429,14 @@ class Node implements \OCP\Files\Node { ) ) { $nonExisting = $this->createNonExistingNode($targetPath); - $this->root->emit('\OC\Files', 'preRename', [$this, $nonExisting]); - $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); + $this->sendHooks(['preRename'], [$this, $nonExisting]); + $this->sendHooks(['preWrite'], [$nonExisting]); if (!$this->view->rename($this->path, $targetPath)) { throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); - $this->root->emit('\OC\Files', 'postRename', [$this, $targetNode]); - $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); + $this->sendHooks(['postRename'], [$this, $targetNode]); + $this->sendHooks(['postWrite'], [$targetNode]); $this->path = $targetPath; return $targetNode; } else { diff --git a/lib/private/Server.php b/lib/private/Server.php index bce4f0feaef..433ee044fa4 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -298,7 +298,7 @@ class Server extends ServerContainer implements IServerContainer { $this->getLogger(), $this->getUserManager() ); - $connector = new HookConnector($root, $view); + $connector = new HookConnector($root, $view, $c->getEventDispatcher()); $connector->viewToNode(); $previewConnector = new \OC\Preview\WatcherConnector($root, $c->getSystemConfig()); diff --git a/lib/public/WorkflowEngine/GenericEntityEvent.php b/lib/public/WorkflowEngine/GenericEntityEvent.php new file mode 100644 index 00000000000..3ea34c6fb87 --- /dev/null +++ b/lib/public/WorkflowEngine/GenericEntityEvent.php @@ -0,0 +1,79 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +/** + * Class GenericEntityEvent + * + * @package OCP\WorkflowEngine + * + * @since 18.0.0 + */ +class GenericEntityEvent implements IEntityEvent { + + /** @var string */ + private $displayName; + /** @var string */ + private $eventName; + + /** + * GenericEntityEvent constructor. + * + * @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); + } + + /** + * returns a translated name to be presented in the web interface. + * + * Example: "created" (en), "kreita" (eo) + * + * @since 18.0.0 + */ + 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 + */ + public function getEventName(): string { + return $this->eventName; + } +} diff --git a/lib/public/WorkflowEngine/ICheck.php b/lib/public/WorkflowEngine/ICheck.php index 1d4fc966460..f5586e83d51 100644 --- a/lib/public/WorkflowEngine/ICheck.php +++ b/lib/public/WorkflowEngine/ICheck.php @@ -2,6 +2,7 @@ /** * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> * @author Morris Jobke <hey@morrisjobke.de> * * @license GNU AGPL version 3 or any later version @@ -23,9 +24,6 @@ namespace OCP\WorkflowEngine; - -use OCP\Files\Storage\IStorage; - /** * Interface ICheck * @@ -34,13 +32,6 @@ use OCP\Files\Storage\IStorage; */ interface ICheck { /** - * @param IStorage $storage - * @param string $path - * @since 9.1 - */ - public function setFileInfo(IStorage $storage, $path); - - /** * @param string $operator * @param string $value * @return bool @@ -55,4 +46,28 @@ interface ICheck { * @since 9.1 */ public function validateCheck($operator, $value); + + /** + * returns a list of Entities the checker supports. The values must match + * the class name of the entity. + * + * An empty result means the check is universally available. + * + * @since 18.0.0 + */ + public function supportedEntities(): array; + + /** + * returns whether the operation can be used in the requested scope. + * + * Scope IDs are defined as constants in OCP\WorkflowEngine\IManager. At + * time of writing these are SCOPE_ADMIN and SCOPE_USER. + * + * For possibly unknown future scopes the recommended behaviour is: if + * user scope is permitted, the default behaviour should return `true`, + * otherwise `false`. + * + * @since 18.0.0 + */ + public function isAvailableForScope(int $scope): bool; } diff --git a/lib/public/WorkflowEngine/IComplexOperation.php b/lib/public/WorkflowEngine/IComplexOperation.php new file mode 100644 index 00000000000..63a4ca2460a --- /dev/null +++ b/lib/public/WorkflowEngine/IComplexOperation.php @@ -0,0 +1,56 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +/** + * Interface IComplexOperation + * + * This interface represents an operator that is less generic and indicates + * that some of the tasks it does itself instead of relying on the engine. + * This includes: + * + * * registering listeners – the implementing app needs to ensure that the + * business logic registers listeners to the events it listens to. For example + * when direct storage access is required, adding a wrapper or listening to + * a specific one is required over usual file events. + * + * @package OCP\WorkflowEngine + * + * @since 18.0.0 + */ +interface IComplexOperation extends IOperation { + + /** + * As IComplexOperation chooses the triggering events itself, a hint has + * to be shown to the user so make clear when this operation is becoming + * active. This method returns such a translated string. + * + * Example: "When a file is accessed" (en) + * + * @since 18.0.0 + */ + public function getTriggerHint(): string; + +} diff --git a/lib/public/WorkflowEngine/IEntity.php b/lib/public/WorkflowEngine/IEntity.php new file mode 100644 index 00000000000..c08e9072a38 --- /dev/null +++ b/lib/public/WorkflowEngine/IEntity.php @@ -0,0 +1,77 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Interface IEntity + * + * This interface represents an entity that supports events the workflow engine + * can listen to. For example a file with the create, update, etc. events. + * + * Ensure to listen to 'OCP/WorkflowEngine::loadEntities' for registering your + * entities. + * + * @package OCP\WorkflowEngine + * @since 18.0.0 + */ +interface IEntity { + + /** + * returns a translated name to be presented in the web interface. + * + * Example: "File" (en), "Dosiero" (eo) + * + * @since 18.0.0 + */ + public function getName(): string; + + /** + * returns the URL to the icon of the entity for display in the web interface. + * + * Usually, the implementation would utilize the `imagePath()` method of the + * `\OCP\IURLGenerator` instance and simply return its result. + * + * Example implementation: return $this->urlGenerator->imagePath('myApp', 'cat.svg'); + * + * @since 18.0.0 + */ + public function getIcon(): string; + + /** + * returns a list of supported events + * + * @return IEntityEvent[] + * @since 18.0.0 + */ + public function getEvents(): array; + + /** + * @since 18.0.0 + */ + public function prepareRuleMatcher(IRuleMatcher $ruleMatcher, string $eventName, GenericEvent $event): void; + +} diff --git a/lib/public/WorkflowEngine/IEntityCheck.php b/lib/public/WorkflowEngine/IEntityCheck.php new file mode 100644 index 00000000000..7a4df0afd5f --- /dev/null +++ b/lib/public/WorkflowEngine/IEntityCheck.php @@ -0,0 +1,54 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + + +use OCP\Files\Storage\IStorage; + +/** + * Interface IFileCheck + * + * @package OCP\WorkflowEngine + * @since 18.0.0 + */ +interface IEntityCheck { + /** + * Equips the check with a subject fitting the Entity. For instance, an + * entity of File will receive an instance of OCP\Files\Node, or a comment + * entity might get an IComment. + * + * The implementing check must be aware of the incoming type. + * + * If an unsupported subject is passed the implementation MAY throw an + * \UnexpectedValueException. + * + * @param IEntity $entity + * @param mixed $subject + * @throws \UnexpectedValueException + * @since 18.0.0 + */ + public function setEntitySubject(IEntity $entity, $subject): void; + +} diff --git a/lib/public/WorkflowEngine/IEntityEvent.php b/lib/public/WorkflowEngine/IEntityEvent.php new file mode 100644 index 00000000000..8baa0573fa8 --- /dev/null +++ b/lib/public/WorkflowEngine/IEntityEvent.php @@ -0,0 +1,54 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +/** + * Interface IEntityEvent + * + * represents an entitiy event that is dispatched via EventDispatcher + * + * @package OCP\WorkflowEngine + * + * @since 18.0.0 + */ +interface IEntityEvent { + /** + * returns a translated name to be presented in the web interface. + * + * Example: "created" (en), "kreita" (eo) + * + * @since 18.0.0 + */ + public function getDisplayName(): string; + + /** + * returns the event name that is emitted by the EventDispatcher, e.g.: + * + * Example: "OCA\MyApp\Factory\Cats::postCreated" + * + * @since 18.0.0 + */ + public function getEventName(): string; +} diff --git a/lib/public/WorkflowEngine/IFileCheck.php b/lib/public/WorkflowEngine/IFileCheck.php new file mode 100644 index 00000000000..557ba0f3c10 --- /dev/null +++ b/lib/public/WorkflowEngine/IFileCheck.php @@ -0,0 +1,42 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + + +use OCP\Files\Storage\IStorage; + +/** + * Interface IFileCheck + * + * @package OCP\WorkflowEngine + * @since 18.0.0 + */ +interface IFileCheck extends IEntityCheck { + /** + * @since 18.0.0 + */ + public function setFileInfo(IStorage $storage, string $path); + +} diff --git a/lib/public/WorkflowEngine/IManager.php b/lib/public/WorkflowEngine/IManager.php index cd323a816f3..78fd718ec9e 100644 --- a/lib/public/WorkflowEngine/IManager.php +++ b/lib/public/WorkflowEngine/IManager.php @@ -23,9 +23,6 @@ namespace OCP\WorkflowEngine; - -use OCP\Files\Storage\IStorage; - /** * Interface IManager * @@ -33,18 +30,40 @@ use OCP\Files\Storage\IStorage; * @since 9.1 */ interface IManager { + + const SCOPE_ADMIN = 0; + const SCOPE_USER = 1; + + const EVENT_NAME_REG_OPERATION = 'OCP\WorkflowEngine::registerOperations'; + const EVENT_NAME_REG_ENTITY = 'OCP\WorkflowEngine::registerEntities'; + const EVENT_NAME_REG_CHECK = 'OCP\WorkflowEngine::registerChecks'; + + /** + * Listen to `\OCP\WorkflowEngine::EVENT_NAME_REG_ENTITY` at the + * EventDispatcher for registering your entities. + * + * @since 18.0.0 + */ + public function registerEntity(IEntity $entity): void; + + /** + * Listen to `\OCP\WorkflowEngine::EVENT_NAME_REG_OPERATION` at the + * EventDispatcher for registering your operators. + * + * @since 18.0.0 + */ + public function registerOperation(IOperation $operator): void; + /** - * @param IStorage $storage - * @param string $path - * @since 9.1 + * Listen to `\OCP\WorkflowEngine::EVENT_NAME_REG_CHECK` at the + * EventDispatcher for registering your operators. + * + * @since 18.0.0 */ - public function setFileInfo(IStorage $storage, $path); + public function registerCheck(ICheck $check): void; /** - * @param string $class - * @param bool $returnFirstMatchingOperationOnly - * @return array - * @since 9.1 + * @since 18.0.0 */ - public function getMatchingOperations($class, $returnFirstMatchingOperationOnly = true); + public function getRuleMatcher(): IRuleMatcher; } diff --git a/lib/public/WorkflowEngine/IOperation.php b/lib/public/WorkflowEngine/IOperation.php index 491a805909c..d16fd618a84 100644 --- a/lib/public/WorkflowEngine/IOperation.php +++ b/lib/public/WorkflowEngine/IOperation.php @@ -23,6 +23,8 @@ namespace OCP\WorkflowEngine; +use Symfony\Component\EventDispatcher\GenericEvent; + /** * Interface IOperation * @@ -31,11 +33,71 @@ namespace OCP\WorkflowEngine; */ interface IOperation { /** - * @param string $name - * @param array[] $checks - * @param string $operation + * returns a translated name to be presented in the web interface + * + * Example: "Automated tagging" (en), "Aŭtomata etikedado" (eo) + * + * @since 18.0.0 + */ + public function getDisplayName(): string; + + /** + * returns a translated, descriptive text to be presented in the web interface. + * + * It should be short and precise. + * + * Example: "Tag based automatic deletion of files after a given time." (en) + * + * @since 18.0.0 + */ + public function getDescription(): string; + + /** + * returns the URL to the icon of the operator for display in the web interface. + * + * Usually, the implementation would utilize the `imagePath()` method of the + * `\OCP\IURLGenerator` instance and simply return its result. + * + * Example implementation: return $this->urlGenerator->imagePath('myApp', 'cat.svg'); + * + * @since 18.0.0 + */ + public function getIcon(): string; + + /** + * returns whether the operation can be used in the requested scope. + * + * Scope IDs are defined as constants in OCP\WorkflowEngine\IManager. At + * time of writing these are SCOPE_ADMIN and SCOPE_USER. + * + * For possibly unknown future scopes the recommended behaviour is: if + * user scope is permitted, the default behaviour should return `true`, + * otherwise `false`. + * + * @since 18.0.0 + */ + public function isAvailableForScope(int $scope): bool; + + /** + * Validates whether a configured workflow rule is valid. If it is not, + * an `\UnexpectedValueException` is supposed to be thrown. + * * @throws \UnexpectedValueException * @since 9.1 */ - public function validateOperation($name, array $checks, $operation); + public function validateOperation(string $name, array $checks, string $operation): void; + + /** + * Is being called by the workflow engine when an event was triggered that + * is configured for this operation. An evaluation whether the event + * qualifies for this operation to run has still to be done by the + * implementor by calling the RuleMatchers getMatchingOperations method + * and evaluating the results. + * + * If the implementor is an IComplexOperation, this method will not be + * called automatically. It can be used or left as no-op by the implementor. + * + * @since 18.0.0 + */ + public function onEvent(string $eventName, GenericEvent $event, IRuleMatcher $ruleMatcher): void; } diff --git a/lib/public/WorkflowEngine/IRuleMatcher.php b/lib/public/WorkflowEngine/IRuleMatcher.php new file mode 100644 index 00000000000..5569800edb7 --- /dev/null +++ b/lib/public/WorkflowEngine/IRuleMatcher.php @@ -0,0 +1,39 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +/** + * Class IRuleMatcher + * + * @package OCP\WorkflowEngine + * + * @since 18.0.0 + */ +interface IRuleMatcher extends IFileCheck { + /** + * @since 18.0.0 + */ + public function getMatchingOperations(string $class, bool $returnFirstMatchingOperationOnly = true): array; +} diff --git a/lib/public/WorkflowEngine/ISpecificOperation.php b/lib/public/WorkflowEngine/ISpecificOperation.php new file mode 100644 index 00000000000..0b26770a13a --- /dev/null +++ b/lib/public/WorkflowEngine/ISpecificOperation.php @@ -0,0 +1,50 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\WorkflowEngine; + +/** + * Interface ISpecificOperation + * + * This interface represents an operator that is designed to work with exactly + * one entity type. + * + * In almost all of the cases it is not necessary to have this limitation, + * because the action is not connected to the event. This mechanism suits + * special cases. + * + * @package OCP\WorkflowEngine + * @since 18.0.0 + */ +interface ISpecificOperation extends IOperation { + + /** + * returns the id of the entity the operator is designed for + * + * Example: 'WorkflowEngine_Entity_File' + * + * @since 18.0.0 + */ + public function getEntityId():string; +} |