diff --git a/appinfo/info.xml b/appinfo/info.xml index b936471..de5c977 100755 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -43,6 +43,7 @@ Allows administrators to write small scripts which users can run through the fil OCA\FilesScripts\Command\ListScripts OCA\FilesScripts\Command\ImportScripts OCA\FilesScripts\Command\ExportScripts + OCA\FilesScripts\Command\Interactive OCA\FilesScripts\Settings\AdminSettings diff --git a/docs/Admin.md b/docs/Admin.md index 82ebf65..9de42af 100644 --- a/docs/Admin.md +++ b/docs/Admin.md @@ -88,3 +88,16 @@ To get the `` value, you can use the user-list command: ```sh occ user:list ``` + +## Interactive (REPL) shell +You can start an interactive shell with the `files_scripts:interactive` command +```sh +occ files_scripts:interactive +``` + +Multi-line commands can be given by ending the line with a backslash character: +``` +> print("hello \ +> world") +``` +You can type the `exit` command to exit the shell. diff --git a/lib/Command/Interactive.php b/lib/Command/Interactive.php new file mode 100644 index 0000000..22ba9ee --- /dev/null +++ b/lib/Command/Interactive.php @@ -0,0 +1,90 @@ +rootFolder = $rootFolder; + $this->luaProvider = $luaProvider; + $this->interpreter = $interpreter; + } + protected function configure(): void { + $this->setDescription('Starts an interactive Lua shell where you can interact with the server using the scripting API') + ->addOption('user', 'u', InputOption::VALUE_OPTIONAL, 'User as which the action should be run') + ->addOption('file', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'File path or id of a file given to the action as input file'); + + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $userId = $input->getOption('user'); + $rootFolder = $this->rootFolder; + + try { + if ($userId) { + $rootFolder = $this->rootFolder->getUserFolder($userId); + } + $files = RunScript::getFilesForCommand($input, $output, $rootFolder); + } catch (\Throwable $e) { + $output->writeln('' . $e->getMessage() .''); + return 1; + } + + $output->writeln('Lua files_scripts interpreter started...'); + $output->writeln('To stop type "exit"'); + + $context = new Context($this->luaProvider->createLua(), $rootFolder, [], $files); + $f = fopen( 'php://stdin', 'r' ); + $command = ""; + while (true) { + echo "> "; + $line = fgets( $f ); + + // Handle exit clause + if (trim($line) == "exit") { + fclose($f); + break; + } + + $replacements = 0; + $line = preg_replace('/(.*)\\\\(\s*)$/i', '$1 $2', $line, 1, $replacements); + $command .= $line; + + // if line does not end with `\` backslash we execute the command + if ($replacements == 0) { + $this->executeCommand($command, $context, $output); + $command = ""; + } + } + + return 0; + } + + private function executeCommand(string $command, Context $context, OutputInterface $output): void { + $script = new Script(); + $script->setProgram($command); + try { + $this->interpreter->execute($script, $context); + } catch (\Throwable $e) { + $output->writeln('' . $e->getMessage() . ''); + } + } +} diff --git a/lib/Command/RunScript.php b/lib/Command/RunScript.php index 5876833..e639334 100644 --- a/lib/Command/RunScript.php +++ b/lib/Command/RunScript.php @@ -1,12 +1,14 @@ getArgument('id'); - $userId = $input->getOption('user'); - $scriptInputsJson = $input->getOption('inputs') ?? '{}'; + public static function getFilesForCommand(InputInterface $input, OutputInterface $output, Folder $rootFolder) { $fileInputs = $input->getOption('file') ?? []; - - try { - $scriptInputsData = json_decode($scriptInputsJson, true, 512, JSON_THROW_ON_ERROR); - } catch (\JsonException $err) { - $output->writeln('Could not parse the inputs JSON'); - return 1; - } - - $script = $this->scriptMapper->find($scriptId); - $output->writeln('Executing file action: ' . $script->getTitle() . ''); - - $scriptInputs = $this->scriptInputMapper->findAllByScriptId($scriptId); - foreach ($scriptInputs as $scriptInput) { - $value = $scriptInputsData[$scriptInput->getName()] ?? null; - $scriptInput->setValue($value); - } - - $rootFolder = $this->rootFolder->getUserFolder($userId); $files = []; $n = 1; @@ -77,22 +58,56 @@ protected function execute(InputInterface $input, OutputInterface $output) { if (ctype_digit(strval($fileInput))) { $nodes = $rootFolder->getById(intval($fileInput)); if (!isset($nodes[0])) { - $output->writeln('Could not find input file ' . $fileInput . ' belonging in root folder ' . $rootFolder->getPath() . ' for file action'); - return 1; + throw new Error('Could not find input file ' . $fileInput . ' belonging in root folder ' . $rootFolder->getPath() . ' for file action'); } $file = $nodes[0]; unset($nodes); } else { $file = $rootFolder->get($fileInput); } - } catch (\Exception $e) { - $output->writeln('Could not find input file ' . $fileInput . ' belonging in root folder ' . $rootFolder->getPath() . ' for file action'); - return 1; + } catch (\Throwable $e) { + throw $e; } $files[$n++] = $file; } } + return $files; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $scriptId = $input->getArgument('id'); + $userId = $input->getOption('user'); + $scriptInputsJson = $input->getOption('inputs') ?? '{}'; + + try { + $scriptInputsData = json_decode($scriptInputsJson, true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $err) { + $output->writeln('Could not parse the inputs JSON'); + return 1; + } + + $script = $this->scriptMapper->find($scriptId); + $output->writeln('Executing file action: ' . $script->getTitle() . ''); + + $scriptInputs = $this->scriptInputMapper->findAllByScriptId($scriptId) ?? []; + foreach ($scriptInputs as $scriptInput) { + $value = $scriptInputsData[$scriptInput->getName()] ?? null; + $scriptInput->setValue($value); + } + + $rootFolder = $this->rootFolder; + try { + if ($userId) { + $rootFolder = $this->rootFolder->getUserFolder($userId); + } + $files = self::getFilesForCommand($input, $output, $rootFolder); + } catch (\Throwable $e) { + $output->writeln('' . $e->getMessage() .''); + return 1; + } + + print(gettype($scriptInputs)); $context = new Context( $this->luaProvider->createLua(), $rootFolder, diff --git a/lib/Interpreter/RegistrableFunction.php b/lib/Interpreter/RegistrableFunction.php index 5c94fb0..cf060c2 100755 --- a/lib/Interpreter/RegistrableFunction.php +++ b/lib/Interpreter/RegistrableFunction.php @@ -52,7 +52,7 @@ final protected function getHomeFolder(): Folder { return $folder; } - final protected function getPath(array $data): string { + final protected function getPath(?array $data): string { return ($data['path'] ?? '') . '/' . ($data['name'] ?? ''); }