From bb7f465c0e4ed37fd50be97995eb57dea33983c8 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Wed, 5 Nov 2014 15:32:09 +0100 Subject: [PATCH 01/39] Generate apache2 and apache 2.4 configuration refs #6120 --- application/clicommands/SetupCommand.php | 68 +++++++ library/Icinga/Config/Webserver/Apache2.php | 28 +++ library/Icinga/Config/Webserver/Apache24.php | 26 +++ library/Icinga/Config/Webserver/Webserver.php | 186 ++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 library/Icinga/Config/Webserver/Apache2.php create mode 100644 library/Icinga/Config/Webserver/Apache24.php create mode 100644 library/Icinga/Config/Webserver/Webserver.php diff --git a/application/clicommands/SetupCommand.php b/application/clicommands/SetupCommand.php index 8f87699ef..d4de4e26b 100644 --- a/application/clicommands/SetupCommand.php +++ b/application/clicommands/SetupCommand.php @@ -4,7 +4,10 @@ namespace Icinga\Clicommands; +use Icinga\Application\Logger; use Icinga\Cli\Command; +use Icinga\Config\Webserver\WebServer; +use Icinga\Exception\ProgrammingError; /** * Setup Icinga Web 2 @@ -120,6 +123,71 @@ class SetupCommand extends Command printf($this->translate("Successfully created configuration directory at: %s\n"), $path); } + /** + * Create webserver configuration + * + * USAGE: + * + * icingacli setup webserver [options] + * + * OPTIONS: + * + * --path= Path for the web server, default /icingaweb + * + * --publicPath= Path to htdocs system path + * + * --file= Write configuration to file + * + * + * EXAMPLES: + * + * icingacli setup webserver apache24 + * + * icingacli setup webserver apache2 --path /icingaweb --publicPath /usr/share/icingaweb/public + * + * icingacli setup webserver apache2 --file /etc/apache2/conf.d/icingaweb.conf + */ + public function webserverAction() + { + if (($type = $this->params->getStandalone()) === null) { + $this->fail($this->translate('Argument type is mandatory.')); + } + try { + $webserver = WebServer::createInstance($type); + } catch (ProgrammingError $e) { + $this->fail($this->translate('Unknown type') . ': ' . $type); + } + $webserver->setApp($this->app); + if (($sapi = $this->params->get('sapi', 'server')) === null) { + $this->fail($this->translate('argument --sapi is mandatory.')); + } + if (($path = $this->params->get('path', '/icingaweb')) === null) { + $this->fail($this->translate('argument --path is mandatory.')); + } + if (($publicPath = $this->params->get('publicPath', $webserver->getPublicPath())) === null) { + $this->fail($this->translate('argument --publicPath is mandatory.')); + } + $webserver->setWebPath($path); + $webserver->setPublicPath($publicPath); + $webserver->setSapi($sapi); + $config = $webserver->generate() . "\n"; + if (($file = $this->params->get('file')) !== null) { + if (file_exists($file) === true) { + $this->fail(sprintf($this->translate('File %s already exists. Please delete it first.'), $file)); + } + Logger::info($this->translate('Write %s configuration to file: %s'), $type, $file); + $re = file_put_contents($file, $config); + if ($re === false) { + $this->fail($this->translate('Could not write to file') . ': ' . $file); + } + Logger::info($this->translate('Successfully written %d bytes to file'), $re); + return true; + } + printf("# Your %s configuration:\n", $type); + echo $config; + return true; + } + /** * Return whether the current user is a super user * diff --git a/library/Icinga/Config/Webserver/Apache2.php b/library/Icinga/Config/Webserver/Apache2.php new file mode 100644 index 000000000..2b9d756cf --- /dev/null +++ b/library/Icinga/Config/Webserver/Apache2.php @@ -0,0 +1,28 @@ +', + ' Options -Indexes', + ' AllowOverride All', + ' Order allow,deny', + ' Allow from all', + ' EnableSendfile Off', + '' + ); + } +} diff --git a/library/Icinga/Config/Webserver/Apache24.php b/library/Icinga/Config/Webserver/Apache24.php new file mode 100644 index 000000000..ad758abcd --- /dev/null +++ b/library/Icinga/Config/Webserver/Apache24.php @@ -0,0 +1,26 @@ +getTemplate(); + if (is_array($template)) { + $template = implode(PHP_EOL, $template); + } + $searchTokens = array( + '{webPath}', + '{publicPath}' + ); + $replaceTokens = array( + $this->getWebPath(), + $this->getPublicPath() + ); + $template = str_replace($searchTokens, $replaceTokens, $template); + return $template; + } + + /** + * Specific template + * + * @return array|string + */ + abstract protected function getTemplate(); + + /** + * Setter for SAPI name + * + * @param string $sapi + */ + public function setSapi($sapi) + { + $this->sapi = $sapi; + } + + /** + * Getter for SAPI name + * + * @return string + */ + public function getSapi() + { + return $this->sapi; + } + + /** + * Setter for web path + * + * @param string $webPath + */ + public function setWebPath($webPath) + { + $this->webPath = $webPath; + } + + /** + * Getter for web path + * + * @return string + */ + public function getWebPath() + { + return $this->webPath; + } + + /** + * @param string $publicPath + */ + public function setPublicPath($publicPath) + { + $this->publicPath = $publicPath; + } + + /** + * Detect public root + * + * @return string + */ + public function detectPublicPath() + { + $applicationPath = $this->getApp()->getApplicationDir(); + $applicationPath = dirname($applicationPath) . DIRECTORY_SEPARATOR . 'public'; + if (is_dir($applicationPath) === true) { + return $applicationPath; + } + return null; + } + + /** + * Getter for public root + * + * @return string + */ + public function getPublicPath() + { + if ($this->publicPath === null) { + $this->publicPath = $this->detectPublicPath(); + } + return $this->publicPath; + } + + /** + * Setter for application bootstrap + * + * @param ApplicationBootstrap $app + */ + public function setApp(ApplicationBootstrap $app) + { + $this->app = $app; + } + + /** + * Getter for application bootstrap + * + * @return ApplicationBootstrap + */ + public function getApp() + { + return $this->app; + } +} From 8e28fff5243c00dddbbcee3b7e7c5759a394afae Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Thu, 6 Nov 2014 10:41:28 +0100 Subject: [PATCH 02/39] Setup/Webserver: Adapt class names to filename refs #6120 --- application/clicommands/SetupCommand.php | 4 ++-- library/Icinga/Config/Webserver/Apache2.php | 2 +- library/Icinga/Config/Webserver/Webserver.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/clicommands/SetupCommand.php b/application/clicommands/SetupCommand.php index d4de4e26b..a8b9ea4f6 100644 --- a/application/clicommands/SetupCommand.php +++ b/application/clicommands/SetupCommand.php @@ -6,7 +6,7 @@ namespace Icinga\Clicommands; use Icinga\Application\Logger; use Icinga\Cli\Command; -use Icinga\Config\Webserver\WebServer; +use Icinga\Config\Webserver\Webserver; use Icinga\Exception\ProgrammingError; /** @@ -153,7 +153,7 @@ class SetupCommand extends Command $this->fail($this->translate('Argument type is mandatory.')); } try { - $webserver = WebServer::createInstance($type); + $webserver = Webserver::createInstance($type); } catch (ProgrammingError $e) { $this->fail($this->translate('Unknown type') . ': ' . $type); } diff --git a/library/Icinga/Config/Webserver/Apache2.php b/library/Icinga/Config/Webserver/Apache2.php index 2b9d756cf..077bf8bc3 100644 --- a/library/Icinga/Config/Webserver/Apache2.php +++ b/library/Icinga/Config/Webserver/Apache2.php @@ -7,7 +7,7 @@ namespace Icinga\Config\Webserver; /** * Generate apache 2.x (< 2.4) configuration */ -class Apache2 extends WebServer +class Apache2 extends Webserver { /** * @return array diff --git a/library/Icinga/Config/Webserver/Webserver.php b/library/Icinga/Config/Webserver/Webserver.php index 2bbd1cbb6..349af82de 100644 --- a/library/Icinga/Config/Webserver/Webserver.php +++ b/library/Icinga/Config/Webserver/Webserver.php @@ -10,7 +10,7 @@ use Icinga\Exception\ProgrammingError; /** * Generate webserver configuration */ -abstract class WebServer +abstract class Webserver { /** * Web path From f49c8c02dbea0bb2b3904d918d459edff0d5dc08 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Thu, 6 Nov 2014 11:06:55 +0100 Subject: [PATCH 03/39] Setup/Webserver: Add nginx configuration refs #6738 --- library/Icinga/Config/Webserver/Nginx.php | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 library/Icinga/Config/Webserver/Nginx.php diff --git a/library/Icinga/Config/Webserver/Nginx.php b/library/Icinga/Config/Webserver/Nginx.php new file mode 100644 index 000000000..600f54e90 --- /dev/null +++ b/library/Icinga/Config/Webserver/Nginx.php @@ -0,0 +1,35 @@ + Date: Thu, 6 Nov 2014 11:24:41 +0100 Subject: [PATCH 04/39] Setup/Webserver: Add nginx sub path configuration refs #6738 --- library/Icinga/Config/Webserver/Nginx.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Icinga/Config/Webserver/Nginx.php b/library/Icinga/Config/Webserver/Nginx.php index 600f54e90..bd670083c 100644 --- a/library/Icinga/Config/Webserver/Nginx.php +++ b/library/Icinga/Config/Webserver/Nginx.php @@ -25,6 +25,8 @@ class Nginx extends Webserver ' fastcgi_param SCRIPT_FILENAME {publicPath}/index.php;', '}', + + 'location ~ ^{webPath} {', ' alias {publicPath};', ' index index.php;', From 162b35566cd99334666af207780d4e4b6c71ebef Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Fri, 7 Nov 2014 09:09:02 +0100 Subject: [PATCH 05/39] Setup/Webserver: Change namespace refs #6120 --- application/clicommands/SetupCommand.php | 2 +- library/Icinga/{Config => Web/Setup}/Webserver/Apache2.php | 2 +- library/Icinga/{Config => Web/Setup}/Webserver/Apache24.php | 2 +- library/Icinga/{Config => Web/Setup}/Webserver/Nginx.php | 2 +- library/Icinga/{Config => Web/Setup}/Webserver/Webserver.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename library/Icinga/{Config => Web/Setup}/Webserver/Apache2.php (93%) rename library/Icinga/{Config => Web/Setup}/Webserver/Apache24.php (93%) rename library/Icinga/{Config => Web/Setup}/Webserver/Nginx.php (95%) rename library/Icinga/{Config => Web/Setup}/Webserver/Webserver.php (98%) diff --git a/application/clicommands/SetupCommand.php b/application/clicommands/SetupCommand.php index a8b9ea4f6..caa07ae1d 100644 --- a/application/clicommands/SetupCommand.php +++ b/application/clicommands/SetupCommand.php @@ -6,7 +6,7 @@ namespace Icinga\Clicommands; use Icinga\Application\Logger; use Icinga\Cli\Command; -use Icinga\Config\Webserver\Webserver; +use Icinga\Web\Setup\Webserver\Webserver; use Icinga\Exception\ProgrammingError; /** diff --git a/library/Icinga/Config/Webserver/Apache2.php b/library/Icinga/Web/Setup/Webserver/Apache2.php similarity index 93% rename from library/Icinga/Config/Webserver/Apache2.php rename to library/Icinga/Web/Setup/Webserver/Apache2.php index 077bf8bc3..49db80e9b 100644 --- a/library/Icinga/Config/Webserver/Apache2.php +++ b/library/Icinga/Web/Setup/Webserver/Apache2.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Config\Webserver; +namespace Icinga\Web\Setup\Webserver; /** * Generate apache 2.x (< 2.4) configuration diff --git a/library/Icinga/Config/Webserver/Apache24.php b/library/Icinga/Web/Setup/Webserver/Apache24.php similarity index 93% rename from library/Icinga/Config/Webserver/Apache24.php rename to library/Icinga/Web/Setup/Webserver/Apache24.php index ad758abcd..29e927770 100644 --- a/library/Icinga/Config/Webserver/Apache24.php +++ b/library/Icinga/Web/Setup/Webserver/Apache24.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Config\Webserver; +namespace Icinga\Web\Setup\Webserver; /** * Generate apache2.4 configuration diff --git a/library/Icinga/Config/Webserver/Nginx.php b/library/Icinga/Web/Setup/Webserver/Nginx.php similarity index 95% rename from library/Icinga/Config/Webserver/Nginx.php rename to library/Icinga/Web/Setup/Webserver/Nginx.php index bd670083c..90996b2a0 100644 --- a/library/Icinga/Config/Webserver/Nginx.php +++ b/library/Icinga/Web/Setup/Webserver/Nginx.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Config\Webserver; +namespace Icinga\Web\Setup\Webserver; /** * Create nginx webserver configuration diff --git a/library/Icinga/Config/Webserver/Webserver.php b/library/Icinga/Web/Setup/Webserver/Webserver.php similarity index 98% rename from library/Icinga/Config/Webserver/Webserver.php rename to library/Icinga/Web/Setup/Webserver/Webserver.php index 349af82de..b246cffd0 100644 --- a/library/Icinga/Config/Webserver/Webserver.php +++ b/library/Icinga/Web/Setup/Webserver/Webserver.php @@ -2,7 +2,7 @@ // {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}} -namespace Icinga\Config\Webserver; +namespace Icinga\Web\Setup\Webserver; use Icinga\Application\ApplicationBootstrap; use Icinga\Exception\ProgrammingError; From ddf6bb4472ed8aa1095023c8fb71a761dd5a07f5 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Fri, 7 Nov 2014 09:18:16 +0100 Subject: [PATCH 06/39] Setup/Webserver: Change template generation to nowdoc refs #6120 --- .../Icinga/Web/Setup/Webserver/Apache2.php | 21 ++++++------- .../Icinga/Web/Setup/Webserver/Apache24.php | 17 ++++++----- library/Icinga/Web/Setup/Webserver/Nginx.php | 30 +++++++++---------- .../Icinga/Web/Setup/Webserver/Webserver.php | 6 ++-- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/library/Icinga/Web/Setup/Webserver/Apache2.php b/library/Icinga/Web/Setup/Webserver/Apache2.php index 49db80e9b..846dc0a96 100644 --- a/library/Icinga/Web/Setup/Webserver/Apache2.php +++ b/library/Icinga/Web/Setup/Webserver/Apache2.php @@ -14,15 +14,16 @@ class Apache2 extends Webserver */ protected function getTemplate() { - return array( - 'Alias {webPath} {publicPath}', - '', - ' Options -Indexes', - ' AllowOverride All', - ' Order allow,deny', - ' Allow from all', - ' EnableSendfile Off', - '' - ); + return <<<'EOD' +Alias {webPath} {publicPath} + + Options -Indexes + AllowOverride All + Order allow,deny + Allow from all + EnableSendfile Off + +EOD; + } } diff --git a/library/Icinga/Web/Setup/Webserver/Apache24.php b/library/Icinga/Web/Setup/Webserver/Apache24.php index 29e927770..c9992e794 100644 --- a/library/Icinga/Web/Setup/Webserver/Apache24.php +++ b/library/Icinga/Web/Setup/Webserver/Apache24.php @@ -7,7 +7,7 @@ namespace Icinga\Web\Setup\Webserver; /** * Generate apache2.4 configuration */ -class Apache24 extends Apache2 +class Apache24 extends Webserver { /** * Use default template and change granted syntax for 2.4 @@ -16,11 +16,14 @@ class Apache24 extends Apache2 */ protected function getTemplate() { - $template = parent::getTemplate(); - $replace = array( - ' Require all granted' - ); - array_splice($template, count($template)-4, 2, $replace); - return $template; + return <<<'EOD' +Alias {webPath} {publicPath} + + Options -Indexes + AllowOverride All + Require all granted + EnableSendfile Off + +EOD; } } diff --git a/library/Icinga/Web/Setup/Webserver/Nginx.php b/library/Icinga/Web/Setup/Webserver/Nginx.php index 90996b2a0..decd7dda5 100644 --- a/library/Icinga/Web/Setup/Webserver/Nginx.php +++ b/library/Icinga/Web/Setup/Webserver/Nginx.php @@ -16,22 +16,20 @@ class Nginx extends Webserver */ protected function getTemplate() { - return array( - 'location ~ ^{webPath}/index\.php(.*)$ {', - ' # fastcgi_pass 127.0.0.1:9000;', - ' fastcgi_pass unix:/var/run/php5-fpm.sock;', - ' fastcgi_index index.php;', - ' include fastcgi_params;', - ' fastcgi_param SCRIPT_FILENAME {publicPath}/index.php;', - '}', + return <<<'EOD' +location ~ ^{webPath}/index\.php(.*)$ { + # fastcgi_pass 127.0.0.1:9000; + fastcgi_pass unix:/var/run/php5-fpm.sock; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME {publicPath}/index.php; +} - - - 'location ~ ^{webPath} {', - ' alias {publicPath};', - ' index index.php;', - ' try_files $uri $uri/ {webPath}/index.php$is_args$args;', - '}', - ); +location ~ ^{webPath} { + alias {publicPath}; + index index.php; + try_files $uri $uri/ {webPath}/index.php$is_args$args; +} +EOD; } } diff --git a/library/Icinga/Web/Setup/Webserver/Webserver.php b/library/Icinga/Web/Setup/Webserver/Webserver.php index b246cffd0..56ade8467 100644 --- a/library/Icinga/Web/Setup/Webserver/Webserver.php +++ b/library/Icinga/Web/Setup/Webserver/Webserver.php @@ -66,9 +66,7 @@ abstract class Webserver public function generate() { $template = $this->getTemplate(); - if (is_array($template)) { - $template = implode(PHP_EOL, $template); - } + $searchTokens = array( '{webPath}', '{publicPath}' @@ -84,7 +82,7 @@ abstract class Webserver /** * Specific template * - * @return array|string + * @return string */ abstract protected function getTemplate(); From f07cbcc107f0934500903d37772118ad7e8796d3 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Fri, 7 Nov 2014 10:34:53 +0100 Subject: [PATCH 07/39] Setup/Webserver: Consolidate apache config Add configDir to replace tokens. refs #6120 --- application/clicommands/SetupCommand.php | 6 ++- .../Icinga/Web/Setup/Webserver/Apache2.php | 39 +++++++++++++++---- .../Icinga/Web/Setup/Webserver/Apache24.php | 29 -------------- .../Icinga/Web/Setup/Webserver/Webserver.php | 6 ++- 4 files changed, 39 insertions(+), 41 deletions(-) delete mode 100644 library/Icinga/Web/Setup/Webserver/Apache24.php diff --git a/application/clicommands/SetupCommand.php b/application/clicommands/SetupCommand.php index caa07ae1d..5fbe309c8 100644 --- a/application/clicommands/SetupCommand.php +++ b/application/clicommands/SetupCommand.php @@ -128,7 +128,7 @@ class SetupCommand extends Command * * USAGE: * - * icingacli setup webserver [options] + * icingacli setup webserver [options] * * OPTIONS: * @@ -141,11 +141,13 @@ class SetupCommand extends Command * * EXAMPLES: * - * icingacli setup webserver apache24 + * icingacli setup webserver apache2 * * icingacli setup webserver apache2 --path /icingaweb --publicPath /usr/share/icingaweb/public * * icingacli setup webserver apache2 --file /etc/apache2/conf.d/icingaweb.conf + * + * icingacli setup webserver nginx */ public function webserverAction() { diff --git a/library/Icinga/Web/Setup/Webserver/Apache2.php b/library/Icinga/Web/Setup/Webserver/Apache2.php index 846dc0a96..767e9a882 100644 --- a/library/Icinga/Web/Setup/Webserver/Apache2.php +++ b/library/Icinga/Web/Setup/Webserver/Apache2.php @@ -15,14 +15,37 @@ class Apache2 extends Webserver protected function getTemplate() { return <<<'EOD' -Alias {webPath} {publicPath} - - Options -Indexes - AllowOverride All - Order allow,deny - Allow from all - EnableSendfile Off - +Alias {webPath} "{publicPath}" + + + Options SymLinksIfOwnerMatch + AllowOverride None + + + # Apache 2.4 + + Require all granted + + + + + # Apache 2.2 + Order allow,deny + Allow from all + + + SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb/ + + EnableSendfile Off + + RewriteEngine on + RewriteBase {webPath}/ + RewriteCond %{REQUEST_FILENAME} -s [OR] + RewriteCond %{REQUEST_FILENAME} -l [OR] + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^.*$ - [NC,L] + RewriteRule ^.*$ index.php [NC,L] + EOD; } diff --git a/library/Icinga/Web/Setup/Webserver/Apache24.php b/library/Icinga/Web/Setup/Webserver/Apache24.php deleted file mode 100644 index c9992e794..000000000 --- a/library/Icinga/Web/Setup/Webserver/Apache24.php +++ /dev/null @@ -1,29 +0,0 @@ - - Options -Indexes - AllowOverride All - Require all granted - EnableSendfile Off - -EOD; - } -} diff --git a/library/Icinga/Web/Setup/Webserver/Webserver.php b/library/Icinga/Web/Setup/Webserver/Webserver.php index 56ade8467..47ffc4a10 100644 --- a/library/Icinga/Web/Setup/Webserver/Webserver.php +++ b/library/Icinga/Web/Setup/Webserver/Webserver.php @@ -69,11 +69,13 @@ abstract class Webserver $searchTokens = array( '{webPath}', - '{publicPath}' + '{publicPath}', + '{configPath}', ); $replaceTokens = array( $this->getWebPath(), - $this->getPublicPath() + $this->getPublicPath(), + $this->getApp()->getConfigDir() ); $template = str_replace($searchTokens, $replaceTokens, $template); return $template; From 8583be040c7424af1db830b3386e37cc9e910c13 Mon Sep 17 00:00:00 2001 From: Marius Hein Date: Fri, 7 Nov 2014 12:02:48 +0100 Subject: [PATCH 08/39] Setup/Webserver: Fix nginx static file resolve issue refs #6738 --- library/Icinga/Web/Setup/Webserver/Nginx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Setup/Webserver/Nginx.php b/library/Icinga/Web/Setup/Webserver/Nginx.php index decd7dda5..97d539e32 100644 --- a/library/Icinga/Web/Setup/Webserver/Nginx.php +++ b/library/Icinga/Web/Setup/Webserver/Nginx.php @@ -25,10 +25,10 @@ location ~ ^{webPath}/index\.php(.*)$ { fastcgi_param SCRIPT_FILENAME {publicPath}/index.php; } -location ~ ^{webPath} { +location ~ ^{webPath}(.+)? { alias {publicPath}; index index.php; - try_files $uri $uri/ {webPath}/index.php$is_args$args; + try_files $1 $uri $uri/ {webPath}/index.php$is_args$args; } EOD; } From 97186c0361ddaba8a10534d89ab852aff3c726e5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 11 Nov 2014 22:02:40 +0100 Subject: [PATCH 09/39] Webserver\Apache2: show hint with no mod_rewrite This is yet unstyled, but still far better than 500 fixes #7643 --- .../Icinga/Web/Setup/Webserver/Apache2.php | 22 +++++++++++++------ public/error_norewrite.html | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 public/error_norewrite.html diff --git a/library/Icinga/Web/Setup/Webserver/Apache2.php b/library/Icinga/Web/Setup/Webserver/Apache2.php index 767e9a882..f878a23e8 100644 --- a/library/Icinga/Web/Setup/Webserver/Apache2.php +++ b/library/Icinga/Web/Setup/Webserver/Apache2.php @@ -38,13 +38,21 @@ Alias {webPath} "{publicPath}" EnableSendfile Off - RewriteEngine on - RewriteBase {webPath}/ - RewriteCond %{REQUEST_FILENAME} -s [OR] - RewriteCond %{REQUEST_FILENAME} -l [OR] - RewriteCond %{REQUEST_FILENAME} -d - RewriteRule ^.*$ - [NC,L] - RewriteRule ^.*$ index.php [NC,L] + + RewriteEngine on + RewriteBase {webPath}/ + RewriteCond %{REQUEST_FILENAME} -s [OR] + RewriteCond %{REQUEST_FILENAME} -l [OR] + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^.*$ - [NC,L] + RewriteRule ^.*$ index.php [NC,L] + + + + DirectoryIndex error_norewrite.html + ErrorDocument 404 /error_norewrite.html + + EOD; diff --git a/public/error_norewrite.html b/public/error_norewrite.html new file mode 100644 index 000000000..d85a00205 --- /dev/null +++ b/public/error_norewrite.html @@ -0,0 +1 @@ +

The rewrite module is not enabled

From 46df428e28acb9e17a9b81039cc5c6a431329766 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Thu, 13 Nov 2014 12:50:39 +0100 Subject: [PATCH 10/39] Add selection count hint in grids with help for hosts and services refs #7654 --- .../scripts/list/components/selectioninfo.phtml | 6 ++++++ .../application/views/scripts/list/hosts.phtml | 1 + .../views/scripts/list/services.phtml | 1 + modules/monitoring/public/css/module.less | 4 ++++ public/js/icinga/events.js | 3 +++ public/js/icinga/ui.js | 16 ++++++++++++++++ 6 files changed, 31 insertions(+) create mode 100644 modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml diff --git a/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml b/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml new file mode 100644 index 000000000..94a4b771a --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml @@ -0,0 +1,6 @@ +translate('Press and hold the Ctrl key while clicking on rows to select multiple rows or press and hold the Shift key to select a range of rows.', 'multiselection'); +?> +
+ 0 translate('row(s) selected', 'multiselection') ?> icon('unhandled.png', $helpMessage) ?> +
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml index 5be77f89f..5109b57aa 100644 --- a/modules/monitoring/application/views/scripts/list/hosts.phtml +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -16,6 +16,7 @@ if ($this->compact): ?> widget('limiter')->setMaxLimit($this->hosts->count()) ?> paginationControl($hosts, null, null, array('preserve' => $this->preserve)) ?> selectionToolbar('multi', $this->href('monitoring/hosts/show?' . $this->filter->toQueryString())) ?> +render('list/components/selectioninfo.phtml') ?>
diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml index 815d43074..e67be6d03 100644 --- a/modules/monitoring/application/views/scripts/list/services.phtml +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -20,6 +20,7 @@ if (!$this->compact): ?> paginationControl($services, null, null, array('preserve' => $this->preserve)) ?> selectionToolbar('multi', $this->href('monitoring/services/show?' . $this->filter->toQueryString())) ?> +render('list/components/selectioninfo.phtml') ?>
diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 6e0124ccd..7b9b58a87 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -141,3 +141,7 @@ table.avp .customvar ul { padding: 0; padding-left: 1.5em; } + +div.selection-info { + padding-top:1em; +} diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index e2d5f03bd..4e4e41bc1 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -306,17 +306,20 @@ var query = icinga.ui.selectionDataToQuery(selectionData); icinga.loader.loadUrl(url + '?' + query, $target); icinga.ui.storeSelectionData(selectionData); + icinga.ui.provideSelectionCount(); } else if ($trs.length === 1) { // display a single row $tr = $trs.first(); icinga.loader.loadUrl($tr.attr('href'), $target); icinga.ui.storeSelectionData($tr.attr('href')); + icinga.ui.provideSelectionCount(); } else { // display nothing if ($target.attr('id') === 'col2') { icinga.ui.layout1col(); } icinga.ui.storeSelectionData(null); + icinga.ui.provideSelectionCount(); } return false; diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index ac98fb14c..ed7cda584 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -470,9 +470,25 @@ * (when only a single row was selected) or Null when nothing was selected. */ loadSelectionData: function() { + this.provideSelectionCount(); return selectionData; }, + /** + * Set the selections row count hint info + */ + provideSelectionCount: function() { + var $count = $('.selection-info-count'); + + if (typeof selectionData === 'string') { + $count.text(1); + } else if (selectionData.length > 1) { + $count.text(selectionData.length); + } else { + $count.text(0); + } + }, + /** * Focus the given table by deselecting all selections on all other tables. * From 0d4ed40cb21daa6d830491d32af235a8fd84f36c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 12:52:21 +0100 Subject: [PATCH 11/39] CLI/Webserver Setup: Remove sapi stuff becuase it's nowhere used refs #6120 --- .../application/clicommands/ConfigCommand.php | 4 --- modules/setup/library/Setup/Webserver.php | 27 ------------------- 2 files changed, 31 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 606b0d74c..c4e4c1520 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -101,9 +101,6 @@ class ConfigCommand extends Command $this->fail($this->translate('Unknown type') . ': ' . $type); } $webserver->setApp($this->app); - if (($sapi = $this->params->get('sapi', 'server')) === null) { - $this->fail($this->translate('argument --sapi is mandatory.')); - } if (($path = $this->params->get('path', '/icingaweb')) === null) { $this->fail($this->translate('argument --path is mandatory.')); } @@ -112,7 +109,6 @@ class ConfigCommand extends Command } $webserver->setWebPath($path); $webserver->setPublicPath($publicPath); - $webserver->setSapi($sapi); $config = $webserver->generate() . "\n"; if (($file = $this->params->get('file')) !== null) { if (file_exists($file) === true) { diff --git a/modules/setup/library/Setup/Webserver.php b/modules/setup/library/Setup/Webserver.php index e06e9ed5f..708c366b8 100644 --- a/modules/setup/library/Setup/Webserver.php +++ b/modules/setup/library/Setup/Webserver.php @@ -19,13 +19,6 @@ abstract class Webserver */ protected $webPath; - /** - * SAPI name (e.g for cgi config generation) - * - * @var string - */ - protected $sapi; - /** * System path to public documents * @@ -88,26 +81,6 @@ abstract class Webserver */ abstract protected function getTemplate(); - /** - * Setter for SAPI name - * - * @param string $sapi - */ - public function setSapi($sapi) - { - $this->sapi = $sapi; - } - - /** - * Getter for SAPI name - * - * @return string - */ - public function getSapi() - { - return $this->sapi; - } - /** * Setter for web path * From 8594dc068af5519c1d2c22dea6c75409ad52ebe7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 13:16:49 +0100 Subject: [PATCH 12/39] CLI/Webserver Setup: Rename publicPath to documentRoot refs #6120 --- .../application/clicommands/ConfigCommand.php | 5 +- modules/setup/library/Setup/Webserver.php | 81 +++++++------------ .../setup/library/Setup/Webserver/Apache.php | 5 +- .../setup/library/Setup/Webserver/Nginx.php | 4 +- 4 files changed, 33 insertions(+), 62 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index c4e4c1520..1a328d01d 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -100,15 +100,14 @@ class ConfigCommand extends Command } catch (ProgrammingError $e) { $this->fail($this->translate('Unknown type') . ': ' . $type); } - $webserver->setApp($this->app); if (($path = $this->params->get('path', '/icingaweb')) === null) { $this->fail($this->translate('argument --path is mandatory.')); } - if (($publicPath = $this->params->get('publicPath', $webserver->getPublicPath())) === null) { + if (($documentRoot = $this->params->get('documentRoot', $webserver->getDocumentRoot())) === null) { $this->fail($this->translate('argument --publicPath is mandatory.')); } $webserver->setWebPath($path); - $webserver->setPublicPath($publicPath); + $webserver->setDocumentRoot($documentRoot); $config = $webserver->generate() . "\n"; if (($file = $this->params->get('file')) !== null) { if (file_exists($file) === true) { diff --git a/modules/setup/library/Setup/Webserver.php b/modules/setup/library/Setup/Webserver.php index 708c366b8..5cffe349c 100644 --- a/modules/setup/library/Setup/Webserver.php +++ b/modules/setup/library/Setup/Webserver.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Setup; -use Icinga\Application\ApplicationBootstrap; +use Icinga\Application\Icinga; use Icinga\Exception\ProgrammingError; /** @@ -12,6 +12,13 @@ use Icinga\Exception\ProgrammingError; */ abstract class Webserver { + /** + * Document root + * + * @var string + */ + protected $documentRoot; + /** * Web path * @@ -19,20 +26,6 @@ abstract class Webserver */ protected $webPath; - /** - * System path to public documents - * - * @var string - */ - protected $publicPath; - - /** - * Application - * - * @var ApplicationBootstrap - */ - protected $app; - /** * Create instance by type name * @@ -62,13 +55,13 @@ abstract class Webserver $searchTokens = array( '{webPath}', - '{publicPath}', + '{documentRoot}', '{configPath}', ); $replaceTokens = array( $this->getWebPath(), - $this->getPublicPath(), - $this->getApp()->getConfigDir() + $this->getDocumentRoot(), + Icinga::app()->getConfigDir() ); $template = str_replace($searchTokens, $replaceTokens, $template); return $template; @@ -102,58 +95,38 @@ abstract class Webserver } /** - * @param string $publicPath + * Set the document root + * + * @param string $documentRoot + * + * @return $this */ - public function setPublicPath($publicPath) + public function setDocumentRoot($documentRoot) { - $this->publicPath = $publicPath; + $this->documentRoot = (string) $documentRoot; + return $this; } /** - * Detect public root + * Detect the document root * * @return string */ - public function detectPublicPath() + public function detectDocumentRoot() { - $applicationPath = $this->getApp()->getApplicationDir(); - $applicationPath = dirname($applicationPath) . DIRECTORY_SEPARATOR . 'public'; - if (is_dir($applicationPath) === true) { - return $applicationPath; - } - return null; + return Icinga::app()->getBaseDir('public'); } /** - * Getter for public root + * Get the document root * * @return string */ - public function getPublicPath() + public function getDocumentRoot() { - if ($this->publicPath === null) { - $this->publicPath = $this->detectPublicPath(); + if ($this->documentRoot === null) { + $this->documentRoot = $this->detectDocumentRoot(); } - return $this->publicPath; - } - - /** - * Setter for application bootstrap - * - * @param ApplicationBootstrap $app - */ - public function setApp(ApplicationBootstrap $app) - { - $this->app = $app; - } - - /** - * Getter for application bootstrap - * - * @return ApplicationBootstrap - */ - public function getApp() - { - return $this->app; + return $this->documentRoot; } } diff --git a/modules/setup/library/Setup/Webserver/Apache.php b/modules/setup/library/Setup/Webserver/Apache.php index a565b9510..e25182fdf 100644 --- a/modules/setup/library/Setup/Webserver/Apache.php +++ b/modules/setup/library/Setup/Webserver/Apache.php @@ -17,9 +17,9 @@ class Apache extends Webserver protected function getTemplate() { return <<<'EOD' -Alias {webPath} "{publicPath}" +Alias {webPath} "{documentRoot}" - + Options SymLinksIfOwnerMatch AllowOverride None @@ -54,7 +54,6 @@ Alias {webPath} "{publicPath}" DirectoryIndex error_norewrite.html ErrorDocument 404 /error_norewrite.html - EOD; diff --git a/modules/setup/library/Setup/Webserver/Nginx.php b/modules/setup/library/Setup/Webserver/Nginx.php index 4725f4808..4885dc03b 100644 --- a/modules/setup/library/Setup/Webserver/Nginx.php +++ b/modules/setup/library/Setup/Webserver/Nginx.php @@ -24,11 +24,11 @@ location ~ ^{webPath}/index\.php(.*)$ { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; - fastcgi_param SCRIPT_FILENAME {publicPath}/index.php; + fastcgi_param SCRIPT_FILENAME {documentRoot}/index.php; } location ~ ^{webPath}(.+)? { - alias {publicPath}; + alias {documentRoot}; index index.php; try_files $1 $uri $uri/ {webPath}/index.php$is_args$args; } From e0f6ce36ea1ca376164894111feb48e8d00657bf Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 13:30:07 +0100 Subject: [PATCH 13/39] CLI/Webserver Setup: Fix argument sanity checks refs #6120 --- .../setup/application/clicommands/ConfigCommand.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 1a328d01d..d97ee8a15 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -100,11 +100,15 @@ class ConfigCommand extends Command } catch (ProgrammingError $e) { $this->fail($this->translate('Unknown type') . ': ' . $type); } - if (($path = $this->params->get('path', '/icingaweb')) === null) { - $this->fail($this->translate('argument --path is mandatory.')); + $path = $this->params->get('path', '/icingaweb'); + if (! is_string($path) || strlen(trim($path)) === 0) { + $this->fail($this->translate('The argument --path expects a URL path')); } - if (($documentRoot = $this->params->get('documentRoot', $webserver->getDocumentRoot())) === null) { - $this->fail($this->translate('argument --publicPath is mandatory.')); + $documentRoot = $this->params->get('documentRoot', $webserver->getDocumentRoot()); + if (! is_string($documentRoot) || strlen(trim($documentRoot)) === 0) { + $this->fail($this->translate( + 'The argument --documentRoot expects a directory from which the webserver will serve files' + )); } $webserver->setWebPath($path); $webserver->setDocumentRoot($documentRoot); From a0d4b6eee6cc34796ce2bae6339d5f6ff781c267 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 13:40:39 +0100 Subject: [PATCH 14/39] CLI/Webserver Setup: Don't use camelCase arguments refs #6120 --- .../setup/application/clicommands/ConfigCommand.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index d97ee8a15..4f0fcdf1b 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -73,18 +73,18 @@ class ConfigCommand extends Command * * OPTIONS: * - * --path= Path for the web server, default /icingaweb + * --path= The URL path to Icinga Web 2 * - * --publicPath= Path to htdocs system path + * --root/--document-root= The directory from which the webserver will serve files * - * --file= Write configuration to file + * --file= Write configuration to file * * * EXAMPLES: * * icingacli setup config webserver apache * - * icingacli setup config webserver apache --path /icingaweb --publicPath /usr/share/icingaweb/public + * icingacli setup config webserver apache --path /icingaweb --document-root /usr/share/icingaweb/public * * icingacli setup config webserver apache --file /etc/apache2/conf.d/icingaweb.conf * @@ -104,10 +104,10 @@ class ConfigCommand extends Command if (! is_string($path) || strlen(trim($path)) === 0) { $this->fail($this->translate('The argument --path expects a URL path')); } - $documentRoot = $this->params->get('documentRoot', $webserver->getDocumentRoot()); + $documentRoot = $this->params->get('root', $this->params->get('document-root', $webserver->getDocumentRoot())); if (! is_string($documentRoot) || strlen(trim($documentRoot)) === 0) { $this->fail($this->translate( - 'The argument --documentRoot expects a directory from which the webserver will serve files' + 'The argument --root/--document-root expects a directory from which the webserver will serve files' )); } $webserver->setWebPath($path); From 36d3c3e3d5db546d0d17a224368a3a64608f5e1f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 13:42:22 +0100 Subject: [PATCH 15/39] CLI/Webserver Setup: Don't print "# Your %s configuration" I want to copy the config only. refs #6120 --- modules/setup/application/clicommands/ConfigCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 4f0fcdf1b..0bf0313c3 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -125,7 +125,6 @@ class ConfigCommand extends Command Logger::info($this->translate('Successfully written %d bytes to file'), $re); return true; } - printf("# Your %s configuration:\n", $type); echo $config; return true; } From 0e64a8d5739b32ae16374738af298ce5d8de3c6a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 13:47:08 +0100 Subject: [PATCH 16/39] CLI/Webserver Setup: Prefix URL paths with '/' if missing refs #6120 --- .../application/clicommands/ConfigCommand.php | 5 +++-- modules/setup/library/Setup/Webserver.php | 15 +++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 0bf0313c3..04b518b07 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -110,8 +110,9 @@ class ConfigCommand extends Command 'The argument --root/--document-root expects a directory from which the webserver will serve files' )); } - $webserver->setWebPath($path); - $webserver->setDocumentRoot($documentRoot); + $webserver + ->setDocumentRoot($documentRoot) + ->setWebPath($path); $config = $webserver->generate() . "\n"; if (($file = $this->params->get('file')) !== null) { if (file_exists($file) === true) { diff --git a/modules/setup/library/Setup/Webserver.php b/modules/setup/library/Setup/Webserver.php index 5cffe349c..d8c18161c 100644 --- a/modules/setup/library/Setup/Webserver.php +++ b/modules/setup/library/Setup/Webserver.php @@ -75,17 +75,20 @@ abstract class Webserver abstract protected function getTemplate(); /** - * Setter for web path + * Set the URL path of Icinga Web 2 * - * @param string $webPath + * @param string $urlPath + * + * @return $this */ - public function setWebPath($webPath) + public function setWebPath($urlPath) { - $this->webPath = $webPath; + $this->webPath = '/' . ltrim(trim((string) $urlPath), '/'); + return $this; } /** - * Getter for web path + * Get the URL path of Icinga Web 2 * * @return string */ @@ -103,7 +106,7 @@ abstract class Webserver */ public function setDocumentRoot($documentRoot) { - $this->documentRoot = (string) $documentRoot; + $this->documentRoot = trim((string) $documentRoot); return $this; } From d0651e9e7fa8daac37e239bca7134ea5ae790011 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Thu, 13 Nov 2014 14:04:23 +0100 Subject: [PATCH 17/39] Rename 'service current state' to 'service detail information' header --- modules/monitoring/application/views/scripts/service/show.phtml | 2 +- modules/monitoring/application/views/scripts/show/service.phtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/service/show.phtml b/modules/monitoring/application/views/scripts/service/show.phtml index 5d0fd22c1..a5c95fd61 100644 --- a/modules/monitoring/application/views/scripts/service/show.phtml +++ b/modules/monitoring/application/views/scripts/service/show.phtml @@ -1,6 +1,6 @@
render('show/components/header.phtml') ?> -

translate("This service's current state") ?>

+

translate("Service detail information") ?>

render('show/components/output.phtml') ?> diff --git a/modules/monitoring/application/views/scripts/show/service.phtml b/modules/monitoring/application/views/scripts/show/service.phtml index f3cb1630c..126d60cb7 100644 --- a/modules/monitoring/application/views/scripts/show/service.phtml +++ b/modules/monitoring/application/views/scripts/show/service.phtml @@ -1,7 +1,7 @@ host_name !== false): ?>
render('show/components/header.phtml') ?> -

translate("This service's current state") ?>

+

translate("Service detail information") ?>

render('show/components/output.phtml') ?> From 459fc77fc7e7cef9e50f9a48fe51559de8e76335 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 14:05:13 +0100 Subject: [PATCH 18/39] CLI/Webserver Setup: Allow to define Icinga Web 2's configuration directory refs #6120 --- .../application/clicommands/ConfigCommand.php | 19 ++++++--- modules/setup/library/Setup/Webserver.php | 39 +++++++++++++++++-- .../setup/library/Setup/Webserver/Apache.php | 2 +- .../setup/library/Setup/Webserver/Nginx.php | 1 + 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 04b518b07..660207df1 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -73,18 +73,20 @@ class ConfigCommand extends Command * * OPTIONS: * - * --path= The URL path to Icinga Web 2 + * --path= The URL path to Icinga Web 2 [/icingaweb] * - * --root/--document-root= The directory from which the webserver will serve files + * --root/--document-root= The directory from which the webserver will serve files [./public] * - * --file= Write configuration to file + * --config= Path to Icinga Web 2's configuration files [/etc/icingaweb] + * + * --file= Write configuration to file [stdout] * * * EXAMPLES: * * icingacli setup config webserver apache * - * icingacli setup config webserver apache --path /icingaweb --document-root /usr/share/icingaweb/public + * icingacli setup config webserver apache --path /icingaweb --document-root /usr/share/icingaweb/public --config=/etc/icingaweb * * icingacli setup config webserver apache --file /etc/apache2/conf.d/icingaweb.conf * @@ -100,7 +102,7 @@ class ConfigCommand extends Command } catch (ProgrammingError $e) { $this->fail($this->translate('Unknown type') . ': ' . $type); } - $path = $this->params->get('path', '/icingaweb'); + $path = $this->params->get('path', $webserver->getWebPath()); if (! is_string($path) || strlen(trim($path)) === 0) { $this->fail($this->translate('The argument --path expects a URL path')); } @@ -110,8 +112,15 @@ class ConfigCommand extends Command 'The argument --root/--document-root expects a directory from which the webserver will serve files' )); } + $configDir = $this->params->get('config', $webserver->getConfigDir()); + if (! is_string($documentRoot) || strlen(trim($documentRoot)) === 0) { + $this->fail($this->translate( + 'The argument --config expects a path to Icinga Web 2\'s configuration files' + )); + } $webserver ->setDocumentRoot($documentRoot) + ->setConfigDir($configDir) ->setWebPath($path); $config = $webserver->generate() . "\n"; if (($file = $this->params->get('file')) !== null) { diff --git a/modules/setup/library/Setup/Webserver.php b/modules/setup/library/Setup/Webserver.php index d8c18161c..40951f6fa 100644 --- a/modules/setup/library/Setup/Webserver.php +++ b/modules/setup/library/Setup/Webserver.php @@ -24,7 +24,14 @@ abstract class Webserver * * @var string */ - protected $webPath; + protected $webPath = '/icingaweb'; + + /** + * Path to Icinga Web 2's configuration files + * + * @var string + */ + protected $configDir; /** * Create instance by type name @@ -56,12 +63,12 @@ abstract class Webserver $searchTokens = array( '{webPath}', '{documentRoot}', - '{configPath}', + '{configDir}', ); $replaceTokens = array( $this->getWebPath(), $this->getDocumentRoot(), - Icinga::app()->getConfigDir() + $this->getConfigDir() ); $template = str_replace($searchTokens, $replaceTokens, $template); return $template; @@ -132,4 +139,30 @@ abstract class Webserver } return $this->documentRoot; } + + /** + * Set the configuration directory + * + * @param string $configDir + * + * @return $this + */ + public function setConfigDir($configDir) + { + $this->configDir = (string) $configDir; + return $this; + } + + /** + * Get the configuration directory + * + * @return string + */ + public function getConfigDir() + { + if ($this->configDir === null) { + return Icinga::app()->getConfigDir(); + } + return $this->configDir; + } } diff --git a/modules/setup/library/Setup/Webserver/Apache.php b/modules/setup/library/Setup/Webserver/Apache.php index e25182fdf..f35c6954f 100644 --- a/modules/setup/library/Setup/Webserver/Apache.php +++ b/modules/setup/library/Setup/Webserver/Apache.php @@ -36,7 +36,7 @@ Alias {webPath} "{documentRoot}" Allow from all - SetEnv ICINGAWEB_CONFIGDIR /etc/icingaweb/ + SetEnv ICINGAWEB_CONFIGDIR "{configDir}" EnableSendfile Off diff --git a/modules/setup/library/Setup/Webserver/Nginx.php b/modules/setup/library/Setup/Webserver/Nginx.php index 4885dc03b..336456142 100644 --- a/modules/setup/library/Setup/Webserver/Nginx.php +++ b/modules/setup/library/Setup/Webserver/Nginx.php @@ -25,6 +25,7 @@ location ~ ^{webPath}/index\.php(.*)$ { fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME {documentRoot}/index.php; + fastcgi_param ICINGAWEB_CONFIGDIR {configDir}; } location ~ ^{webPath}(.+)? { From 7c8e9e6f5649c71912b3d5af555dcbbbc37d09b4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 14:18:14 +0100 Subject: [PATCH 19/39] CLI/Webserver Setup: Rename webPath to urlPath This is how the apache docs call it. Nginx speaks of uri. refs #6120 --- .../application/clicommands/ConfigCommand.php | 6 +++--- modules/setup/library/Setup/Webserver.php | 16 ++++++++-------- modules/setup/library/Setup/Webserver/Apache.php | 4 ++-- modules/setup/library/Setup/Webserver/Nginx.php | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/setup/application/clicommands/ConfigCommand.php b/modules/setup/application/clicommands/ConfigCommand.php index 660207df1..676572542 100644 --- a/modules/setup/application/clicommands/ConfigCommand.php +++ b/modules/setup/application/clicommands/ConfigCommand.php @@ -102,8 +102,8 @@ class ConfigCommand extends Command } catch (ProgrammingError $e) { $this->fail($this->translate('Unknown type') . ': ' . $type); } - $path = $this->params->get('path', $webserver->getWebPath()); - if (! is_string($path) || strlen(trim($path)) === 0) { + $urlPath = $this->params->get('path', $webserver->getUrlPath()); + if (! is_string($urlPath) || strlen(trim($urlPath)) === 0) { $this->fail($this->translate('The argument --path expects a URL path')); } $documentRoot = $this->params->get('root', $this->params->get('document-root', $webserver->getDocumentRoot())); @@ -121,7 +121,7 @@ class ConfigCommand extends Command $webserver ->setDocumentRoot($documentRoot) ->setConfigDir($configDir) - ->setWebPath($path); + ->setUrlPath($urlPath); $config = $webserver->generate() . "\n"; if (($file = $this->params->get('file')) !== null) { if (file_exists($file) === true) { diff --git a/modules/setup/library/Setup/Webserver.php b/modules/setup/library/Setup/Webserver.php index 40951f6fa..2449f9b9e 100644 --- a/modules/setup/library/Setup/Webserver.php +++ b/modules/setup/library/Setup/Webserver.php @@ -20,11 +20,11 @@ abstract class Webserver protected $documentRoot; /** - * Web path + * URL path of Icinga Web 2 * * @var string */ - protected $webPath = '/icingaweb'; + protected $urlPath = '/icingaweb'; /** * Path to Icinga Web 2's configuration files @@ -61,12 +61,12 @@ abstract class Webserver $template = $this->getTemplate(); $searchTokens = array( - '{webPath}', + '{urlPath}', '{documentRoot}', '{configDir}', ); $replaceTokens = array( - $this->getWebPath(), + $this->getUrlPath(), $this->getDocumentRoot(), $this->getConfigDir() ); @@ -88,9 +88,9 @@ abstract class Webserver * * @return $this */ - public function setWebPath($urlPath) + public function setUrlPath($urlPath) { - $this->webPath = '/' . ltrim(trim((string) $urlPath), '/'); + $this->urlPath = '/' . ltrim(trim((string) $urlPath), '/'); return $this; } @@ -99,9 +99,9 @@ abstract class Webserver * * @return string */ - public function getWebPath() + public function getUrlPath() { - return $this->webPath; + return $this->urlPath; } /** diff --git a/modules/setup/library/Setup/Webserver/Apache.php b/modules/setup/library/Setup/Webserver/Apache.php index f35c6954f..79d81eb71 100644 --- a/modules/setup/library/Setup/Webserver/Apache.php +++ b/modules/setup/library/Setup/Webserver/Apache.php @@ -17,7 +17,7 @@ class Apache extends Webserver protected function getTemplate() { return <<<'EOD' -Alias {webPath} "{documentRoot}" +Alias {urlPath} "{documentRoot}" Options SymLinksIfOwnerMatch @@ -42,7 +42,7 @@ Alias {webPath} "{documentRoot}" RewriteEngine on - RewriteBase {webPath}/ + RewriteBase {urlPath}/ RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d diff --git a/modules/setup/library/Setup/Webserver/Nginx.php b/modules/setup/library/Setup/Webserver/Nginx.php index 336456142..fd5e92d72 100644 --- a/modules/setup/library/Setup/Webserver/Nginx.php +++ b/modules/setup/library/Setup/Webserver/Nginx.php @@ -19,7 +19,7 @@ class Nginx extends Webserver protected function getTemplate() { return <<<'EOD' -location ~ ^{webPath}/index\.php(.*)$ { +location ~ ^{urlPath}/index\.php(.*)$ { # fastcgi_pass 127.0.0.1:9000; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; @@ -28,10 +28,10 @@ location ~ ^{webPath}/index\.php(.*)$ { fastcgi_param ICINGAWEB_CONFIGDIR {configDir}; } -location ~ ^{webPath}(.+)? { +location ~ ^{urlPath}(.+)? { alias {documentRoot}; index index.php; - try_files $1 $uri $uri/ {webPath}/index.php$is_args$args; + try_files $1 $uri $uri/ {urlPath}/index.php$is_args$args; } EOD; } From dc1960184d888e0fec54b825972566b0fd9a5c95 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Thu, 13 Nov 2014 14:18:19 +0100 Subject: [PATCH 20/39] Add hint in the grid if there are no active downtimes --- .../monitoring/application/views/scripts/list/downtimes.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index 25a2529f6..15c7ec5da 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -14,8 +14,8 @@
filterEditor ?> - - translate('No downtimes matching the filter'); ?> + + translate('No active downtimes'); ?>
From 6be51d15654885ab100c8f626cc72bd2a14c7065 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Nov 2014 14:25:51 +0100 Subject: [PATCH 21/39] Use Icinga\Web\Form\Element\Button instead of Zend's button Due to a bug in Zend prior to 1.12.2 we need our own button implementation to support earlier versions of the Zend Framework. The side effect is, that this button can also be used as submit button for our forms as isChecked will do its job now properly. --- library/Icinga/Web/Form/Element/Button.php | 81 ++++++++++++++++++++++ library/Icinga/Web/Wizard.php | 76 ++++++++++---------- 2 files changed, 122 insertions(+), 35 deletions(-) create mode 100644 library/Icinga/Web/Form/Element/Button.php diff --git a/library/Icinga/Web/Form/Element/Button.php b/library/Icinga/Web/Form/Element/Button.php new file mode 100644 index 000000000..00da28d10 --- /dev/null +++ b/library/Icinga/Web/Form/Element/Button.php @@ -0,0 +1,81 @@ + $options); + } + + if (!isset($options['ignore'])) { + $options['ignore'] = true; + } + + parent::__construct($spec, $options); + + if ($label = $this->getLabel()) { + // Necessary to get the label shown on the generated HTML + $this->content = $label; + } + } + + /** + * Validate element value (pseudo) + * + * There is no need to reset the value + * + * @param mixed $value Is always ignored + * @param mixed $context Is always ignored + * + * @return bool Returns always TRUE + */ + public function isValid($value, $context = null) + { + return true; + } + + /** + * Has this button been selected? + * + * @return bool + */ + public function isChecked() + { + return $this->getRequest()->getParam($this->getName()) === $this->getValue(); + } + + /** + * Return the current request + * + * @return Request + */ + protected function getRequest() + { + return Icinga::app()->getFrontController()->getRequest(); + } +} diff --git a/library/Icinga/Web/Wizard.php b/library/Icinga/Web/Wizard.php index faf950292..424b46b2c 100644 --- a/library/Icinga/Web/Wizard.php +++ b/library/Icinga/Web/Wizard.php @@ -8,6 +8,7 @@ use LogicException; use InvalidArgumentException; use Icinga\Web\Session\SessionNamespace; use Icinga\Web\Form\Decorator\ElementDoubler; +use Icinga\Web\Form\Element\Button; /** * Container and controller for form based wizards @@ -426,55 +427,60 @@ class Wizard $index = array_search($page, $pages, true); if ($index === 0) { $page->addElement( - 'button', - static::BTN_NEXT, - array( - 'type' => 'submit', - 'value' => $pages[1]->getName(), - 'label' => t('Next'), - 'decorators' => array('ViewHelper') + new Button( + static::BTN_NEXT, + array( + 'type' => 'submit', + 'value' => $pages[1]->getName(), + 'label' => t('Next'), + 'decorators' => array('ViewHelper') + ) ) ); } elseif ($index < count($pages) - 1) { $page->addElement( - 'button', - static::BTN_PREV, - array( - 'type' => 'submit', - 'value' => $pages[$index - 1]->getName(), - 'label' => t('Back'), - 'decorators' => array('ViewHelper') + new Button( + static::BTN_PREV, + array( + 'type' => 'submit', + 'value' => $pages[$index - 1]->getName(), + 'label' => t('Back'), + 'decorators' => array('ViewHelper') + ) ) ); $page->addElement( - 'button', - static::BTN_NEXT, - array( - 'type' => 'submit', - 'value' => $pages[$index + 1]->getName(), - 'label' => t('Next'), - 'decorators' => array('ViewHelper') + new Button( + static::BTN_NEXT, + array( + 'type' => 'submit', + 'value' => $pages[$index + 1]->getName(), + 'label' => t('Next'), + 'decorators' => array('ViewHelper') + ) ) ); } else { $page->addElement( - 'button', - static::BTN_PREV, - array( - 'type' => 'submit', - 'value' => $pages[$index - 1]->getName(), - 'label' => t('Back'), - 'decorators' => array('ViewHelper') + new Button( + static::BTN_PREV, + array( + 'type' => 'submit', + 'value' => $pages[$index - 1]->getName(), + 'label' => t('Back'), + 'decorators' => array('ViewHelper') + ) ) ); $page->addElement( - 'button', - static::BTN_NEXT, - array( - 'type' => 'submit', - 'value' => $page->getName(), - 'label' => t('Finish'), - 'decorators' => array('ViewHelper') + new Button( + static::BTN_NEXT, + array( + 'type' => 'submit', + 'value' => $page->getName(), + 'label' => t('Finish'), + 'decorators' => array('ViewHelper') + ) ) ); } From 4595b5131858809d830b3d014fa04a106b02947b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Nov 2014 14:34:04 +0100 Subject: [PATCH 22/39] Remove Zend version check refs #7464 --- modules/setup/library/Setup/WebWizard.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/modules/setup/library/Setup/WebWizard.php b/modules/setup/library/Setup/WebWizard.php index 9c5ec76e7..c33fceb92 100644 --- a/modules/setup/library/Setup/WebWizard.php +++ b/modules/setup/library/Setup/WebWizard.php @@ -5,7 +5,6 @@ namespace Icinga\Module\Setup; use PDOException; -use Zend_Version; use Icinga\Web\Form; use Icinga\Web\Wizard; use Icinga\Web\Request; @@ -384,20 +383,6 @@ class WebWizard extends Wizard implements SetupWizard sprintf(mt('setup', 'You are running PHP version %s.'), $phpVersion) ); - // The only reason for requiring 1.12.2 is a bug in Zend_Form_Decorator_ViewHelper in older versions causing a - // Zend_Form_Element_Button's value not being rendered. Feel free to adjust this in case we require an earlier - // version! - $zendVersion = Zend_Version::VERSION; - $requirements->addMandatory( - mt('setup', 'Zend Framework 1'), - mt( - 'setup', - 'Icinga Web 2 requires at least Zend Framework 1.12.2 to work properly.' - ), - Zend_Version::compareVersion('1.12.2') !== 1, - sprintf(mt('setup', 'You have got Zend Framwork %s installed.'), $zendVersion) - ); - $defaultTimezone = Platform::getPhpConfig('date.timezone'); $requirements->addMandatory( mt('setup', 'Default Timezone'), From 688c016f2d7fb36ad638000af5ad794467b1ea83 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Nov 2014 14:34:31 +0100 Subject: [PATCH 23/39] Re-enable requirement checks for Zend_Db_Adapter_* refs #7464 --- modules/setup/library/Setup/WebWizard.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/setup/library/Setup/WebWizard.php b/modules/setup/library/Setup/WebWizard.php index c33fceb92..469f25e02 100644 --- a/modules/setup/library/Setup/WebWizard.php +++ b/modules/setup/library/Setup/WebWizard.php @@ -492,8 +492,7 @@ class WebWizard extends Wizard implements SetupWizard ) ); - // TODO(7464): Re-enable or remove this entirely once a decision has been made regarding shipping Zend with Iw2 - /*$mysqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Mysql'); + $mysqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Mysql'); $requirements->addOptional( mt('setup', 'Zend Database Adapter For MySQL'), mt('setup', 'The Zend database adapter for MySQL is required to access a MySQL database.'), @@ -511,7 +510,7 @@ class WebWizard extends Wizard implements SetupWizard $pgsqlAdapterFound ? mt('setup', 'The Zend database adapter for PostgreSQL is available.') : ( mt('setup', 'The Zend database adapter for PostgreSQL is missing.') ) - );*/ + ); $configDir = $this->getConfigDir(); $requirements->addMandatory( From 1aa8858dca2b4ae40f9bc05bad5f1d0dfd743edd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:17:36 +0100 Subject: [PATCH 24/39] helpers/icon: support webfont shortcuts * set icon class instead of icon image if no such is given * fix TODO: merge parameter classes refs #6936 --- library/Icinga/Web/View/helpers/url.php | 37 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/library/Icinga/Web/View/helpers/url.php b/library/Icinga/Web/View/helpers/url.php index 935a55a97..901e68803 100644 --- a/library/Icinga/Web/View/helpers/url.php +++ b/library/Icinga/Web/View/helpers/url.php @@ -51,19 +51,32 @@ $this->addHelperFunction('img', function ($url, array $properties = array()) use }); $this->addHelperFunction('icon', function ($img, $title = null, array $properties = array()) use ($view) { - // TODO: join with classes passed in $properties? - $attributes = array( - 'class' => 'icon', - ); - if ($title !== null) { - $attributes['alt'] = $title; - $attributes['title'] = $title; - } + $isClass = strpos($img, '.') === false; + $class = null; - return $view->img( - 'img/icons/' . $img, - array_merge($attributes, $properties) - ); + if ($isClass) { + $class = 'icon-' . $img; + } else { + $class = 'icon'; + } + if ($title !== null) { + $properties['alt'] = $title; + $properties['title'] = $title; + } + + if ($class !== null) { + if (isset($props['class'])) { + $properties['class'] .= ' ' . $class; + } else { + $properties['class'] = $class; + } + } + if ($isClass) { + return sprintf('', $view->propertiesToString($properties)); + + } else { + return $view->img('img/icons/' . $img, $properties); + } }); $this->addHelperFunction('propertiesToString', function ($properties) use ($view) { From 29c7c2e7079814358436dcde071ef118bcd29af2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:21:49 +0100 Subject: [PATCH 25/39] Web\MenuRenderer: support webfont icons refs #6936 --- library/Icinga/Web/MenuRenderer.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/Icinga/Web/MenuRenderer.php b/library/Icinga/Web/MenuRenderer.php index bbd5c208d..8502e5215 100644 --- a/library/Icinga/Web/MenuRenderer.php +++ b/library/Icinga/Web/MenuRenderer.php @@ -114,6 +114,14 @@ class MenuRenderer extends RecursiveIteratorIterator Logger::error('Could not invoke custom renderer. Exception: '. $e->getMessage()); } } + if ($child->getIcon() && strpos($child->getIcon(), '.') === false) { + return sprintf( + '%s', + $child->getUrl() ?: '#', + $child->getIcon(), + htmlspecialchars($child->getTitle()) + ); + } return sprintf( '%s%s', $child->getUrl() ?: '#', From b16959bc448b4c8de542a9e85538866b7a58f7c1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:22:47 +0100 Subject: [PATCH 26/39] Widget\Tab: support webfont icons refs #6936 --- library/Icinga/Web/Widget/Tab.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Widget/Tab.php b/library/Icinga/Web/Widget/Tab.php index 28c04e6ab..bf7ea9bcc 100644 --- a/library/Icinga/Web/Widget/Tab.php +++ b/library/Icinga/Web/Widget/Tab.php @@ -197,11 +197,18 @@ class Tab extends AbstractWidget public function render() { $view = $this->view(); - $class = $this->active ? ' class="active" ' : ''; + $classes = array(); + if ($this->active) { + $classes[] = 'active'; + } $caption = $view->escape($this->title); if ($this->icon !== null) { - $caption = $view->img($this->icon, array('class' => 'icon')) . $caption; + if (strpos($this->icon, '.') === false) { + $classes[] = 'icon-' . $this->icon; + } else { + $caption = $view->img($this->icon, array('class' => 'icon')) . $caption; + } } if ($this->url !== null) { $this->url->overwriteParams($this->urlParams); @@ -218,6 +225,7 @@ class Tab extends AbstractWidget } else { $tab = $caption; } + $class = empty($classes) ? '' : sprintf(' class="%s"', implode(' ', $classes)); return '
  • ' . $tab . "
  • \n"; } } From cab89914bbc9be84f2eea48d307f91db78193c95 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Thu, 13 Nov 2014 15:28:10 +0100 Subject: [PATCH 27/39] Add clickable icon for command check now --- public/css/icinga/forms.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index bf2f600c5..91bade8c3 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -101,7 +101,7 @@ form.inline { display: inline; } -form.link-like input[type="submit"] { +form.link-like input[type="submit"], form.link-like button[type="submit"] { color: @colorLinkDefault; font-weight: normal; border: none; @@ -111,7 +111,7 @@ form.link-like input[type="submit"] { cursor: pointer; } -form.link-like input[type="submit"]:hover, form.link-like input[type="submit"]:focus { +form.link-like input[type="submit"]:hover, form.link-like input[type="submit"]:focus, form.link-like button[type="submit"]:hover, orm.link-like button[type="submit"]:focus { text-decoration: underline; background: none; color: @colorLinkDefault; From 50869c783db180ab91d8d162e50aa23f25ffe9d7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:28:41 +0100 Subject: [PATCH 28/39] MonitoringMenuItemRenderer: webfont icon support refs #6936 --- library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php b/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php index 63e819d1b..6493497af 100644 --- a/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php +++ b/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php @@ -79,6 +79,16 @@ class MonitoringMenuItemRenderer implements MenuItemRenderer { $count ); } + if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) { + return sprintf( + '%s%s', + $menu->getUrl() ?: '#', + $menu->getIcon(), + htmlspecialchars($menu->getTitle()), + $badge + ); + } + return sprintf( '%s%s%s', $menu->getUrl() ?: '#', From 47b1189af4f685ae7fb416cdb3599add1af92041 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:40:20 +0100 Subject: [PATCH 29/39] application/fonts: initial import First sample font, to be replaced. refs #6936 --- .../fonts/fontanello-ifont/LICENSE.txt | 21 + application/fonts/fontanello-ifont/README.txt | 75 +++ .../fonts/fontanello-ifont/config.json | 568 ++++++++++++++++++ .../fonts/fontanello-ifont/css/animation.css | 85 +++ .../fontanello-ifont/css/ifont-codes.css | 94 +++ .../fontanello-ifont/css/ifont-embedded.css | 147 +++++ .../fontanello-ifont/css/ifont-ie7-codes.css | 94 +++ .../fonts/fontanello-ifont/css/ifont-ie7.css | 105 ++++ .../fonts/fontanello-ifont/css/ifont.css | 145 +++++ application/fonts/fontanello-ifont/demo.html | 400 ++++++++++++ .../fonts/fontanello-ifont/font/ifont.eot | Bin 0 -> 33856 bytes .../fonts/fontanello-ifont/font/ifont.svg | 104 ++++ .../fonts/fontanello-ifont/font/ifont.ttf | Bin 0 -> 33700 bytes .../fonts/fontanello-ifont/font/ifont.woff | Bin 0 -> 19464 bytes 14 files changed, 1838 insertions(+) create mode 100644 application/fonts/fontanello-ifont/LICENSE.txt create mode 100644 application/fonts/fontanello-ifont/README.txt create mode 100644 application/fonts/fontanello-ifont/config.json create mode 100644 application/fonts/fontanello-ifont/css/animation.css create mode 100644 application/fonts/fontanello-ifont/css/ifont-codes.css create mode 100644 application/fonts/fontanello-ifont/css/ifont-embedded.css create mode 100644 application/fonts/fontanello-ifont/css/ifont-ie7-codes.css create mode 100644 application/fonts/fontanello-ifont/css/ifont-ie7.css create mode 100644 application/fonts/fontanello-ifont/css/ifont.css create mode 100644 application/fonts/fontanello-ifont/demo.html create mode 100644 application/fonts/fontanello-ifont/font/ifont.eot create mode 100644 application/fonts/fontanello-ifont/font/ifont.svg create mode 100644 application/fonts/fontanello-ifont/font/ifont.ttf create mode 100644 application/fonts/fontanello-ifont/font/ifont.woff diff --git a/application/fonts/fontanello-ifont/LICENSE.txt b/application/fonts/fontanello-ifont/LICENSE.txt new file mode 100644 index 000000000..a00d23db2 --- /dev/null +++ b/application/fonts/fontanello-ifont/LICENSE.txt @@ -0,0 +1,21 @@ +Font license info + + +## Font Awesome + + Copyright (C) 2012 by Dave Gandy + + Author: Dave Gandy + License: SIL () + Homepage: http://fortawesome.github.com/Font-Awesome/ + + +## Iconic + + Copyright (C) 2012 by P.J. Onori + + Author: P.J. Onori + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://somerandomdude.com/work/iconic/ + + diff --git a/application/fonts/fontanello-ifont/README.txt b/application/fonts/fontanello-ifont/README.txt new file mode 100644 index 000000000..43e23f283 --- /dev/null +++ b/application/fonts/fontanello-ifont/README.txt @@ -0,0 +1,75 @@ +This webfont is generated by http://fontello.com open source project. + + +================================================================================ +Please, note, that you should obey original font licences, used to make this +webfont pack. Details available in LICENSE.txt file. + +- Usually, it's enough to publish content of LICENSE.txt file somewhere on your + site in "About" section. + +- If your project is open-source, usually, it will be ok to make LICENSE.txt + file publically available in your repository. + +- Fonts, used in Fontello, don't require to make clickable links on your site. + But any kind of additional authors crediting is welcome. +================================================================================ + + +Comments on archive content +--------------------------- + +- /font/* - fonts in different formats + +- /css/* - different kinds of css, for all situations. Should be ok with + twitter bootstrap. Also, you can skip style and assign icon classes + directly to text elements, if you don't mind about IE7. + +- demo.html - demo file, to show your webfont content + +- LICENSE.txt - license info about source fonts, used to build your one. + +- config.json - keeps your settings. You can import it back to fontello anytime, + to continue your work + + +Why so many CSS files ? +----------------------- + +Because we like to fit all your needs :) + +- basic file, .css - is usually enougth, in contains @font-face + and character codes definition + +- *-ie7.css - if you need IE7 support, but still don't wish to put char codes + directly into html + +- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face + rules, but still wish to benefit of css generation. That can be very + convenient for automated assets build systems. When you need to update font - + no needs to manually edit files, just override old version with archive + content. See fontello source codes for example. + +- *-embedded.css - basic css file, but with embedded WOFF font, to avoid + CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain. + We strongly recommend to resolve this issue by `Access-Control-Allow-Origin` + server headers. But if you ok with dirty hack - this file is for you. Note, + that data url moved to separate @font-face to avoid problems with + + + + + + + + +
    +

    + Icinga Web 2 + font demo +

    + +
    +
    +
    +
    icon-th0xe800
    +
    icon-user0xe801
    +
    icon-users0xe802
    +
    icon-ok0xe803
    +
    +
    +
    icon-cancel0xe804
    +
    icon-plus0xe805
    +
    icon-minus0xe806
    +
    icon-folder-empty0xe807
    +
    +
    +
    icon-download0xe808
    +
    icon-upload0xe809
    +
    icon-git0xe80a
    +
    icon-cubes0xe80b
    +
    +
    +
    icon-database0xe80c
    +
    icon-gauge0xe80d
    +
    icon-sitemap0xe80e
    +
    icon-sort-name-up0xe80f
    +
    +
    +
    icon-sort-name-down0xe810
    +
    icon-megaphone0xe811
    +
    icon-bug0xe812
    +
    icon-tasks0xe813
    +
    +
    +
    icon-filter0xe814
    +
    icon-off0xe815
    +
    icon-book0xe816
    +
    icon-paste0xe817
    +
    +
    +
    icon-scissors0xe818
    +
    icon-globe0xe819
    +
    icon-cloud0xe81a
    +
    icon-flash0xe81b
    +
    +
    +
    icon-signal0xe81c
    +
    icon-down-dir0xe81d
    +
    icon-up-dir0xe81e
    +
    icon-left-dir0xe81f
    +
    +
    +
    icon-right-dir0xe820
    +
    icon-down-open0xe821
    +
    icon-right-open0xe822
    +
    icon-up-open0xe823
    +
    +
    +
    icon-left-open0xe824
    +
    icon-up-big0xe825
    +
    icon-right-big0xe826
    +
    icon-left-big0xe827
    +
    +
    +
    icon-down-big0xe828
    +
    icon-resize-full-alt0xe829
    +
    icon-resize-full0xe82a
    +
    icon-resize-small0xe82b
    +
    +
    +
    icon-move0xe82c
    +
    icon-resize-horizontal0xe82d
    +
    icon-resize-vertical0xe82e
    +
    icon-zoom-in0xe82f
    +
    +
    +
    icon-block0xe830
    +
    icon-zoom-out0xe831
    +
    icon-lightbulb0xe832
    +
    icon-clock0xe833
    +
    +
    +
    icon-volume-up0xe834
    +
    icon-volume-down0xe835
    +
    icon-volume-off0xe836
    +
    icon-mute0xe837
    +
    +
    +
    icon-mic0xe838
    +
    icon-login0xe839
    +
    icon-logout0xe83a
    +
    icon-calendar-empty0xe83b
    +
    +
    +
    icon-calendar0xe83c
    +
    icon-wrench0xe83d
    +
    icon-sliders0xe83e
    +
    icon-cog-alt0xe83f
    +
    +
    +
    icon-cog0xe840
    +
    icon-phone0xe841
    +
    icon-file-pdf0xe842
    +
    icon-file-word0xe843
    +
    +
    +
    icon-file-excel0xe844
    +
    icon-doc-text0xe845
    +
    icon-trash0xe846
    +
    icon-comment-empty0xe847
    +
    +
    +
    icon-comment0xe848
    +
    icon-chat0xe849
    +
    icon-chat-empty0xe84a
    +
    icon-bell0xe84b
    +
    +
    +
    icon-bell-alt0xe84c
    +
    icon-attention-alt0xe84d
    +
    icon-print0xe84e
    +
    icon-edit0xe84f
    +
    +
    +
    icon-forward0xe850
    +
    icon-reply0xe851
    +
    icon-reply-all0xe852
    +
    icon-eye0xe853
    +
    +
    +
    icon-tag0xe854
    +
    icon-tags0xe855
    +
    icon-lock-open-alt0xe856
    +
    icon-lock-open0xe857
    +
    +
    +
    icon-lock0xe858
    +
    icon-home0xe859
    +
    icon-info0xe85a
    +
    icon-help0xe85b
    +
    +
    +
    icon-search0xe85c
    +
    +
    + + diff --git a/application/fonts/fontanello-ifont/font/ifont.eot b/application/fonts/fontanello-ifont/font/ifont.eot new file mode 100644 index 0000000000000000000000000000000000000000..41d098f80a095042602b9992d49fd6d7991a025a GIT binary patch literal 33856 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 JAhQw1{x=<7{@MTl literal 0 HcmV?d00001 diff --git a/application/fonts/fontanello-ifont/font/ifont.svg b/application/fonts/fontanello-ifont/font/ifont.svg new file mode 100644 index 000000000..17e4581c7 --- /dev/null +++ b/application/fonts/fontanello-ifont/font/ifont.svg @@ -0,0 +1,104 @@ + + + +Copyright (C) 2014 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/fonts/fontanello-ifont/font/ifont.ttf b/application/fonts/fontanello-ifont/font/ifont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2ec7727904392c11952cd60fcbeb8e7183d3c51c GIT binary patch literal 33700 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 I1pq_<0H&G`vH$=8 literal 0 HcmV?d00001 From 1c0e427d3bd249cec94f950bb54ddf5b6392ed37 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:41:13 +0100 Subject: [PATCH 30/39] Web\StyleSheet: compile webfont too refs #6936 --- library/Icinga/Web/StyleSheet.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index 062b7d86f..53415628c 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -25,6 +25,7 @@ class StyleSheet 'css/icinga/monitoring-colors.less', 'css/icinga/selection-toolbar.less', 'css/icinga/login.less', + '../application/fonts/fontanello-ifont/css/ifont-embedded.css', 'css/vendor/tipsy.css' ); From 28c239c6fd6d5ce28386a34f96d0d14b8af5e8ef Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:42:25 +0100 Subject: [PATCH 31/39] Web\Controller: initial commit * This is the controller all modules should inherit from. * We will flip code with the ModuleActionController as soon as a couple of pending feature branches are merged back to the master. --- library/Icinga/Web/Controller.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 library/Icinga/Web/Controller.php diff --git a/library/Icinga/Web/Controller.php b/library/Icinga/Web/Controller.php new file mode 100644 index 000000000..111ee244c --- /dev/null +++ b/library/Icinga/Web/Controller.php @@ -0,0 +1,14 @@ + Date: Thu, 13 Nov 2014 15:54:31 +0100 Subject: [PATCH 32/39] doc/style: show web font icon set refs #6936 --- .../application/controllers/StyleController.php | 13 +++++++++++++ .../doc/application/views/scripts/style/font.phtml | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 modules/doc/application/controllers/StyleController.php create mode 100644 modules/doc/application/views/scripts/style/font.phtml diff --git a/modules/doc/application/controllers/StyleController.php b/modules/doc/application/controllers/StyleController.php new file mode 100644 index 000000000..dc61a97b0 --- /dev/null +++ b/modules/doc/application/controllers/StyleController.php @@ -0,0 +1,13 @@ +getApplicationDir('fonts/fontanello-ifont/config.json'); + $this->view->font = json_decode(file_get_contents($confFile)); + } +} diff --git a/modules/doc/application/views/scripts/style/font.phtml b/modules/doc/application/views/scripts/style/font.phtml new file mode 100644 index 000000000..97d0c61b7 --- /dev/null +++ b/modules/doc/application/views/scripts/style/font.phtml @@ -0,0 +1,14 @@ +

    +

    Icinga Web 2 Icons

    +
    + +
    +font->glyphs as $icon): ?> + +
    +escape($icon->css) ?> (0xcode) ?>) +
    + +
    From dedb348af90e4ba89f409bef1aedd5e18eed9136 Mon Sep 17 00:00:00 2001 From: Alexander Fuhr Date: Thu, 13 Nov 2014 15:38:44 +0100 Subject: [PATCH 33/39] Add clickable icon for command check now 2 --- .../Command/Object/CheckNowCommandForm.php | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php index 14e7be011..aa109ff7f 100644 --- a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php @@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring\Form\Command\Object; use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand; use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand; +use Icinga\Web\Form\Element\Button; use Icinga\Web\Form\Element\Note; use Icinga\Web\Notification; use Icinga\Web\Request; @@ -30,23 +31,19 @@ class CheckNowCommandForm extends ObjectsCommandForm */ public function addSubmitButton() { + $iconUrl = $this->getView()->href('img/icons/refresh_petrol.png'); + $this->addElements(array( - new Note( - 'icon', // Bogus - array( - 'decorators' => array(array( - 'HtmlTag', - array('tag' => 'img', 'src' => $this->getView()->href('img/icons/refresh_petrol.png')) - )) - ) - ), - array( - 'submit', + new Button( 'btn_submit', array( 'ignore' => true, - 'label' => mt('monitoring', 'Check now'), - 'decorators' => array('ViewHelper') + 'type' => 'submit', + 'value' => mt('monitoring', 'Check now'), + 'label' => ' ' . mt('monitoring', 'Check now'), + 'decorators' => array('ViewHelper'), + 'escape' => false, + 'class' => 'link-like' ) ) )); From c68f9502ea0fcbefd72222b1524dabe2ea8aa25b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 15:56:34 +0100 Subject: [PATCH 34/39] modules/doc: restructure menu Moved first hierarchy into the menu, added font style link refs #6936 --- modules/doc/configuration.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/doc/configuration.php b/modules/doc/configuration.php index ce3f99113..9332bf9cb 100644 --- a/modules/doc/configuration.php +++ b/modules/doc/configuration.php @@ -8,5 +8,16 @@ $section = $this->menuSection($this->translate('Documentation'), array( 'title' => 'Documentation', 'icon' => 'img/icons/comment.png', 'url' => 'doc', - 'priority' => 80 + 'priority' => 190 +)); + +$section->add('Icinga Web 2', array( + 'url' => 'doc/icingaweb/toc', +)); +$section->add('Module documentations', array( + 'url' => 'doc/module', +)); +$section->add($this->translate('Fonts'), array( + 'url' => 'doc/style/font', + 'priority' => 200, )); From f86d3e76367ac1f6800ca62e9e23fb293cb8fc11 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 13 Nov 2014 16:35:26 +0100 Subject: [PATCH 35/39] Web\StyleSheet: PCRE problem workaround, PHP5.3.7 Lines with embedded fonts where too long for lessphp's PCRE matches unless PHP 5.3.7 --- library/Icinga/Web/StyleSheet.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index 53415628c..ec926d992 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -31,6 +31,7 @@ class StyleSheet public static function compileForPdf() { + self::checkPhp(); $less = new LessCompiler(); $basedir = Icinga::app()->getBootstrapDirectory(); foreach (self::$lessFiles as $file) { @@ -55,8 +56,17 @@ class StyleSheet ); } + protected static function checkPhp() + { + // PHP had a rather conservative PCRE backtrack limit unless 5.3.7 + if (version_compare(PHP_VERSION, '5.3.7') <= 0) { + ini_set('pcre.backtrack_limit', 1000000); + } + } + public static function send($minified = false) { + self::checkPhp(); $app = Icinga::app(); $basedir = $app->getBootstrapDirectory(); foreach (self::$lessFiles as $file) { From 77f5bc39321528381b9d0540927b242b2f12513a Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Nov 2014 16:32:20 +0100 Subject: [PATCH 36/39] Use GROUPBY instead of DISTINCT and subqueries when counting --- library/Icinga/Data/Db/DbQuery.php | 8 +++++++- library/Icinga/Data/PivotTable.php | 30 +++++++++++------------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 94765d98f..56118edcf 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -72,6 +72,12 @@ class DbQuery extends SimpleQuery parent::init(); } + public function setUseSubqueryCount($useSubqueryCount = true) + { + $this->useSubqueryCount = $useSubqueryCount; + return $this; + } + public function where($condition, $value = null) { // $this->count = $this->select = null; @@ -259,7 +265,7 @@ class DbQuery extends SimpleQuery */ public function getCountQuery() { - // TODO: there may be situations where we should clone the "select" + // TODO: there may be situations where we should clone the "select" $count = $this->dbSelect(); $this->applyFilterSql($count); diff --git a/library/Icinga/Data/PivotTable.php b/library/Icinga/Data/PivotTable.php index ab6fab13d..0a1f009fe 100644 --- a/library/Icinga/Data/PivotTable.php +++ b/library/Icinga/Data/PivotTable.php @@ -69,11 +69,13 @@ class PivotTable protected function prepareQueries() { $this->xAxisQuery = clone $this->baseQuery; - $this->xAxisQuery->distinct(); + $this->xAxisQuery->group($this->xAxisColumn); $this->xAxisQuery->columns(array($this->xAxisColumn)); + $this->xAxisQuery->setUseSubqueryCount(); $this->yAxisQuery = clone $this->baseQuery; - $this->yAxisQuery->distinct(); + $this->yAxisQuery->group($this->yAxisColumn); $this->yAxisQuery->columns(array($this->yAxisColumn)); + $this->yAxisQuery->setUseSubqueryCount(); return $this; } @@ -85,24 +87,14 @@ class PivotTable */ protected function adjustSorting() { - $currentOrderColumns = $this->baseQuery->getOrder(); - $xAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->xAxisColumn), SimpleQuery::SORT_ASC)); - $yAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->yAxisColumn), SimpleQuery::SORT_ASC)); - - foreach ($currentOrderColumns as $orderInfo) { - if ($orderInfo[0] === $xAxisOrderColumns[0][0]) { - $xAxisOrderColumns[0] = $orderInfo; - } elseif ($orderInfo[0] === $yAxisOrderColumns[0][0]) { - $yAxisOrderColumns[0] = $orderInfo; - } else { - $xAxisOrderColumns[] = $orderInfo; - $yAxisOrderColumns[] = $orderInfo; - } + if (false === $this->xAxisQuery->hasOrder($this->xAxisColumn)) { + $this->xAxisQuery->order($this->xAxisColumn, 'ASC'); } -//TODO: simplify this whole function. No need to care about mapping -// foreach ($xAxisOrderColumns as -// $this->xAxisQuery->setOrder($xAxisOrderColumns); -// $this->yAxisQuery->setOrder($yAxisOrderColumns); + + if (false === $this->yAxisQuery->hasOrder($this->yAxisColumn)) { + $this->yAxisQuery->order($this->yAxisColumn, 'ASC'); + } + return $this; } From 0e340015686a95e80062f9f08a6705ad3a276de2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 16:33:10 +0100 Subject: [PATCH 37/39] Use automatically a subquery when counting with groups --- library/Icinga/Data/Db/DbQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 56118edcf..50bdecf00 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -269,7 +269,7 @@ class DbQuery extends SimpleQuery $count = $this->dbSelect(); $this->applyFilterSql($count); - if ($this->useSubqueryCount) { + if ($this->useSubqueryCount || $this->group) { $count->columns($this->columns); $columns = array('cnt' => 'COUNT(*)'); return $this->db->select()->from($count, $columns); From 94f85972714fc8d52c190012a42dfd926bab5c97 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Nov 2014 16:34:27 +0100 Subject: [PATCH 38/39] Add existing GROUPBYs to count queries --- library/Icinga/Data/Db/DbQuery.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 50bdecf00..5062c8caf 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -267,7 +267,9 @@ class DbQuery extends SimpleQuery { // TODO: there may be situations where we should clone the "select" $count = $this->dbSelect(); - + if ($this->group) { + $count->group($this->group); + } $this->applyFilterSql($count); if ($this->useSubqueryCount || $this->group) { $count->columns($this->columns); From f49d9e8591c497fc2767be234a31002c334d7d77 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Nov 2014 16:45:03 +0100 Subject: [PATCH 39/39] Fix the tooltip's max page count in the joystick navigation --- application/views/scripts/joystickPagination.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/views/scripts/joystickPagination.phtml b/application/views/scripts/joystickPagination.phtml index 1808ba7e3..cf6afbd70 100644 --- a/application/views/scripts/joystickPagination.phtml +++ b/application/views/scripts/joystickPagination.phtml @@ -68,7 +68,7 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 : $fromTo, t('Services'), $currentXAxisPage * $xAxisPages->itemCountPerPage + 1, - $nextXAxisPage * $xAxisPages->itemCountPerPage, + $nextXAxisPage === $xAxisPages->last ? $xAxisPages->totalItemCount : $nextXAxisPage * $xAxisPages->itemCountPerPage, $xAxisPages->totalItemCount ); ?>">icon('next_petrol.png'); ?> @@ -86,7 +86,7 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 : $fromTo, t('Hosts'), $currentYAxisPage * $yAxisPages->itemCountPerPage + 1, - $nextYAxisPage * $yAxisPages->itemCountPerPage, + $nextYAxisPage === $yAxisPages->last ? $yAxisPages->totalItemCount : $nextYAxisPage * $yAxisPages->itemCountPerPage, $yAxisPages->totalItemCount ); ?>">icon('down_petrol.png'); ?>