Skip to content

Commit 9fe9d44

Browse files
authored
Merge pull request #81 from nextcloud/fix/noid/substitute-n
fix substitution of n placeholder
2 parents 93eee61 + df86415 commit 9fe9d44

File tree

2 files changed

+153
-17
lines changed

2 files changed

+153
-17
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2021 Arthur Schiwon <[email protected]>
6+
*
7+
* @author Arthur Schiwon <[email protected]>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*
24+
*/
25+
26+
namespace OCA\WorkflowScript\Exception;
27+
28+
use Exception;
29+
use Throwable;
30+
31+
class PlaceholderNotSubstituted extends Exception {
32+
33+
/** @var string */
34+
private $placeholder;
35+
36+
public function __construct(string $placeholder, Throwable $previous = null) {
37+
parent::__construct('', 0, $previous);
38+
$this->placeholder = $placeholder;
39+
}
40+
41+
public function getPlaceholder(): string {
42+
return $this->placeholder;
43+
}
44+
}

lib/Operation.php

Lines changed: 109 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@
2626
use Exception;
2727
use InvalidArgumentException;
2828
use OC;
29-
use OC\Files\Filesystem;
3029
use OC\Files\View;
30+
use OC\User\NoUserException;
31+
use OCA\Files_Sharing\SharedStorage;
32+
use OCA\GroupFolders\Mount\GroupFolderStorage;
3133
use OCA\WorkflowEngine\Entity\File;
3234
use OCA\WorkflowScript\BackgroundJobs\Launcher;
35+
use OCA\WorkflowScript\Exception\PlaceholderNotSubstituted;
3336
use OCP\BackgroundJob\IJobList;
3437
use OCP\EventDispatcher\Event;
3538
use OCP\EventDispatcher\GenericEvent;
@@ -38,6 +41,7 @@
3841
use OCP\Files\IRootFolder;
3942
use OCP\Files\Node;
4043
use OCP\Files\NotFoundException;
44+
use OCP\Files\NotPermittedException;
4145
use OCP\IConfig;
4246
use OCP\IL10N;
4347
use OCP\IUser;
@@ -46,6 +50,7 @@
4650
use OCP\WorkflowEngine\IManager;
4751
use OCP\WorkflowEngine\IRuleMatcher;
4852
use OCP\WorkflowEngine\ISpecificOperation;
53+
use Psr\Log\LoggerInterface;
4954
use Symfony\Component\EventDispatcher\GenericEvent as LegacyGenericEvent;
5055
use UnexpectedValueException;
5156

@@ -63,14 +68,25 @@ class Operation implements ISpecificOperation {
6368
private $rootFolder;
6469
/** @var IConfig */
6570
private $config;
71+
/** @var LoggerInterface */
72+
private $logger;
6673

67-
public function __construct(IManager $workflowEngineManager, IJobList $jobList, IL10N $l, IUserSession $session, IRootFolder $rootFolder, IConfig $config) {
74+
public function __construct(
75+
IManager $workflowEngineManager,
76+
IJobList $jobList,
77+
IL10N $l,
78+
IUserSession $session,
79+
IRootFolder $rootFolder,
80+
IConfig $config,
81+
LoggerInterface $logger
82+
) {
6883
$this->workflowEngineManager = $workflowEngineManager;
6984
$this->jobList = $jobList;
7085
$this->l = $l;
7186
$this->session = $session;
7287
$this->rootFolder = $rootFolder;
7388
$this->config = $config;
89+
$this->logger = $logger;
7490
}
7591

7692
/**
@@ -148,7 +164,20 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch
148164

149165
$matches = $ruleMatcher->getFlows(false);
150166
foreach ($matches as $match) {
151-
$command = $this->buildCommand($match['operation'], $node, $eventName, $extra);
167+
try {
168+
$command = $this->buildCommand($match['operation'], $node, $eventName, $extra);
169+
} catch (PlaceholderNotSubstituted $e) {
170+
$this->logger->warning(
171+
'Could not substitute {placeholder} in {command} with node {node}',
172+
[
173+
'app' => 'workflow_script',
174+
'placeholder' => $e->getPlaceholder(),
175+
'command' => $match['operation'],
176+
'node' => $node,
177+
'exception' => $e,
178+
]
179+
);
180+
}
152181
$args = ['command' => $command];
153182
if (strpos($command, '%f')) {
154183
$args['path'] = $node->getPath();
@@ -159,6 +188,9 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch
159188
}
160189
}
161190

191+
/**
192+
* @throws PlaceholderNotSubstituted
193+
*/
162194
protected function buildCommand(string $template, Node $node, string $event, array $extra = []) {
163195
$command = $template;
164196

@@ -168,20 +200,9 @@ protected function buildCommand(string $template, Node $node, string $event, arr
168200

169201
if (strpos($command, '%n')) {
170202
// Nextcloud relative-path
171-
$nodeID = -1;
172-
try {
173-
$nodeID = $node->getId();
174-
} catch (InvalidPathException | NotFoundException $e) {
175-
throw new InvalidArgumentException('', 0, $e);
176-
}
177-
178-
$base_path = $this->config->getSystemValue('datadirectory');
179-
try {
180-
$path = Filesystem::getLocalFile(Filesystem::getPath($nodeID));
181-
} catch (NotFoundException $e) {
182-
throw new InvalidArgumentException('', 0, $e);
183-
}
184-
$command = str_replace('%n', escapeshellarg(str_replace($base_path . '/', '', $path)), $command);
203+
$ncRelPath = $this->replacePlaceholderN($node);
204+
$command = str_replace('%n', escapeshellarg($ncRelPath), $command);
205+
unset($ncRelPath);
185206
}
186207

187208
if (strpos($command, '%f')) {
@@ -238,6 +259,77 @@ protected function buildCommand(string $template, Node $node, string $event, arr
238259
return $command;
239260
}
240261

262+
/**
263+
* @throws PlaceholderNotSubstituted
264+
*/
265+
protected function replacePlaceholderN(Node $node): string {
266+
$owner = $node->getOwner();
267+
try {
268+
$nodeID = $node->getId();
269+
$storage = $node->getStorage();
270+
} catch (NotFoundException | InvalidPathException $e) {
271+
$context = [
272+
'app' => 'workflow_script',
273+
'exception' => $e,
274+
'node' => $node,
275+
];
276+
$message = 'Could not get if of node {node}';
277+
if (isset($nodeID)) {
278+
$message = 'Could not find storage for file ID {fid}, node: {node}';
279+
$context['fid'] = $nodeID;
280+
}
281+
282+
$this->logger->warning($message, $context);
283+
throw new PlaceholderNotSubstituted('n', $e);
284+
}
285+
286+
if (isset($storage) && $storage->instanceOfStorage(GroupFolderStorage::class)) {
287+
// group folders are always located within $DATADIR/__groupfolders/
288+
$absPath = $storage->getLocalFile($node->getPath());
289+
$pos = strpos($absPath, '/__groupfolders/');
290+
// if the string cannot be found, the fallback is absolute path
291+
// it should never happen #famousLastWords
292+
if ($pos === false) {
293+
$this->logger->warning(
294+
'Groupfolder path does not contain __groupfolders. File ID: {fid}, Node path: {path}, absolute path: {abspath}',
295+
[
296+
'app' => 'workflow_script',
297+
'fid' => $nodeID,
298+
'path' => $node->getPath(),
299+
'abspath' => $absPath,
300+
]
301+
);
302+
}
303+
$ncRelPath = substr($absPath, (int)$pos);
304+
} elseif (isset($storage) && $storage->instanceOfStorage(SharedStorage::class)) {
305+
try {
306+
$folder = $this->rootFolder->getUserFolder($owner->getUID());
307+
} catch (NotPermittedException | NoUserException $e) {
308+
throw new PlaceholderNotSubstituted('n', $e);
309+
}
310+
$nodes = $folder->getById($nodeID);
311+
if (empty($nodes)) {
312+
throw new PlaceholderNotSubstituted('n');
313+
}
314+
$newNode = array_shift($nodes);
315+
$ncRelPath = $newNode->getPath();
316+
} else {
317+
$ncRelPath = $node->getPath();
318+
if (strpos($node->getPath(), $owner->getUID()) !== 0) {
319+
$nodes = $this->rootFolder->getById($nodeID);
320+
foreach ($nodes as $testNode) {
321+
if (strpos($node->getPath(), $owner->getUID()) === 0) {
322+
$ncRelPath = $testNode;
323+
break;
324+
}
325+
}
326+
}
327+
}
328+
$ncRelPath = ltrim($ncRelPath, '/');
329+
330+
return $ncRelPath;
331+
}
332+
241333
public function getEntityId(): string {
242334
return File::class;
243335
}

0 commit comments

Comments
 (0)