From f440a450403edcdc88010a775fdb83598adb5b78 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:34:36 +0100 Subject: [PATCH 1/9] NEW AbstractServerInstaller --- .../AppInstallers/AbstractServerInstaller.php | 112 ++++++++++++++++++ .../AppInstallers/ApacheServerInstaller.php | 109 +++++++++++++++++ .../AppInstallers/IISServerInstaller.php | 108 ++++++----------- CoreApp.php | 88 ++++---------- 4 files changed, 274 insertions(+), 143 deletions(-) create mode 100644 CommonLogic/AppInstallers/AbstractServerInstaller.php create mode 100644 CommonLogic/AppInstallers/ApacheServerInstaller.php diff --git a/CommonLogic/AppInstallers/AbstractServerInstaller.php b/CommonLogic/AppInstallers/AbstractServerInstaller.php new file mode 100644 index 000000000..602ec5580 --- /dev/null +++ b/CommonLogic/AppInstallers/AbstractServerInstaller.php @@ -0,0 +1,112 @@ +getSelectorInstalling())) + ->setFilePath(Filemanager::pathJoin([ + $this->getWorkbench()->getInstallationPath(), + $this->getConfigFileName() + ])) + ->setFileTemplatePath($this->getConfigTemplatePathRelative()) + ->setMarkerBegin("\n{$this->stringToComment('BEGIN [#marker#]')}") + ->setMarkerEnd($this->stringToComment('END [#marker#]')); + + $this->configInstaller = $configInstaller; + } + + /** + * Returns the filename for the config file created by this installer. + * For apache this would be `.htaccess` and for IIS it would be `Web.config`. + * + * @return string + */ + protected abstract function getConfigFileName() : string; + + /** + * Returns the filepath for the config template relative to the CoreApp folder. + * This would usually be just the name of the template, e.g. `default.Web.config`. + * + * @return string + */ + protected abstract function getConfigTemplatePathRelative() : string; + + /** + * Turns a string into a config comment. + * + * @param string $markerText + * @return string + */ + protected abstract function stringToComment(string $markerText) : string; + + /** + * + * {@inheritDoc} + * @see \exface\Core\Interfaces\InstallerInterface::backup() + */ + public function backup(string $absolute_path) : \Iterator + { + yield from $this->configInstaller->backup($absolute_path); + } + + /** + * + * {@inheritDoc} + * @see \exface\Core\Interfaces\InstallerInterface::uninstall() + */ + public function uninstall() : \Iterator + { + yield from $this->configInstaller->uninstall(); + } + + /** + * + * {@inheritDoc} + * @see \exface\Core\Interfaces\InstallerInterface::install() + */ + public function install(string $source_absolute_path): \Iterator + { + $indentOuter = $this->getOutputIndentation(); + $indent = $indentOuter . $indentOuter; + $serverType = ServerSoftwareDataType::getServerSoftwareFamily() ?? 'UNKNOWN SERVER SOFTWARE'; + $serverVersion = ServerSoftwareDataType::getServerSoftwareVersion() ?? 'UNKNOWN VERSION'; + + yield $indentOuter . "Server configuration for {$serverType} {$serverVersion}:" . PHP_EOL; + + $this->configInstaller->setOutputIndentation($indent); + yield $indent . "Using \"{$this->getConfigTemplatePathRelative()}\" template for {$serverType}." . PHP_EOL; + yield from $this->configInstaller->install($source_absolute_path); + $this->configInstaller->setOutputIndentation($indentOuter); + } + + /** + * + * {@inheritDoc} + * @see \exface\Core\CommonLogic\AppInstallers\AbstractAppInstaller::setOutputIndentation() + */ + public function setOutputIndentation(string $value) : AbstractAppInstaller + { + $this->configInstaller->setOutputIndentation($value); + return parent::setOutputIndentation($value); + } +} \ No newline at end of file diff --git a/CommonLogic/AppInstallers/ApacheServerInstaller.php b/CommonLogic/AppInstallers/ApacheServerInstaller.php new file mode 100644 index 000000000..4d5af17b0 --- /dev/null +++ b/CommonLogic/AppInstallers/ApacheServerInstaller.php @@ -0,0 +1,109 @@ +configInstaller + ->addContent('Core URLs', $this->getContentCoreUrls()) + ->addContent('Core Security', $this->getContentSecurityRules()) + ->addContent("zlib compression OFF for WebConsoleFacade", " + + php_flag zlib.output_compression Off + + +");; + } + + /** + * Returns a string containing config options for core URLs. + * + * Edit the return value if you want to change the core URL rules. + * + * @return string + */ + protected function getContentCoreUrls() : string + { + return " + +# API requests +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^api/.*$ vendor/exface/core/index.php [L,QSA,NC] + +# Force trailing slash on requests to the root folder of the workbench +# E.g. me.com/exface -> me.com/exface/ +RewriteCond %{REQUEST_URI} ^$ +RewriteRule ^$ %{REQUEST_URI} [R=301] + +# index request without any path +RewriteRule ^/?$ vendor/exface/core/index.php [L,QSA] + +# Requests to UI pages +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^[^/]*$ vendor/exface/core/index.php [L,QSA] + +"; + } + + /** + * Returns a string containing config options for security rules. + * + * Edit the return value if you want to change the core security rules. + * + * @return string + */ + protected function getContentSecurityRules() : string + { + return " + +# Block direct access to PHP scripts +RewriteCond %{REQUEST_FILENAME} -f +RewriteCond %{REQUEST_FILENAME} !vendor/exface/core/index.php [NC] +RewriteRule ^vendor/.*\.php$ - [F,L,NC] + +# Block requests to config, cache, backup, etc. +RewriteRule ^(config|backup|translations|logs)/.*$ - [F,NC] +# Block requests to system files (starting with a dot) in the data folder +RewriteRule ^data/\..*$ - [F,NC] + +# Block .html files. +RewriteRule ^vendor/.*\.html$ - [F,L,NC] + +# Block library docs. +RewriteRule ^vendor/.*/gh-pages.*$ - [F,L,NC] + +"; + } + + /** + * @inheritDoc + */ + protected function getConfigFileName(): string + { + return '.htaccess'; + } + + /** + * @inheritDoc + */ + protected function getConfigTemplatePathRelative(): string + { + return 'default.htaccess'; + } + + /** + * @inheritDoc + */ + protected function stringToComment(string $markerText): string + { + return "# {$markerText}"; + } +} \ No newline at end of file diff --git a/CommonLogic/AppInstallers/IISServerInstaller.php b/CommonLogic/AppInstallers/IISServerInstaller.php index 416af5f78..edba6f74f 100644 --- a/CommonLogic/AppInstallers/IISServerInstaller.php +++ b/CommonLogic/AppInstallers/IISServerInstaller.php @@ -2,90 +2,31 @@ namespace exface\Core\CommonLogic\AppInstallers; use exface\Core\DataTypes\StringDataType; -use exface\Core\Interfaces\Selectors\SelectorInterface; -use exface\Core\CommonLogic\Filemanager; use exface\Core\DataTypes\ServerSoftwareDataType; /** - * This installer takes care of file permissions, web.config an other settings required to run on Microsoft IIS. + * This installer takes care of file permissions, web.config and other settings required to run on Microsoft IIS. * * @author Ralf Mulansky * */ -class IISServerInstaller extends AbstractAppInstaller +class IISServerInstaller extends AbstractServerInstaller { - private $webConfigInstaller = null; - - private $webConfigVersion = null; - - /** - * - * @param SelectorInterface $selectorToInstall - */ - public function __construct(SelectorInterface $selectorToInstall) - { - parent::__construct($selectorToInstall); - - // Web.config for IIS servers - $iisVersion = ServerSoftwareDataType::getServerSoftwareVersion(); - switch (true) { - case $iisVersion < '10.0': - $tpl = 'default8.Web.config'; - $this->webConfigVersion = '8.5+'; - break; - default: - $tpl = 'default.Web.config'; - $this->webConfigVersion = '10+'; - break; - } - $webconfigInstaller = new FileContentInstaller($this->getSelectorInstalling()); - $webconfigInstaller - ->setFilePath(Filemanager::pathJoin([$this->getWorkbench()->getInstallationPath(), 'Web.config'])) - ->setFileTemplatePath($tpl) - ->setMarkerBegin("\n") - ->setMarkerEnd("");; - $this->webConfigInstaller = $webconfigInstaller; - } - - /** - * - * {@inheritDoc} - * @see \exface\Core\Interfaces\InstallerInterface::backup() - */ - public function backup(string $absolute_path) : \Iterator - { - yield from $this->webConfigInstaller->backup($absolute_path); - } - - /** - * - * {@inheritDoc} - * @see \exface\Core\Interfaces\InstallerInterface::uninstall() - */ - public function uninstall() : \Iterator - { - yield from $this->webConfigInstaller->uninstall(); - } - /** * * {@inheritDoc} * @see \exface\Core\Interfaces\InstallerInterface::install() */ - public function install(string $source_absolute_path): \Iterator + public function install(string $source_absolute_path): \Iterator { - $indentOuter = $this->getOutputIndentation(); - $indent = $indentOuter . $indentOuter; - - yield $indentOuter . "Server configuration for Microsoft IIS " . ServerSoftwareDataType::getServerSoftwareVersion() ?? 'UNKNOWN VERSION' . PHP_EOL; - - $this->webConfigInstaller->setOutputIndentation($indent); - yield $indent . 'Using web.config template for IIS ' . $this->webConfigVersion . PHP_EOL; - yield from $this->webConfigInstaller->install($source_absolute_path); - $this->webConfigInstaller->setOutputIndentation($indentOuter); - + yield parent::install($source_absolute_path); + $fm = $this->getWorkbench()->filemanager(); $user = 'IUSR'; + + $indentOuter = $this->getOutputIndentation(); + $indent = $indentOuter . $indentOuter; + yield $indent . $this->setPermissionsForPath($fm->getPathToDataFolder(), $user) . PHP_EOL; yield $indent . $this->setPermissionsForPath($fm->getPathToLogFolder(), $user) . PHP_EOL; yield $indent . $this->setPermissionsForPath($fm->getPathToConfigFolder(), $user) . PHP_EOL; @@ -113,15 +54,32 @@ protected function setPermissionsForPath(string $path, string $user): string } return "Permission for the user '{$user}' and folder/file '{$shortPath}' changed!"; } - + + protected function getConfigFileName(): string + { + return 'Web.config'; + } + /** - * - * {@inheritDoc} - * @see \exface\Core\CommonLogic\AppInstallers\AbstractAppInstaller::setOutputIndentation() + * @inheritDoc + */ + protected function getConfigTemplatePathRelative(): string + { + // Web.config for IIS servers + $iisVersion = ServerSoftwareDataType::getServerSoftwareVersion(); + return match (true) { + $iisVersion < '10.0' => + 'default8.Web.config', + default => + 'default.Web.config', + }; + } + + /** + * @inheritDoc */ - public function setOutputIndentation(string $value) : AbstractAppInstaller + protected function stringToComment(string $markerText): string { - $this->webConfigInstaller->setOutputIndentation($value); - return parent::setOutputIndentation($value); + return ""; } } \ No newline at end of file diff --git a/CoreApp.php b/CoreApp.php index a377d00ab..fecf683d1 100644 --- a/CoreApp.php +++ b/CoreApp.php @@ -1,6 +1,7 @@ addInstaller(new CoreInstaller($this->getSelector()), true); - - // .htaccess for Apache servers - - $htaccessInstaller = new FileContentInstaller($this->getSelector()); - $htaccessInstaller - ->setFilePath(Filemanager::pathJoin([$this->getWorkbench()->getInstallationPath(), '.htaccess'])) - ->setFileTemplatePath('default.htaccess') - ->setMarkerBegin("\n# BEGIN [#marker#]") - ->setMarkerEnd('# END [#marker#]') - ->addContent('Core URLs', " - -# API requests -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^api/.*$ vendor/exface/core/index.php [L,QSA,NC] - -# Force trailing slash on requests to the root folder of the workbench -# E.g. me.com/exface -> me.com/exface/ -RewriteCond %{REQUEST_URI} ^$ -RewriteRule ^$ %{REQUEST_URI} [R=301] - -# index request without any path -RewriteRule ^/?$ vendor/exface/core/index.php [L,QSA] - -# Requests to UI pages -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^[^/]*$ vendor/exface/core/index.php [L,QSA] - -") - ->addContent('Core Security', " -# Block direct access to PHP scripts -RewriteCond %{REQUEST_FILENAME} -f -RewriteCond %{REQUEST_FILENAME} !vendor/exface/core/index.php [NC] -RewriteRule ^vendor/.*\.php$ - [F,L,NC] - -# Block requests to config, cache, backup, etc. -RewriteRule ^(config|backup|translations|logs)/.*$ - [F,NC] -# Block requests to system files (starting with a dot) in the data folder -RewriteRule ^data/\..*$ - [F,NC] - -# Block .html files. -RewriteRule ^vendor/.*\.html$ - [F,L,NC] - -# Block library docs. -RewriteRule ^vendor/.*/gh-pages.*$ - [F,L,NC] - -"); - $installer->addInstaller($htaccessInstaller); - // robot.txt - $robotsTxtInstaller = new FileContentInstaller($this->getSelector()); $robotsTxtInstaller ->setFilePath(Filemanager::pathJoin([$this->getWorkbench()->getInstallationPath(), 'robots.txt'])) @@ -136,12 +86,6 @@ public function getInstaller(InstallerInterface $injected_installer = null) $tplInstaller = new HttpFacadeInstaller($this->getSelector()); $tplInstaller->setFacade(FacadeFactory::createFromString(WebConsoleFacade::class, $this->getWorkbench())); $installer->addInstaller($tplInstaller); - $htaccessInstaller->addContent("zlib compression off for webconsole facade\n", " - - php_flag zlib.output_compression Off - - -"); // HttpTask facade $tplInstaller = new HttpFacadeInstaller($this->getSelector()); @@ -159,17 +103,8 @@ public function getInstaller(InstallerInterface $injected_installer = null) $installer->addInstaller($tplInstaller); // Server installer (e.g. for Microsoft IIS) - $serverInstallerClass = $this->getWorkbench()->getConfig()->getOption("INSTALLER.SERVER_INSTALLER.CLASS"); - // Autodetect server installer if not set explicitly - if ($serverInstallerClass === null) { - switch (true) { - case ServerSoftwareDataType::isServerIIS(): - $serverInstallerClass = '\\' . ltrim(IISServerInstaller::class, "\\"); - break; - // TODO add installers for apache and nginx here! - } - } - if ($serverInstallerClass != null) { + $serverInstallerClass = $this->getServerInstallerClass(); + if ($serverInstallerClass !== null) { $serverInstaller = new $serverInstallerClass($this->getSelector()); $installer->addInstaller($serverInstaller); } @@ -186,6 +121,23 @@ public function getInstaller(InstallerInterface $injected_installer = null) return $installer; } + + /** + * @return string|null + */ + protected function getServerInstallerClass() : string|null + { + $serverInstallerClass = $this->getWorkbench()->getConfig()->getOption("INSTALLER.SERVER_INSTALLER.CLASS"); + + // Autodetect server installer if not set explicitly + return match (true) { + !empty($serverInstallerClass) => $serverInstallerClass, + ServerSoftwareDataType::isServerIIS() => '\\' . ltrim(IISServerInstaller::class, "\\"), + ServerSoftwareDataType::isServerNginx() => null, + ServerSoftwareDataType::isServerApache() => '\\' . ltrim(ApacheServerInstaller::class, "\\"), + default => null, + }; + } /** * The configuration of the core consists of two parts: system config and cora app config. From 8700af19aaeb8b510de0cc745630ffcf39722a36 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Wed, 26 Nov 2025 15:04:02 +0100 Subject: [PATCH 2/9] DEV FileContentsInstaller improved formatting and new configuration options --- .../AppInstallers/AbstractServerInstaller.php | 9 +- .../AppInstallers/ApacheServerInstaller.php | 29 ++---- .../AppInstallers/FileContentInstaller.php | 93 +++++++++++++++++-- .../AppInstallers/IISServerInstaller.php | 6 +- 4 files changed, 102 insertions(+), 35 deletions(-) diff --git a/CommonLogic/AppInstallers/AbstractServerInstaller.php b/CommonLogic/AppInstallers/AbstractServerInstaller.php index 602ec5580..86fcd84df 100644 --- a/CommonLogic/AppInstallers/AbstractServerInstaller.php +++ b/CommonLogic/AppInstallers/AbstractServerInstaller.php @@ -1,7 +1,6 @@ getConfigFileName() ])) ->setFileTemplatePath($this->getConfigTemplatePathRelative()) - ->setMarkerBegin("\n{$this->stringToComment('BEGIN [#marker#]')}") + ->setMarkerBegin($this->stringToComment('BEGIN [#marker#]')) ->setMarkerEnd($this->stringToComment('END [#marker#]')); $this->configInstaller = $configInstaller; @@ -54,10 +53,10 @@ protected abstract function getConfigTemplatePathRelative() : string; /** * Turns a string into a config comment. * - * @param string $markerText + * @param string $comment * @return string */ - protected abstract function stringToComment(string $markerText) : string; + protected abstract function stringToComment(string $comment) : string; /** * diff --git a/CommonLogic/AppInstallers/ApacheServerInstaller.php b/CommonLogic/AppInstallers/ApacheServerInstaller.php index 4d5af17b0..48cd4f3a1 100644 --- a/CommonLogic/AppInstallers/ApacheServerInstaller.php +++ b/CommonLogic/AppInstallers/ApacheServerInstaller.php @@ -13,14 +13,13 @@ public function __construct(SelectorInterface $selectorToInstall) $this->configInstaller ->addContent('Core URLs', $this->getContentCoreUrls()) ->addContent('Core Security', $this->getContentSecurityRules()) - ->addContent("zlib compression OFF for WebConsoleFacade", " - + ->addContent("zlib compression OFF for WebConsoleFacade", +" php_flag zlib.output_compression Off - - -");; +"); } + /** * Returns a string containing config options for core URLs. * @@ -30,9 +29,7 @@ public function __construct(SelectorInterface $selectorToInstall) */ protected function getContentCoreUrls() : string { - return " - -# API requests + return "# API requests RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^api/.*$ vendor/exface/core/index.php [L,QSA,NC] @@ -48,9 +45,7 @@ protected function getContentCoreUrls() : string # Requests to UI pages RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^[^/]*$ vendor/exface/core/index.php [L,QSA] - -"; +RewriteRule ^[^/]*$ vendor/exface/core/index.php [L,QSA]"; } /** @@ -62,9 +57,7 @@ protected function getContentCoreUrls() : string */ protected function getContentSecurityRules() : string { - return " - -# Block direct access to PHP scripts + return "# Block direct access to PHP scripts RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{REQUEST_FILENAME} !vendor/exface/core/index.php [NC] RewriteRule ^vendor/.*\.php$ - [F,L,NC] @@ -78,9 +71,7 @@ protected function getContentSecurityRules() : string RewriteRule ^vendor/.*\.html$ - [F,L,NC] # Block library docs. -RewriteRule ^vendor/.*/gh-pages.*$ - [F,L,NC] - -"; +RewriteRule ^vendor/.*/gh-pages.*$ - [F,L,NC]"; } /** @@ -102,8 +93,8 @@ protected function getConfigTemplatePathRelative(): string /** * @inheritDoc */ - protected function stringToComment(string $markerText): string + protected function stringToComment(string $comment): string { - return "# {$markerText}"; + return "# {$comment}"; } } \ No newline at end of file diff --git a/CommonLogic/AppInstallers/FileContentInstaller.php b/CommonLogic/AppInstallers/FileContentInstaller.php index f1550c39b..722059942 100644 --- a/CommonLogic/AppInstallers/FileContentInstaller.php +++ b/CommonLogic/AppInstallers/FileContentInstaller.php @@ -3,6 +3,7 @@ use exface\Core\Exceptions\Installers\InstallerRuntimeError; use exface\Core\DataTypes\StringDataType; +use exface\Core\Exceptions\InvalidArgumentException; /** * @@ -11,6 +12,31 @@ */ class FileContentInstaller extends AbstractAppInstaller { + /** + * Behavior setting for missing markers. If applied, missed markers and their contents + * will be appended to the end of the file. + * @see FileContentInstaller::setMissingMarkerBehavior() + */ + public const MISSING_MARKER_BEHAVIOR_APPEND = 'append'; + + /** + * Behavior setting for missing markers. If applied, missed markers will be ignored. + * @see FileContentInstaller::setMissingMarkerBehavior() + */ + public const MISSING_MARKER_BEHAVIOR_IGNORE = 'ignore'; + + /** + * Behavior setting for missing markers. If applied, missed markers will cause an error. + * @see FileContentInstaller::setMissingMarkerBehavior() + */ + public const MISSING_MARKER_BEHAVIOR_ERROR = 'error'; + + private array $missingMarkerBehaviorOptions = [ + self::MISSING_MARKER_BEHAVIOR_APPEND, + self::MISSING_MARKER_BEHAVIOR_IGNORE, + self::MISSING_MARKER_BEHAVIOR_ERROR + ]; + private $filePathAbsolute = null; private $fileTemplatePathAbsolute = null; @@ -19,6 +45,8 @@ class FileContentInstaller extends AbstractAppInstaller private $markerEnd = null; + private $missingMarkerBehavior = self::MISSING_MARKER_BEHAVIOR_APPEND; + /** * [ marker => content ] * @var array @@ -89,12 +117,22 @@ public function install(string $source_absolute_path) : \Iterator $changesCnt = 0; foreach ($this->contentArray as $marker => $content) { $originalContent = $fileContent; - $begin = StringDataType::replacePlaceholders($this->getMarkerBegin(), ['marker' => preg_quote($marker)]); - $end = StringDataType::replacePlaceholders($this->getMarkerEnd(), ['marker' => preg_quote($marker)]); - $pattern = '/' . $begin . '.*' . $end . '/is'; - + $begin = trim(StringDataType::replacePlaceholders($this->getMarkerBegin(), ['marker' => preg_quote($marker)])); + $end = trim(StringDataType::replacePlaceholders($this->getMarkerEnd(), ['marker' => preg_quote($marker)])); + $pattern = '/' . "\s+" . $begin . '.*' . $end . '/is'; + $newContent = "\n\n" . $begin . "\n" . $content . "\n" . $end; + if (preg_match($pattern, $fileContent)) { - $fileContent = preg_replace($pattern, $begin . $content . $end, $fileContent); + if(preg_match('/^(\t+)(?=' . $begin . ')/m', $fileContent, $indents) === 1) { + $newContent = StringDataType::indent($newContent, $indents[0]); + } + + $fileContent = preg_replace( + $pattern, + $newContent, + $fileContent + ); + if ($fileContent === null) { throw new InstallerRuntimeError($this, 'Error replacing marker "' . $marker . '" in file "' . $this->getFilePathAbsolute() . '"!'); } @@ -102,12 +140,23 @@ public function install(string $source_absolute_path) : \Iterator $changesCnt++; } } else { - $fileContent .= $begin . $content . $end; - $changesCnt++; + switch ($this->getMissingMarkerBehavior()) { + // Append. + case self::MISSING_MARKER_BEHAVIOR_APPEND: + $fileContent .= $newContent . "\n"; + $changesCnt++; + break; + // Ignore. + case self::MISSING_MARKER_BEHAVIOR_IGNORE: + break; + // Error. + default: + throw new InstallerRuntimeError($this, "Failed to find marker \"{$marker}\" in file \"{$this->getFilePathAbsolute()}\"."); + } } } + file_put_contents($this->getFilePathAbsolute(), $fileContent); - yield ' made ' . $changesCnt . ' changes.' . PHP_EOL; } @@ -220,4 +269,32 @@ public function setMarkerEnd(string $marker) : FileContentInstaller return $this; } + /** + * Returns the behavior setting for missing markers. + * Check the corresponding `MISSING_MARKER_BEHAVIOR` constants for more info. + * + * @return string + */ + public function getMissingMarkerBehavior() : string + { + return $this->missingMarkerBehavior; + } + + /** + * Set the behavior for missing markers. + * Check the corresponding `MISSING_MARKER_BEHAVIOR` constants for more info. + * + * @param string $value + * @return FileContentInstaller + */ + public function setMissingMarkerBehavior(string $value) : FileContentInstaller + { + if(!in_array($value, $this->missingMarkerBehaviorOptions)) { + $allowed = json_encode($this->missingMarkerBehaviorOptions); + throw new InvalidArgumentException('Invalid argument "' . $value . '", expected ' . $allowed . '.'); + } + + $this->missingMarkerBehavior = $value; + return $this; + } } \ No newline at end of file diff --git a/CommonLogic/AppInstallers/IISServerInstaller.php b/CommonLogic/AppInstallers/IISServerInstaller.php index edba6f74f..2ae2c1ba9 100644 --- a/CommonLogic/AppInstallers/IISServerInstaller.php +++ b/CommonLogic/AppInstallers/IISServerInstaller.php @@ -19,7 +19,7 @@ class IISServerInstaller extends AbstractServerInstaller */ public function install(string $source_absolute_path): \Iterator { - yield parent::install($source_absolute_path); + parent::install($source_absolute_path); $fm = $this->getWorkbench()->filemanager(); $user = 'IUSR'; @@ -78,8 +78,8 @@ protected function getConfigTemplatePathRelative(): string /** * @inheritDoc */ - protected function stringToComment(string $markerText): string + protected function stringToComment(string $comment): string { - return ""; + return ""; } } \ No newline at end of file From 358aae87149da95c9abd599dc0289f874f57cd95 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Wed, 26 Nov 2025 15:04:37 +0100 Subject: [PATCH 3/9] NEW NgninxServerInstaller and default.nginx.conf --- .../AppInstallers/NginxServerInstaller.php | 114 ++++++++++++++++++ Install/default.nginx.conf | 23 ++++ 2 files changed, 137 insertions(+) create mode 100644 CommonLogic/AppInstallers/NginxServerInstaller.php create mode 100644 Install/default.nginx.conf diff --git a/CommonLogic/AppInstallers/NginxServerInstaller.php b/CommonLogic/AppInstallers/NginxServerInstaller.php new file mode 100644 index 000000000..c23da9d81 --- /dev/null +++ b/CommonLogic/AppInstallers/NginxServerInstaller.php @@ -0,0 +1,114 @@ +configInstaller + ->setMissingMarkerBehavior($this->configInstaller::MISSING_MARKER_BEHAVIOR_ERROR) + ->addContent('Locations', $this->getLocationsContent()); + } + + protected function getLocationsContent() : string + { + return 'location / { + if (!-e $request_filename){ + rewrite ^/api/.*$ /vendor/exface/Core/index.php; + } + + if ($request_uri ~ "^$"){ + rewrite ^/$ /$request_uri redirect; + } + + rewrite ^/?$ /vendor/exface/Core/index.php; + + if (!-e $request_filename){ + rewrite ^/[^/]*$ /vendor/exface/Core/index.php; + } +} + +location /config { + return 403; +} + +location /backup { + return 403; +} + +location /translations { + return 403; +} + +location /logs { + return 403; +} + +location ~ ^/data/\..*$ { + return 403; +} + +location ~* ^vendor/.*\.html$ { + return 403; +} + +location ~* ^vendor/.*/gh-pages.*$ { + return 403; +} + +# Disable .git directory +location ~ /\.git { + deny all; + access_log off; + log_not_found off; +} + +# Add locations of phpmyadmin here. +location ~ [^/]\.php(/|$) { + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + fastcgi_pass 127.0.0.1:9000; + include fastcgi_params; + fastcgi_param HTTP_PROXY ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors on; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 3600; + fastcgi_read_timeout 3600; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; +}'; + } + + /** + * @inheritDoc + */ + protected function getConfigFileName(): string + { + return 'nginx.conf'; + } + + /** + * @inheritDoc + */ + protected function getConfigTemplatePathRelative(): string + { + return 'default.nginx.conf'; + } + + /** + * @inheritDoc + */ + protected function stringToComment(string $comment): string + { + return "# {$comment}"; + } +} \ No newline at end of file diff --git a/Install/default.nginx.conf b/Install/default.nginx.conf new file mode 100644 index 000000000..7a553be1e --- /dev/null +++ b/Install/default.nginx.conf @@ -0,0 +1,23 @@ +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Your main server block: + server { + listen 80; + server_name _; + + root /site/wwwroot/powerui/current; + + # Include folder-specific routing rules + include /site/wwwroot/powerui/current/nginx/*.conf; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + # BEGIN Locations + + # END Locations + } +} \ No newline at end of file From 460932a316887a0b1d0de0fd4f29460b88869621 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Wed, 26 Nov 2025 15:13:14 +0100 Subject: [PATCH 4/9] DEV FileContentsInstaller improved error for missing markers --- CommonLogic/AppInstallers/FileContentInstaller.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CommonLogic/AppInstallers/FileContentInstaller.php b/CommonLogic/AppInstallers/FileContentInstaller.php index 722059942..d5c974f3f 100644 --- a/CommonLogic/AppInstallers/FileContentInstaller.php +++ b/CommonLogic/AppInstallers/FileContentInstaller.php @@ -151,7 +151,13 @@ public function install(string $source_absolute_path) : \Iterator break; // Error. default: - throw new InstallerRuntimeError($this, "Failed to find marker \"{$marker}\" in file \"{$this->getFilePathAbsolute()}\"."); + $requiredMarkers = json_encode(array_keys($this->contentArray)); + throw new InstallerRuntimeError( + $this, + "Failed to find marker \"{$marker}\" in file \"{$this->getFilePathAbsolute()}\"." . + " Both \"{$this->getMarkerBegin()}\" and \"{$this->getMarkerEnd()}\" for the following" . + " markers should be present in that file: " . $requiredMarkers . "." + ); } } } From 2042628b16b3dfa0f222e35eee87fd9a4b105964 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:35:10 +0100 Subject: [PATCH 5/9] DEV CoreApp improved automatic server type detection --- CoreApp.php | 53 +++++++++++++++++++++++----- DataTypes/ServerSoftwareDataType.php | 10 ++++-- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/CoreApp.php b/CoreApp.php index fecf683d1..c858732f7 100644 --- a/CoreApp.php +++ b/CoreApp.php @@ -3,6 +3,8 @@ use exface\Core\CommonLogic\AppInstallers\ApacheServerInstaller; use exface\Core\CommonLogic\AppInstallers\AppDocsInstaller; +use exface\Core\CommonLogic\AppInstallers\NginxServerInstaller; +use exface\Core\Exceptions\Installers\InstallerRuntimeError; use exface\Core\Facades\PermalinkFacade; use exface\Core\Interfaces\InstallerInterface; use exface\Core\Factories\ConfigurationFactory; @@ -23,6 +25,7 @@ class CoreApp extends App { + const CONFIG_SERVER_INSTALLER = 'INSTALLER.SERVER_INSTALLER.CLASS'; const CONFIG_FILENAME_SYSTEM = 'System'; private $config_loading = false; @@ -102,11 +105,19 @@ public function getInstaller(InstallerInterface $injected_installer = null) $tplInstaller->setFacade(FacadeFactory::createFromString(PermalinkFacade::class, $this->getWorkbench())); $installer->addInstaller($tplInstaller); - // Server installer (e.g. for Microsoft IIS) + // Server installer. $serverInstallerClass = $this->getServerInstallerClass(); if ($serverInstallerClass !== null) { $serverInstaller = new $serverInstallerClass($this->getSelector()); $installer->addInstaller($serverInstaller); + } else { + $msg = 'Could not determine server installer class! Consider defining the server installer class ' . + 'explicitly by setting "' . self::CONFIG_SERVER_INSTALLER . '" in "System.config.json".'; + + throw new InstallerRuntimeError( + $installer, + $msg + ); } // Scheduler @@ -127,15 +138,39 @@ public function getInstaller(InstallerInterface $injected_installer = null) */ protected function getServerInstallerClass() : string|null { - $serverInstallerClass = $this->getWorkbench()->getConfig()->getOption("INSTALLER.SERVER_INSTALLER.CLASS"); + // From config option. + $cfg = $this->getWorkbench()->getConfig(); + if($cfg->hasOption(self::CONFIG_SERVER_INSTALLER)) { + $configOption = $this->getWorkbench()->getConfig()->getOption(self::CONFIG_SERVER_INSTALLER); + + if(!empty($configOption)) { + return $configOption; + } + } - // Autodetect server installer if not set explicitly - return match (true) { - !empty($serverInstallerClass) => $serverInstallerClass, - ServerSoftwareDataType::isServerIIS() => '\\' . ltrim(IISServerInstaller::class, "\\"), - ServerSoftwareDataType::isServerNginx() => null, - ServerSoftwareDataType::isServerApache() => '\\' . ltrim(ApacheServerInstaller::class, "\\"), - default => null, + // Read from PHP constant. + $softwareFamily = ServerSoftwareDataType::getServerSoftwareFamily(); + + // Guess via folder structure. + if(empty($softwareFamily)) { + $path = $this->getWorkbench()->getInstallationPath(); + $softwareFamily = match (true) { + preg_match("/\\\\www\\\\/", $path) === 1 => ServerSoftwareDataType::SERVER_SOFTWARE_APACHE, + preg_match("/[Cc]:inetpub\\\\/", $path) === 1 => ServerSoftwareDataType::SERVER_SOFTWARE_IIS, + preg_match("/\\\\wwwroot\\\\/", $path) === 1 => ServerSoftwareDataType::SERVER_SOFTWARE_NGINX, + default => null + }; + + if(empty($softwareFamily)) { + return null; + } + } + + return match ($softwareFamily) { + ServerSoftwareDataType::SERVER_SOFTWARE_APACHE => '\\' . ltrim(ApacheServerInstaller::class, "\\"), + ServerSoftwareDataType::SERVER_SOFTWARE_IIS => '\\' . ltrim(IISServerInstaller::class, "\\"), + ServerSoftwareDataType::SERVER_SOFTWARE_NGINX => '\\' . ltrim(NginxServerInstaller::class, "\\"), + default => null }; } diff --git a/DataTypes/ServerSoftwareDataType.php b/DataTypes/ServerSoftwareDataType.php index 739d31dc7..dac2829c1 100644 --- a/DataTypes/ServerSoftwareDataType.php +++ b/DataTypes/ServerSoftwareDataType.php @@ -11,6 +11,10 @@ */ class ServerSoftwareDataType extends StringDataType { + const SERVER_SOFTWARE_APACHE = 'Apache'; + const SERVER_SOFTWARE_IIS = 'Microsoft-IIS'; + const SERVER_SOFTWARE_NGINX = 'nginx'; + /** * Returns the full name of the servers software, that runs the workbench * @@ -75,7 +79,7 @@ public static function getServerSoftwareVersion() : ?string */ public static function isServerIIS() : bool { - return strcasecmp(static::getServerSoftwareFamily(), 'Microsoft-IIS') === 0; + return strcasecmp(static::getServerSoftwareFamily(), self::SERVER_SOFTWARE_IIS) === 0; } /** @@ -84,7 +88,7 @@ public static function isServerIIS() : bool */ public static function isServerApache() : bool { - return strcasecmp(static::getServerSoftwareFamily(), 'Apache') === 0; + return strcasecmp(static::getServerSoftwareFamily(), self::SERVER_SOFTWARE_APACHE) === 0; } /** @@ -93,7 +97,7 @@ public static function isServerApache() : bool */ public static function isServerNginx() : bool { - return strcasecmp(static::getServerSoftwareFamily(), 'nginx') === 0; + return strcasecmp(static::getServerSoftwareFamily(), self::SERVER_SOFTWARE_NGINX) === 0; } /** From d83d29f94d404f9206259fbaa72bbc87dc73e767 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:40:22 +0100 Subject: [PATCH 6/9] DEV AppInstallerContainer can now inject messages --- .../AppInstallers/AbstractServerInstaller.php | 18 +++- .../AppInstallers/ApacheServerInstaller.php | 8 ++ .../AppInstallers/AppDebugInstaller.php | 101 ++++++++++++++++++ .../AppInstallers/AppInstallerContainer.php | 12 +++ .../AppInstallers/IISServerInstaller.php | 8 ++ .../AppInstallers/NginxServerInstaller.php | 8 ++ CoreApp.php | 52 ++++++--- Interfaces/InstallerContainerInterface.php | 14 ++- 8 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 CommonLogic/AppInstallers/AppDebugInstaller.php diff --git a/CommonLogic/AppInstallers/AbstractServerInstaller.php b/CommonLogic/AppInstallers/AbstractServerInstaller.php index 86fcd84df..a2ec8b51e 100644 --- a/CommonLogic/AppInstallers/AbstractServerInstaller.php +++ b/CommonLogic/AppInstallers/AbstractServerInstaller.php @@ -57,8 +57,13 @@ protected abstract function getConfigTemplatePathRelative() : string; * @return string */ protected abstract function stringToComment(string $comment) : string; - + /** + * @return string + */ + protected abstract function ServerSoftwareFamilyDefault() : string; + + /** * * {@inheritDoc} * @see \exface\Core\Interfaces\InstallerInterface::backup() @@ -87,10 +92,15 @@ public function install(string $source_absolute_path): \Iterator { $indentOuter = $this->getOutputIndentation(); $indent = $indentOuter . $indentOuter; - $serverType = ServerSoftwareDataType::getServerSoftwareFamily() ?? 'UNKNOWN SERVER SOFTWARE'; - $serverVersion = ServerSoftwareDataType::getServerSoftwareVersion() ?? 'UNKNOWN VERSION'; - yield $indentOuter . "Server configuration for {$serverType} {$serverVersion}:" . PHP_EOL; + $serverType = ServerSoftwareDataType::getServerSoftwareFamily() ?? + $this->ServerSoftwareFamilyDefault() ?? + 'UNKNOWN SERVER SOFTWARE'; + + $serverVersion = ServerSoftwareDataType::getServerSoftwareVersion() ?? + 'with unknown version'; + + yield $indentOuter . "Loading server configuration for \"{$serverType}\" {$serverVersion}:" . PHP_EOL; $this->configInstaller->setOutputIndentation($indent); yield $indent . "Using \"{$this->getConfigTemplatePathRelative()}\" template for {$serverType}." . PHP_EOL; diff --git a/CommonLogic/AppInstallers/ApacheServerInstaller.php b/CommonLogic/AppInstallers/ApacheServerInstaller.php index 48cd4f3a1..de0db424d 100644 --- a/CommonLogic/AppInstallers/ApacheServerInstaller.php +++ b/CommonLogic/AppInstallers/ApacheServerInstaller.php @@ -97,4 +97,12 @@ protected function stringToComment(string $comment): string { return "# {$comment}"; } + + /** + * @inheritDoc + */ + protected function ServerSoftwareFamilyDefault(): string + { + return 'Apache'; + } } \ No newline at end of file diff --git a/CommonLogic/AppInstallers/AppDebugInstaller.php b/CommonLogic/AppInstallers/AppDebugInstaller.php new file mode 100644 index 000000000..940fbe13f --- /dev/null +++ b/CommonLogic/AppInstallers/AppDebugInstaller.php @@ -0,0 +1,101 @@ +indent = $indent; + $this->message = str_ends_with($message, PHP_EOL) ? $message : $message . PHP_EOL; + } + + /** + * @inheritDoc + */ + public function install(string $source_absolute_path): \Iterator + { + yield $this->getMessage() ?? new \EmptyIterator(); + } + + /** + * @inheritDoc + */ + public function backup(string $absolute_path): \Iterator + { + yield $this->getMessage() ?? new \EmptyIterator(); + } + + /** + * @inheritDoc + */ + public function uninstall(): \Iterator + { + yield $this->getMessage() ?? new \EmptyIterator(); + } + + /** + * STUB! Do not use. + * @deprecated + */ + public function getWorkbench() : WorkbenchInterface + { + throw new NotImplementedError('Method "getWorkbench()" is only a stub in "' . self::class . '"!'); + } + + /** + * STUB! Do not use. + * @deprecated + */ + public function getApp() : WorkbenchInterface + { + throw new NotImplementedError('Method "getApp()" is only a stub in "' . self::class . '"!'); + } + + /** + * @return string + */ + public function getOutputIndentation() : string + { + return $this->indent; + } + + /** + * @param string $value + * @return $this + */ + public function setOutputIndentation(string $value) : AppDebugInstaller + { + $this->indent = $value; + return $this; + } + + /** + * @param bool $withIndentation + * @return string|null + */ + public function getMessage(bool $withIndentation = true) : ?string + { + if($this->message === null) { + return null; + } + + return $this->getOutputIndentation() . $this->message; + } +} \ No newline at end of file diff --git a/CommonLogic/AppInstallers/AppInstallerContainer.php b/CommonLogic/AppInstallers/AppInstallerContainer.php index e69225254..d00ee527e 100644 --- a/CommonLogic/AppInstallers/AppInstallerContainer.php +++ b/CommonLogic/AppInstallers/AppInstallerContainer.php @@ -161,4 +161,16 @@ public function extract(callable $filterCallback) : InstallerContainerInterface } return $container; } + + /** + * @inheritDoc + */ + public function addMessage(string $message) : InstallerContainerInterface + { + if($message !== '') { + $this->addInstaller(new AppDebugInstaller($message)); + } + + return $this; + } } \ No newline at end of file diff --git a/CommonLogic/AppInstallers/IISServerInstaller.php b/CommonLogic/AppInstallers/IISServerInstaller.php index 2ae2c1ba9..6b3988245 100644 --- a/CommonLogic/AppInstallers/IISServerInstaller.php +++ b/CommonLogic/AppInstallers/IISServerInstaller.php @@ -82,4 +82,12 @@ protected function stringToComment(string $comment): string { return ""; } + + /** + * @inheritDoc + */ + protected function ServerSoftwareFamilyDefault(): string + { + return 'Microsoft-IIS'; + } } \ No newline at end of file diff --git a/CommonLogic/AppInstallers/NginxServerInstaller.php b/CommonLogic/AppInstallers/NginxServerInstaller.php index 8f7a1b50e..71aea4326 100644 --- a/CommonLogic/AppInstallers/NginxServerInstaller.php +++ b/CommonLogic/AppInstallers/NginxServerInstaller.php @@ -87,4 +87,12 @@ protected function stringToComment(string $comment): string { return "# {$comment}"; } + + /** + * @inheritDoc + */ + protected function ServerSoftwareFamilyDefault(): string + { + return 'nginx'; + } } \ No newline at end of file diff --git a/CoreApp.php b/CoreApp.php index 2ef31b6c1..8cda457de 100644 --- a/CoreApp.php +++ b/CoreApp.php @@ -3,8 +3,9 @@ use exface\Core\CommonLogic\AppInstallers\ApacheServerInstaller; use exface\Core\CommonLogic\AppInstallers\AppDocsInstaller; +use exface\Core\CommonLogic\AppInstallers\AppInstallerContainer; +use exface\Core\CommonLogic\AppInstallers\AppDebugInstaller; use exface\Core\CommonLogic\AppInstallers\NginxServerInstaller; -use exface\Core\Exceptions\Installers\InstallerRuntimeError; use exface\Core\Facades\PermalinkFacade; use exface\Core\Interfaces\InstallerInterface; use exface\Core\Factories\ConfigurationFactory; @@ -106,17 +107,14 @@ public function getInstaller(InstallerInterface $injected_installer = null) $installer->addInstaller($tplInstaller); // Server installer. - $serverInstallerClass = $this->getServerInstallerClass(); + $serverInstallerClass = $this->getServerInstallerClass($installer); if ($serverInstallerClass !== null) { $serverInstaller = new $serverInstallerClass($this->getSelector()); $installer->addInstaller($serverInstaller); } else { - $msg = 'Could not determine server installer class! Consider defining the server installer class ' . - 'explicitly by setting "' . self::CONFIG_SERVER_INSTALLER . '" in "System.config.json".'; - - throw new InstallerRuntimeError( - $installer, - $msg + $installer->addMessage('FAILED - Could not determine server installer class! Consider defining the' . + ' server installer class explicitly by setting "' . self::CONFIG_SERVER_INSTALLER . + '" in "System.config.json".' ); } @@ -134,18 +132,28 @@ public function getInstaller(InstallerInterface $injected_installer = null) } /** + * @param AppInstallerContainer $installer * @return string|null */ - protected function getServerInstallerClass() : string|null + protected function getServerInstallerClass(AppInstallerContainer $installer) : string|null { + $installer->addMessage('Determining server installer class:'); + $indent = ' '; + // From config option. $cfg = $this->getWorkbench()->getConfig(); if($cfg->hasOption(self::CONFIG_SERVER_INSTALLER)) { $configOption = $this->getWorkbench()->getConfig()->getOption(self::CONFIG_SERVER_INSTALLER); - - if(!empty($configOption)) { + + // Valid-ish config option. + if(class_exists($configOption)) { + $installer->addMessage($indent . 'Found installer class in config: "' . $configOption . '".'); return $configOption; - } + } + + // Invalid config option. + $installer->addMessage($indent . 'Value "' . $configOption . '" for config option "' . + self::CONFIG_SERVER_INSTALLER . '" is not a valid class name.'); } // Read from PHP constant. @@ -155,6 +163,9 @@ protected function getServerInstallerClass() : string|null // Future installations should have a manually defined `` if(empty($softwareFamily)) { $path = $this->getWorkbench()->getInstallationPath(); + $installer->addMessage($indent . 'Deducing server software from installation path "' . $path . + '" under "' . php_uname('s') . '".'); + $softwareFamily = match (true) { // Microsoft IIS runs on windows and has its files mostly in c:\inetpub\wwwroot ServerSoftwareDataType::isOsWindows() && preg_match('/[Cc]:\\\\inetpub\\\\wwwroot\\\\/', $path) === 1 => ServerSoftwareDataType::SERVER_SOFTWARE_IIS, @@ -165,18 +176,31 @@ protected function getServerInstallerClass() : string|null ServerSoftwareDataType::isOsLinux() && preg_match("/\/www\//", $path) === 1 => ServerSoftwareDataType::SERVER_SOFTWARE_APACHE, default => null }; - + if(empty($softwareFamily)) { + $installer->addMessage($indent . 'Could not determine server software.'); return null; + } else { + $installer->addMessage($indent . 'Server software from folder structure: "' . $softwareFamily . '".'); } + } else { + $installer->addMessage($indent . 'Server software from PHP constant: "' . $softwareFamily . '".'); } - return match ($softwareFamily) { + $class = match ($softwareFamily) { ServerSoftwareDataType::SERVER_SOFTWARE_APACHE => '\\' . ltrim(ApacheServerInstaller::class, "\\"), ServerSoftwareDataType::SERVER_SOFTWARE_IIS => '\\' . ltrim(IISServerInstaller::class, "\\"), ServerSoftwareDataType::SERVER_SOFTWARE_NGINX => '\\' . ltrim(NginxServerInstaller::class, "\\"), default => null }; + + if($class !== null) { + $installer->addMessage($indent . 'Deduced installer class from server software: "' . $class . '".'); + } else { + $installer->addMessage($indent . 'Could not deduce server installer class.'); + } + + return $class; } /** diff --git a/Interfaces/InstallerContainerInterface.php b/Interfaces/InstallerContainerInterface.php index 6ec1db63e..df42f6a59 100644 --- a/Interfaces/InstallerContainerInterface.php +++ b/Interfaces/InstallerContainerInterface.php @@ -17,7 +17,7 @@ public function addInstaller(InstallerInterface $installer, $insertAtBeinning = * @return InstallerInterface[] */ public function getInstallers() : array; - + /** * Returns a new installer container with only installers matching the ginve filter * @@ -25,4 +25,16 @@ public function getInstallers() : array; * @return InstallerContainerInterface */ public function extract(callable $filterCallback) : InstallerContainerInterface; + + /** + * Adds a message to be displayed when performing `install()`, `backup()` or `uninstall()`. + * + * NOTE: Adding a message is similar to adding an installer. Both use the same queue. Adding a message + * just before adding an installer means, that this message will always be displayed before that installer + * is executed. + * + * @param string $message + * @return InstallerContainerInterface + */ + public function addMessage(string $message) : InstallerContainerInterface; } \ No newline at end of file From b197b16d4d3ca45e0029e55d64970f76f48849eb Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:53:57 +0100 Subject: [PATCH 7/9] DEV AppInstallerContainer simplified addMessage --- .../AppInstallers/AppInstallerContainer.php | 19 +++++++++++++++-- ...pDebugInstaller.php => DebugInstaller.php} | 21 +++++++------------ CoreApp.php | 1 - 3 files changed, 24 insertions(+), 17 deletions(-) rename CommonLogic/AppInstallers/{AppDebugInstaller.php => DebugInstaller.php} (77%) diff --git a/CommonLogic/AppInstallers/AppInstallerContainer.php b/CommonLogic/AppInstallers/AppInstallerContainer.php index d00ee527e..77dc6dd91 100644 --- a/CommonLogic/AppInstallers/AppInstallerContainer.php +++ b/CommonLogic/AppInstallers/AppInstallerContainer.php @@ -44,6 +44,11 @@ public final function install(string $source_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { + if($installer instanceof DebugInstaller) { + $installer->install($source_absolute_path); + continue; + } + $eventMgr->dispatch(new OnBeforeInstallEvent($installer, $source_absolute_path)); yield from $installer->install($source_absolute_path); $eventMgr->dispatch(new OnInstallEvent($installer, $source_absolute_path)); @@ -93,6 +98,11 @@ public final function backup(string $destination_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { + if($installer instanceof DebugInstaller) { + $installer->backup($destination_absolute_path); + continue; + } + $eventMgr->dispatch(new OnBeforeBackupEvent($installer, $destination_absolute_path)); yield from $installer->backup($destination_absolute_path); $eventMgr->dispatch(new OnBackupEvent($installer, $destination_absolute_path)); @@ -117,6 +127,11 @@ public final function uninstall() : \Iterator // TODO disable mutations here too??? $eventMgr = $this->getWorkbench()->eventManager(); foreach (array_reverse($this->getInstallers()) as $installer) { + if($installer instanceof DebugInstaller) { + $installer->uninstall(); + continue; + } + $eventMgr->dispatch(new OnBeforeUninstallEvent($installer)); yield from $installer->uninstall(); $eventMgr->dispatch(new OnUninstallEvent($installer)); @@ -165,10 +180,10 @@ public function extract(callable $filterCallback) : InstallerContainerInterface /** * @inheritDoc */ - public function addMessage(string $message) : InstallerContainerInterface + public function addMessage(string $message, bool $insertAtBeginning = false) : InstallerContainerInterface { if($message !== '') { - $this->addInstaller(new AppDebugInstaller($message)); + $this->addInstaller(new DebugInstaller($this->getWorkbench(), $message), $insertAtBeginning); } return $this; diff --git a/CommonLogic/AppInstallers/AppDebugInstaller.php b/CommonLogic/AppInstallers/DebugInstaller.php similarity index 77% rename from CommonLogic/AppInstallers/AppDebugInstaller.php rename to CommonLogic/AppInstallers/DebugInstaller.php index 940fbe13f..e33e8ca49 100644 --- a/CommonLogic/AppInstallers/AppDebugInstaller.php +++ b/CommonLogic/AppInstallers/DebugInstaller.php @@ -2,8 +2,7 @@ namespace exface\Core\CommonLogic\AppInstallers; -use exface\Core\Exceptions\NotImplementedError; -use exface\Core\Interfaces\AppInstallerInterface; +use exface\Core\Interfaces\InstallerInterface; use exface\Core\Interfaces\WorkbenchInterface; /** @@ -12,16 +11,19 @@ * * You can use this installer as a stub or to inject debugging messages and logging into your deployment logic. */ -class AppDebugInstaller implements AppInstallerInterface +class DebugInstaller implements InstallerInterface { + private WorkbenchInterface $workbench; private ?string $message; private string $indent; function __construct( + WorkbenchInterface $workbench, string $message = null, string $indent = ' ' ) { + $this->workbench = $workbench; $this->indent = $indent; $this->message = str_ends_with($message, PHP_EOL) ? $message : $message . PHP_EOL; } @@ -56,16 +58,7 @@ public function uninstall(): \Iterator */ public function getWorkbench() : WorkbenchInterface { - throw new NotImplementedError('Method "getWorkbench()" is only a stub in "' . self::class . '"!'); - } - - /** - * STUB! Do not use. - * @deprecated - */ - public function getApp() : WorkbenchInterface - { - throw new NotImplementedError('Method "getApp()" is only a stub in "' . self::class . '"!'); + return $this->workbench; } /** @@ -80,7 +73,7 @@ public function getOutputIndentation() : string * @param string $value * @return $this */ - public function setOutputIndentation(string $value) : AppDebugInstaller + public function setOutputIndentation(string $value) : DebugInstaller { $this->indent = $value; return $this; diff --git a/CoreApp.php b/CoreApp.php index 8cda457de..927363180 100644 --- a/CoreApp.php +++ b/CoreApp.php @@ -4,7 +4,6 @@ use exface\Core\CommonLogic\AppInstallers\ApacheServerInstaller; use exface\Core\CommonLogic\AppInstallers\AppDocsInstaller; use exface\Core\CommonLogic\AppInstallers\AppInstallerContainer; -use exface\Core\CommonLogic\AppInstallers\AppDebugInstaller; use exface\Core\CommonLogic\AppInstallers\NginxServerInstaller; use exface\Core\Facades\PermalinkFacade; use exface\Core\Interfaces\InstallerInterface; From fa1c0757df8075db0ac5feedc945f03657a936c8 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:55:30 +0100 Subject: [PATCH 8/9] FIX AppInstallerContainer fixed syntax --- CommonLogic/AppInstallers/AppInstallerContainer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CommonLogic/AppInstallers/AppInstallerContainer.php b/CommonLogic/AppInstallers/AppInstallerContainer.php index 77dc6dd91..5f5694ea1 100644 --- a/CommonLogic/AppInstallers/AppInstallerContainer.php +++ b/CommonLogic/AppInstallers/AppInstallerContainer.php @@ -45,7 +45,7 @@ public final function install(string $source_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { if($installer instanceof DebugInstaller) { - $installer->install($source_absolute_path); + yield from $installer->install($source_absolute_path); continue; } @@ -99,7 +99,7 @@ public final function backup(string $destination_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { if($installer instanceof DebugInstaller) { - $installer->backup($destination_absolute_path); + yield from $installer->backup($destination_absolute_path); continue; } @@ -128,7 +128,7 @@ public final function uninstall() : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach (array_reverse($this->getInstallers()) as $installer) { if($installer instanceof DebugInstaller) { - $installer->uninstall(); + yield from $installer->uninstall(); continue; } From 56b896946d520e714fc745e4dfe2365026f238f8 Mon Sep 17 00:00:00 2001 From: hail-cookies <81558994+hail-cookies@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:55:48 +0100 Subject: [PATCH 9/9] DEV DebugInstaller now implements IAmSilentInterface for better readability --- .../AppInstallers/AppInstallerContainer.php | 7 ++++--- CommonLogic/AppInstallers/DebugInstaller.php | 3 ++- Interfaces/IAmSilentInterface.php | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 Interfaces/IAmSilentInterface.php diff --git a/CommonLogic/AppInstallers/AppInstallerContainer.php b/CommonLogic/AppInstallers/AppInstallerContainer.php index 5f5694ea1..3e52a633d 100644 --- a/CommonLogic/AppInstallers/AppInstallerContainer.php +++ b/CommonLogic/AppInstallers/AppInstallerContainer.php @@ -3,6 +3,7 @@ use exface\Core\DataTypes\DateTimeDataType; use exface\Core\Interfaces\AppInstallerInterface; +use exface\Core\Interfaces\IAmSilentInterface; use exface\Core\Interfaces\InstallerInterface; use exface\Core\Interfaces\InstallerContainerInterface; use exface\Core\Events\Installer\OnBeforeInstallEvent; @@ -44,7 +45,7 @@ public final function install(string $source_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { - if($installer instanceof DebugInstaller) { + if($installer instanceof IAmSilentInterface) { yield from $installer->install($source_absolute_path); continue; } @@ -98,7 +99,7 @@ public final function backup(string $destination_absolute_path) : \Iterator $eventMgr = $this->getWorkbench()->eventManager(); foreach ($this->getInstallers() as $installer) { - if($installer instanceof DebugInstaller) { + if($installer instanceof IAmSilentInterface) { yield from $installer->backup($destination_absolute_path); continue; } @@ -127,7 +128,7 @@ public final function uninstall() : \Iterator // TODO disable mutations here too??? $eventMgr = $this->getWorkbench()->eventManager(); foreach (array_reverse($this->getInstallers()) as $installer) { - if($installer instanceof DebugInstaller) { + if($installer instanceof IAmSilentInterface) { yield from $installer->uninstall(); continue; } diff --git a/CommonLogic/AppInstallers/DebugInstaller.php b/CommonLogic/AppInstallers/DebugInstaller.php index e33e8ca49..3522e9a0f 100644 --- a/CommonLogic/AppInstallers/DebugInstaller.php +++ b/CommonLogic/AppInstallers/DebugInstaller.php @@ -3,6 +3,7 @@ namespace exface\Core\CommonLogic\AppInstallers; use exface\Core\Interfaces\InstallerInterface; +use exface\Core\Interfaces\IAmSilentInterface; use exface\Core\Interfaces\WorkbenchInterface; /** @@ -11,7 +12,7 @@ * * You can use this installer as a stub or to inject debugging messages and logging into your deployment logic. */ -class DebugInstaller implements InstallerInterface +class DebugInstaller implements InstallerInterface, IAmSilentInterface { private WorkbenchInterface $workbench; private ?string $message; diff --git a/Interfaces/IAmSilentInterface.php b/Interfaces/IAmSilentInterface.php new file mode 100644 index 000000000..0a4157562 --- /dev/null +++ b/Interfaces/IAmSilentInterface.php @@ -0,0 +1,18 @@ +