2626use Exception ;
2727use InvalidArgumentException ;
2828use OC ;
29- use OC \Files \Filesystem ;
3029use OC \Files \View ;
30+ use OC \User \NoUserException ;
31+ use OCA \Files_Sharing \SharedStorage ;
32+ use OCA \GroupFolders \Mount \GroupFolderStorage ;
3133use OCA \WorkflowEngine \Entity \File ;
3234use OCA \WorkflowScript \BackgroundJobs \Launcher ;
35+ use OCA \WorkflowScript \Exception \PlaceholderNotSubstituted ;
3336use OCP \BackgroundJob \IJobList ;
3437use OCP \EventDispatcher \Event ;
3538use OCP \EventDispatcher \GenericEvent ;
3841use OCP \Files \IRootFolder ;
3942use OCP \Files \Node ;
4043use OCP \Files \NotFoundException ;
44+ use OCP \Files \NotPermittedException ;
4145use OCP \IConfig ;
4246use OCP \IL10N ;
4347use OCP \IUser ;
4650use OCP \WorkflowEngine \IManager ;
4751use OCP \WorkflowEngine \IRuleMatcher ;
4852use OCP \WorkflowEngine \ISpecificOperation ;
53+ use Psr \Log \LoggerInterface ;
4954use Symfony \Component \EventDispatcher \GenericEvent as LegacyGenericEvent ;
5055use 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