diff --git a/README.md b/README.md index 07d4347..1ecbc97 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ When defining the script, you can specify one of the following placeholders that %i | file id | *142430* %a | actor's user id | *bob* %o | owner's user id | *alice* +%r | approval requester's user id (requires the [approval app](https://github.com/nextcloud/approval), only substituted when the flow is triggered by an approval state change; if no requester is known, the command is not executed and a warning is logged) | *alice* %n | nextcloud-relative path | *alice/files/Pictures/Wonderland/20180717_192103.jpg* %f | locally available file | */tmp/oc_tmp_m6E6OO-.jpg* %x | old nextcloud-relative file path (only on *rename* and *copy*) | *alice/files/Workbench/20180717_192103.jpg* diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index bcd2356..a29f230 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -7,6 +7,8 @@ namespace OCA\WorkflowScript\AppInfo; +use OCA\Approval\Events\ApprovalStateChangedEvent; +use OCA\WorkflowScript\Listener\ApprovalStateChangedListener; use OCA\WorkflowScript\Listener\RegisterFlowOperationsListener; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; @@ -30,6 +32,8 @@ public function __construct() { #[\Override] public function register(IRegistrationContext $context): void { $context->registerEventListener(RegisterOperationsEvent::class, RegisterFlowOperationsListener::class); + // the approval app is optional; if it is absent, its event is simply never emitted + $context->registerEventListener(ApprovalStateChangedEvent::class, ApprovalStateChangedListener::class); } #[\Override] diff --git a/lib/Listener/ApprovalStateChangedListener.php b/lib/Listener/ApprovalStateChangedListener.php new file mode 100644 index 0000000..04ab4ff --- /dev/null +++ b/lib/Listener/ApprovalStateChangedListener.php @@ -0,0 +1,50 @@ + + * @psalm-api + */ +class ApprovalStateChangedListener implements IEventListener { + public function __construct( + private RequesterContext $requesterContext, + ) { + } + + /** + * @inheritDoc + */ + #[\Override] + public function handle(Event $event): void { + if (!$event instanceof ApprovalStateChangedEvent) { + return; + } + + $requesterId = $event->getRequesterUserId(); + if ($requesterId === null || $requesterId === '') { + return; + } + + $this->requesterContext->setRequester($event->getFileId(), $requesterId); + } +} diff --git a/lib/Operation.php b/lib/Operation.php index 81f8957..54506a1 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -46,6 +46,7 @@ public function __construct( private IRootFolder $rootFolder, private LoggerInterface $logger, private IURLGenerator $urlGenerator, + private RequesterContext $requesterContext, ) { } @@ -211,6 +212,20 @@ protected function buildCommand(string $template, Node $node, string $event, arr $command = str_replace('%o', escapeshellarg($userID), $command); } + if (strpos($command, '%r')) { + // filled by ApprovalStateChangedListener when the flow was + // triggered by an approval state change of the approval app + $requesterID = null; + try { + $requesterID = $this->requesterContext->getRequester($node->getId()); + } catch (InvalidPathException|NotFoundException) { + } + if ($requesterID === null) { + throw new PlaceholderNotSubstituted('r'); + } + $command = str_replace('%r', escapeshellarg($requesterID), $command); + } + if (strpos($command, '%x')) { if (!isset($extra['oldFilePath'])) { $extra['oldFilePath'] = ''; diff --git a/lib/RequesterContext.php b/lib/RequesterContext.php new file mode 100644 index 0000000..dd366c9 --- /dev/null +++ b/lib/RequesterContext.php @@ -0,0 +1,31 @@ + */ + private array $requesters = []; + + public function setRequester(int $fileId, string $requesterId): void { + $this->requesters[$fileId] = $requesterId; + } + + public function getRequester(int $fileId): ?string { + return $this->requesters[$fileId] ?? null; + } +} diff --git a/psalm.xml b/psalm.xml index c178bf5..d0be197 100644 --- a/psalm.xml +++ b/psalm.xml @@ -18,5 +18,6 @@ + diff --git a/tests/stubs/oca_approval_events_approvalstatechangedevent.php b/tests/stubs/oca_approval_events_approvalstatechangedevent.php new file mode 100644 index 0000000..07f8604 --- /dev/null +++ b/tests/stubs/oca_approval_events_approvalstatechangedevent.php @@ -0,0 +1,46 @@ +fileId; + } + + public function getRuleId(): int { + return $this->ruleId; + } + + public function getNewState(): int { + return $this->newState; + } + + public function getActorUserId(): ?string { + return $this->actorUserId; + } + + public function getRequesterUserId(): ?string { + return $this->requesterUserId; + } + } +}