mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-29 16:54:04 +02:00
Merge branch 'master' into bugfix/rebuild-form-builder-5525
Conflicts: application/controllers/AuthenticationController.php application/controllers/ConfigController.php application/forms/Authentication/LoginForm.php application/forms/Preference/GeneralForm.php modules/monitoring/application/controllers/ChartController.php
This commit is contained in:
commit
bb7972aa39
3
.gitignore
vendored
3
.gitignore
vendored
@ -36,6 +36,9 @@ config/preferences/*.ini
|
||||
# Application logfiles
|
||||
var/log/*.log
|
||||
|
||||
# Packaging
|
||||
/debian
|
||||
*.tar.gz
|
||||
*.komodoproject
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
object CheckCommand "dummy-host" {
|
||||
import "plugin-check-command"
|
||||
command = [ PluginDir + "/libexec/test_hostcheck.pl" ]
|
||||
command = [ PluginDir + "/test_hostcheck.pl" ]
|
||||
arguments = {
|
||||
"--type" = "$check_type$"
|
||||
"--failchance" = "$check_failchance$"
|
||||
@ -18,7 +18,7 @@ object CheckCommand "dummy-host" {
|
||||
|
||||
object CheckCommand "dummy-service" {
|
||||
import "plugin-check-command"
|
||||
command = [ PluginDir + "/libexec/test_servicecheck.pl" ]
|
||||
command = [ PluginDir + "/test_servicecheck.pl" ]
|
||||
arguments = {
|
||||
"--total-critical-on-host" = "$check_critical_on_host$"
|
||||
"--total-warning-on-host" = "$check_warning_on_host$"
|
||||
|
@ -18,10 +18,15 @@ title = "Configuration"
|
||||
url = "config"
|
||||
priority = 300
|
||||
|
||||
[System.Modules]
|
||||
title = "Modules"
|
||||
url = "config/modules"
|
||||
priority = 400
|
||||
|
||||
[System.ApplicationLog]
|
||||
title = "Application log"
|
||||
url = "list/applicationlog"
|
||||
priority = 400
|
||||
priority = 500
|
||||
|
||||
[Logout]
|
||||
url = "authentication/logout"
|
||||
|
5
.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini
Normal file
5
.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[Documentation]
|
||||
title = "Documentation"
|
||||
icon = "img/icons/comment.png"
|
||||
url = "doc"
|
||||
priority = 80
|
@ -68,7 +68,7 @@ priority = 70
|
||||
|
||||
[Overview.Comments]
|
||||
title = "Comments"
|
||||
url = "monitoring/list/comments"
|
||||
url = "monitoring/list/comments?comment_type=(comment|ack)"
|
||||
priority = 70
|
||||
|
||||
[Overview.Contacts]
|
||||
|
@ -3,10 +3,14 @@ include mysql
|
||||
include pgsql
|
||||
include openldap
|
||||
|
||||
Exec { path => '/bin:/usr/bin:/sbin' }
|
||||
Exec { path => '/bin:/usr/bin:/sbin:/usr/sbin' }
|
||||
|
||||
$icingaVersion = '1.11.2'
|
||||
$icinga2Version = '2.0.0'
|
||||
$icingaVersion = '1.11.5'
|
||||
$icinga2Version = '2.0.1'
|
||||
$pluginVersion = '2.0'
|
||||
$livestatusVersion = '1.2.4p5'
|
||||
$phantomjsVersion = '1.9.1'
|
||||
$casperjsVersion = '1.0.2'
|
||||
|
||||
exec { 'create-mysql-icinga-db':
|
||||
unless => 'mysql -uicinga -picinga icinga',
|
||||
@ -72,10 +76,10 @@ cmmi { 'icinga-mysql':
|
||||
--with-htmurl=/icinga-mysql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-mysql.conf \
|
||||
--with-cgiurl=/icinga-mysql/cgi-bin \
|
||||
--with-http-auth-file=/usr/share/icinga/htpasswd.users \
|
||||
--with-plugin-dir=/usr/lib64/nagios/plugins/libexec',
|
||||
--with-plugin-dir=/usr/lib64/nagios/plugins',
|
||||
creates => '/usr/local/icinga-mysql',
|
||||
make => 'make all && make fullinstall install-config',
|
||||
require => [ User['icinga'], Cmmi['icinga-plugins'], Package['apache'] ],
|
||||
require => [ User['icinga'], Class['monitoring-plugins'], Package['apache'] ],
|
||||
notify => Service['apache']
|
||||
}
|
||||
|
||||
@ -98,10 +102,10 @@ cmmi { 'icinga-pgsql':
|
||||
--with-htmurl=/icinga-pgsql --with-httpd-conf-file=/etc/httpd/conf.d/icinga-pgsql.conf \
|
||||
--with-cgiurl=/icinga-pgsql/cgi-bin \
|
||||
--with-http-auth-file=/usr/share/icinga/htpasswd.users \
|
||||
--with-plugin-dir=/usr/lib64/nagios/plugins/libexec',
|
||||
--with-plugin-dir=/usr/lib64/nagios/plugins',
|
||||
creates => '/usr/local/icinga-pgsql',
|
||||
make => 'make all && make fullinstall install-config',
|
||||
require => [ User['icinga'], Cmmi['icinga-plugins'], Package['apache'] ],
|
||||
require => [ User['icinga'], Class['monitoring-plugins'], Package['apache'] ],
|
||||
notify => Service['apache']
|
||||
}
|
||||
|
||||
@ -206,20 +210,11 @@ exec { 'icinga-htpasswd':
|
||||
require => Class['apache']
|
||||
}
|
||||
|
||||
cmmi { 'icinga-plugins':
|
||||
url => 'https://www.monitoring-plugins.org/download/nagios-plugins-1.5.tar.gz',
|
||||
output => 'nagios-plugins-1.5.tar.gz',
|
||||
flags => '--prefix=/usr/lib64/nagios/plugins \
|
||||
--with-nagios-user=icinga --with-nagios-group=icinga \
|
||||
--with-cgiurl=/icinga-mysql/cgi-bin',
|
||||
creates => '/usr/lib64/nagios/plugins/libexec',
|
||||
make => 'make && make install',
|
||||
require => User['icinga']
|
||||
}
|
||||
include monitoring-plugins
|
||||
|
||||
cmmi { 'mk-livestatus':
|
||||
url => 'http://mathias-kettner.de/download/mk-livestatus-1.2.2p1.tar.gz',
|
||||
output => 'mk-livestatus-1.2.2p1.tar.gz',
|
||||
url => "http://mathias-kettner.de/download/mk-livestatus-${livestatusVersion}.tar.gz",
|
||||
output => "mk-livestatus-${livestatusVersion}.tar.gz",
|
||||
flags => '--prefix=/usr/local/icinga-mysql --exec-prefix=/usr/local/icinga-mysql',
|
||||
creates => '/usr/local/icinga-mysql/lib/mk-livestatus',
|
||||
make => 'make && make install',
|
||||
@ -262,14 +257,14 @@ exec { 'populate-openldap':
|
||||
}
|
||||
|
||||
class { 'phantomjs':
|
||||
url => 'https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2',
|
||||
output => 'phantomjs-1.9.1-linux-x86_64.tar.bz2',
|
||||
url => "https://phantomjs.googlecode.com/files/phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2",
|
||||
output => "phantomjs-${phantomjsVersion}-linux-x86_64.tar.bz2",
|
||||
creates => '/usr/local/phantomjs'
|
||||
}
|
||||
|
||||
class { 'casperjs':
|
||||
url => 'https://github.com/n1k0/casperjs/tarball/1.0.2',
|
||||
output => 'casperjs-1.0.2.tar.gz',
|
||||
url => "https://github.com/n1k0/casperjs/tarball/${casperjsVersion}",
|
||||
output => "casperjs-${casperjsVersion}.tar.gz",
|
||||
creates => '/usr/local/casperjs'
|
||||
}
|
||||
|
||||
@ -421,11 +416,10 @@ package { 'icinga2-ido-mysql':
|
||||
|
||||
exec { 'populate-icinga2-mysql-db':
|
||||
unless => 'mysql -uicinga2 -picinga2 icinga2 -e "SELECT * FROM icinga_dbversion;" &> /dev/null',
|
||||
command => "mysql -uroot icinga2 < /usr/share/doc/icinga2-ido-mysql-$icinga2Version/schema/mysql.sql",
|
||||
command => 'mysql -uroot icinga2 < /usr/share/icinga2-ido-mysql/schema/mysql.sql',
|
||||
require => [ Exec['create-mysql-icinga2-db'], Package['icinga2-ido-mysql'] ]
|
||||
}
|
||||
|
||||
|
||||
file { '/etc/icinga2/features-available/ido-mysql.conf':
|
||||
source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icinga2/features-available/ido-mysql.conf',
|
||||
owner => 'icinga',
|
||||
@ -574,7 +568,7 @@ populate_monitoring_test_config { ['commands', 'contacts', 'dependencies',
|
||||
}
|
||||
|
||||
define populate_monitoring_test_config_plugins {
|
||||
file { "/usr/lib64/nagios/plugins/libexec/${name}":
|
||||
file { "/usr/lib64/nagios/plugins/${name}":
|
||||
owner => 'icinga',
|
||||
group => 'icinga',
|
||||
source => "/usr/local/share/misc/monitoring_test_config/plugins/${name}",
|
||||
@ -792,3 +786,15 @@ file { '/etc/bash_completion.d/icingacli':
|
||||
require => Exec['install bash-completion']
|
||||
}
|
||||
|
||||
file { '/etc/icingaweb/modules/doc/':
|
||||
ensure => 'directory',
|
||||
owner => 'apache',
|
||||
group => 'apache'
|
||||
}
|
||||
|
||||
file { '/etc/icingaweb/modules/doc/menu.ini':
|
||||
source => 'puppet:////vagrant/.vagrant-puppet/files/etc/icingaweb/modules/doc/menu.ini',
|
||||
owner => 'apache',
|
||||
group => 'apache',
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
class monitoring-plugins {
|
||||
include epel
|
||||
|
||||
# nagios plugins from epel
|
||||
package { 'nagios-plugins-all':
|
||||
ensure => installed,
|
||||
require => Class['epel']
|
||||
}
|
||||
}
|
69
README.md
69
README.md
@ -2,13 +2,59 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Vagrant - Virtual development environment](#vagrant)
|
||||
0. [General Information](#general information)
|
||||
1. [Installation](#installation)
|
||||
2. [Support](#support)
|
||||
3. [Vagrant - Virtual development environment](#vagrant)
|
||||
|
||||
## General Information
|
||||
|
||||
`Icinga Web 2` is the next generation monitoring web interface, framework
|
||||
and CLI tool developed by the [Icinga Project](https://www.icinga.org/community/team/).
|
||||
|
||||
Responsive and fast, rewritten from scratch supporting multiple backends and
|
||||
providing a CLI tool. Compatible with Icinga Core 2.x and 1.x.
|
||||
|
||||
Check the Icinga website for some [insights](https://www.icinga.org/icinga/screenshots/icinga-web-2/).
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> `Icinga Web 2` is still in development and not meant for production deployment.
|
||||
> Watch the [development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap)
|
||||
> and [Icinga website](https://www.icinga.org/) for release schedule updates!
|
||||
|
||||
## Installation
|
||||
|
||||
Please navigate to [doc/installation.md](doc/installation.md) for updated details.
|
||||
|
||||
## Support
|
||||
|
||||
Please head over to the [community support channels](https://www.icinga.org/icinga/faq/get-help/)
|
||||
in case of questions, bugs, etc.
|
||||
|
||||
Please make sure to provide the following details:
|
||||
|
||||
* OS, distribution, version
|
||||
* PHP and/or MySQL/PostgreSQL version
|
||||
* Which browser and its version
|
||||
* Screenshot and problem description
|
||||
|
||||
|
||||
## Vagrant
|
||||
|
||||
> **Note** that the deployment of the virtual machine is tested against Vagrant starting with version 1.1.
|
||||
### Requirements
|
||||
|
||||
* Vagrant 1.2+
|
||||
* Virtualbox 4.2.16+
|
||||
* a fairly powerful hardware (quad core, 4gb ram, fast hdd)
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The deployment of the virtual machine is tested against Vagrant starting with version 1.2.
|
||||
> Unfortunately older versions will not work.
|
||||
|
||||
### General
|
||||
|
||||
The Icinga Web 2 project ships with a Vagrant virtual machine that integrates
|
||||
the source code with various services and example data in a controlled
|
||||
environment. This enables developers and users to test Livestatus, status.dat,
|
||||
@ -17,7 +63,9 @@ have to do is install Vagrant and run:
|
||||
|
||||
vagrant up
|
||||
|
||||
> **Note** that the first boot of the vm takes a fairly long time because
|
||||
> **Note**
|
||||
>
|
||||
> The first boot of the vm takes a fairly long time because
|
||||
> you'll download a plain CentOS base box and Vagrant will automatically
|
||||
> provision the environment on the first go.
|
||||
|
||||
@ -68,7 +116,7 @@ After you should be able to browse [localhost:8080/icingaweb](http://localhost:8
|
||||
**Installed files**:
|
||||
|
||||
* `/usr/share/icinga/htpasswd.users` account information for logging into the Icinga classic web interface for both icinga instances
|
||||
* `/usr/lib64/nagios/plugins` Nagios Plugins for both icinga instances
|
||||
* `/usr/lib64/nagios/plugins` Monitoring Plugins for all Icinga instances
|
||||
|
||||
#### Icinga with IDOUtils using a MySQL database
|
||||
|
||||
@ -196,12 +244,13 @@ code style issues.
|
||||
|
||||
#### Icinga 2
|
||||
|
||||
**Installation path**: `/usr/local/icinga2`
|
||||
Installed from the Icinga [snapshot package repository](http://packages.icinga.org/epel/).
|
||||
The configuration is located in `/etc/icinga2`.
|
||||
|
||||
**Example usage**:
|
||||
|
||||
cd /usr/local/icinga2
|
||||
./sbin/icinga2 -c etc/icinga2/icinga2.conf.dist
|
||||
/etc/init.d/icinga2 (start|stop|restart|reload)
|
||||
|
||||
|
||||
## Log into Icinga Web 2
|
||||
|
||||
@ -211,3 +260,9 @@ If you've configure LDAP as authentication backend (which is the default) use th
|
||||
> **Password**: password
|
||||
|
||||
Have a look at [LDAP example data](#ldap example data) for more accounts.
|
||||
|
||||
Using MySQL as backend:
|
||||
|
||||
> **Username**: icingaadmin
|
||||
> **Password**: icinga
|
||||
|
||||
|
@ -34,11 +34,16 @@ class AuthenticationController extends ActionController
|
||||
public function loginAction()
|
||||
{
|
||||
$auth = $this->Auth();
|
||||
$this->view->form = new LoginForm();
|
||||
$this->view->form = $form = new LoginForm();
|
||||
$this->view->title = $this->translate('Icingaweb Login');
|
||||
|
||||
try {
|
||||
$redirectUrl = Url::fromPath($this->params->get('redirect', 'dashboard'));
|
||||
$redirectUrl = $this->view->form->getValue('redirect');
|
||||
if ($redirectUrl) {
|
||||
$redirectUrl = Url::fromPath($redirectUrl);
|
||||
} else {
|
||||
$redirectUrl = Url::fromPath('dashboard');
|
||||
}
|
||||
|
||||
if ($auth->isAuthenticated()) {
|
||||
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||
@ -47,14 +52,10 @@ class AuthenticationController extends ActionController
|
||||
try {
|
||||
$config = Config::app('authentication');
|
||||
} catch (NotReadableError $e) {
|
||||
Logger::error(
|
||||
new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e)
|
||||
);
|
||||
throw new ConfigurationError(
|
||||
t(
|
||||
'No authentication methods available. Authentication configuration could not be loaded.'
|
||||
. ' Please check the system log or Icinga Web 2 log for more information'
|
||||
)
|
||||
$this->translate('Could not read your authentiction.ini, no authentication methods are available.'),
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
@ -66,6 +67,14 @@ class AuthenticationController extends ActionController
|
||||
$backendsTried = 0;
|
||||
$backendsWithError = 0;
|
||||
|
||||
$redirectUrl = $form->getValue('redirect');
|
||||
|
||||
if ($redirectUrl) {
|
||||
$redirectUrl = Url::fromPath($redirectUrl);
|
||||
} else {
|
||||
$redirectUrl = Url::fromPath('dashboard');
|
||||
}
|
||||
|
||||
foreach ($chain as $backend) {
|
||||
if ($backend instanceof AutoLoginBackend) {
|
||||
continue;
|
||||
@ -85,25 +94,25 @@ class AuthenticationController extends ActionController
|
||||
}
|
||||
if ($backendsTried === 0) {
|
||||
throw new ConfigurationError(
|
||||
t(
|
||||
'No authentication methods available. It seems that no authentication method has been set'
|
||||
. ' up. Please check the system log or Icinga Web 2 log for more information'
|
||||
)
|
||||
$this->translate(
|
||||
'No authentication methods available. Did you create'
|
||||
. ' authentication.ini when installing Icinga Web 2?'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($backendsTried === $backendsWithError) {
|
||||
throw new ConfigurationError(
|
||||
$this->translate(
|
||||
'No authentication methods available. It seems that all set up authentication methods have'
|
||||
. ' errors. Please check the system log or Icinga Web 2 log for more information'
|
||||
'All configured authentication methods failed.'
|
||||
. ' Please check the system log or Icinga Web 2 log for more information.'
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($backendsWithError) {
|
||||
$this->view->form->getElement('username')->addError(
|
||||
$this->translate(
|
||||
'Note that not all authentication backends are available for authentication because they'
|
||||
. ' have errors. Please check the system log or Icinga Web 2 log for more information'
|
||||
'Please note that not all authentication methods where available.'
|
||||
. ' Check the system log or Icinga Web 2 log for more information.'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -131,9 +140,10 @@ class AuthenticationController extends ActionController
|
||||
public function logoutAction()
|
||||
{
|
||||
$auth = $this->Auth();
|
||||
$isRemoteUser = $auth->getUser()->isRemoteUser();
|
||||
$auth->removeAuthorization();
|
||||
|
||||
if ($auth->isAuthenticatedFromRemoteUser()) {
|
||||
if ($isRemoteUser === true) {
|
||||
$this->_helper->layout->setLayout('login');
|
||||
$this->_response->setHttpResponseCode(401);
|
||||
} else {
|
||||
|
@ -6,7 +6,6 @@ use Icinga\Web\Controller\BaseConfigController;
|
||||
use Icinga\Web\Widget\AlertMessageBox;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Application\Icinga;
|
||||
@ -41,9 +40,6 @@ class ConfigController extends BaseConfigController
|
||||
))->add('logging', array(
|
||||
'title' => 'Logging',
|
||||
'url' => 'config/logging'
|
||||
))->add('modules', array(
|
||||
'title' => 'Modules',
|
||||
'url' => 'config/modules'
|
||||
));
|
||||
}
|
||||
|
||||
@ -116,6 +112,11 @@ class ConfigController extends BaseConfigController
|
||||
*/
|
||||
public function modulesAction()
|
||||
{
|
||||
$this->view->tabs = Widget::create('tabs')->add('modules', array(
|
||||
'title' => 'Modules',
|
||||
'url' => 'config/modules'
|
||||
));
|
||||
|
||||
$this->view->tabs->activate('modules');
|
||||
$this->view->modules = Icinga::app()->getModuleManager()->select()
|
||||
->from('modules')
|
||||
|
@ -18,7 +18,9 @@ class LayoutController extends ActionController
|
||||
*/
|
||||
public function menuAction()
|
||||
{
|
||||
$this->view->menuRenderer = new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->getRelativeUrl());
|
||||
$this->view->menuRenderer = new MenuRenderer(
|
||||
Menu::fromConfig()->order(), Url::fromRequest()->without('renderLayout')->getRelativeUrl()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@
|
||||
namespace Icinga\Form\Authentication;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* Class LoginForm
|
||||
@ -44,6 +45,13 @@ class LoginForm extends Form
|
||||
'placeholder' => t('...and your password'),
|
||||
'class' => isset($formData['username']) ? 'autofocus' : ''
|
||||
)
|
||||
),
|
||||
$this->createElement(
|
||||
'hidden',
|
||||
'redirect',
|
||||
array(
|
||||
'value' => Url::fromRequest()->getParam('redirect')
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -110,7 +110,6 @@ class GeneralForm extends Form
|
||||
foreach (Translator::getAvailableLocaleCodes() as $language) {
|
||||
$languages[$language] = $language;
|
||||
}
|
||||
$languages[Translator::DEFAULT_LOCALE] = Translator::DEFAULT_LOCALE;
|
||||
|
||||
return $this->createElement(
|
||||
'select',
|
||||
|
@ -36,7 +36,6 @@ class GeneralForm extends Form
|
||||
foreach (Translator::getAvailableLocaleCodes() as $language) {
|
||||
$languages[$language] = $language;
|
||||
}
|
||||
$languages[Translator::DEFAULT_LOCALE] = Translator::DEFAULT_LOCALE;
|
||||
|
||||
$useBrowserLanguage = isset($formData['browser_language']) ? $formData['browser_language'] == 1 : true;
|
||||
$selectOptions = array(
|
||||
|
@ -38,7 +38,7 @@ if ($notifications->hasMessages()) {
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div id="main" role="main">
|
||||
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest() ?>"<?= $refresh ?> style="display: block">
|
||||
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest()->without('renderLayout') ?>"<?= $refresh ?> style="display: block">
|
||||
<?= $this->render('inline.phtml') ?>
|
||||
</div>
|
||||
<div id="col2" class="container">
|
||||
|
@ -44,6 +44,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
|
||||
<!--[if lt IE 9]>
|
||||
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
||||
<![endif]-->
|
||||
<link type="image/png" rel="shortcut icon" href="<?= $this->baseUrl('img/favicon.png') ?>" />
|
||||
|
||||
</head>
|
||||
<body id="body">
|
||||
|
@ -14,5 +14,5 @@ if (! $this->auth()->isAuthenticated()) {
|
||||
<form action="<?= $this->href('search') ?>" method="get" role="search">
|
||||
<input type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
||||
</form>
|
||||
<?= new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->getRelativeUrl()); ?>
|
||||
<?= new MenuRenderer(Menu::fromConfig()->order(), Url::fromRequest()->without('renderLayout')->getRelativeUrl()); ?>
|
||||
</div>
|
||||
|
@ -7,10 +7,7 @@
|
||||
in every further request until the browser was closed. To allow logout and to allow the user to change the
|
||||
logged-in user this JavaScript provides a workaround to force a new authentication prompt in most browsers.
|
||||
-->
|
||||
|
||||
<div class="row">
|
||||
<br/>
|
||||
<div class="md-offset-3 col-md-6 col-sm-6 col-sm-offset-3">
|
||||
<div class="content">
|
||||
<div class="alert alert-warning" id="logout-status">
|
||||
<b> <?= t('Logging out...'); ?> </b> <br />
|
||||
<?= t(
|
||||
@ -19,37 +16,19 @@
|
||||
'browser session.'
|
||||
); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3">
|
||||
<div class="container" >
|
||||
<a class="button btn btn-cta form-control input-sm" href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a>
|
||||
<a href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
/**
|
||||
* When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it
|
||||
* before it is able to finish. This will cause the browser to show a new authentication prompt in the next
|
||||
* request.
|
||||
*/
|
||||
window.onload = function () {
|
||||
function getXMLHttpRequest() {
|
||||
var xmlhttp = null;
|
||||
try {
|
||||
if (window.XMLHttpRequest) {
|
||||
xmlhttp = new XMLHttpRequest();
|
||||
} else if (window.ActiveXObject) {
|
||||
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
|
||||
}
|
||||
} catch (e) {}
|
||||
return xmlhttp;
|
||||
}
|
||||
var msg = document.getElementById('logout-status');
|
||||
$(document).ready(function() {
|
||||
msg = $('#logout-status');
|
||||
try {
|
||||
if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
|
||||
document.execCommand('ClearAuthenticationCache');
|
||||
@ -60,13 +39,9 @@
|
||||
xhttp.abort();
|
||||
}
|
||||
} catch (e) {
|
||||
msg.innerHTML = '<?= t(
|
||||
'Logout not possible, it may be necessary to quit the session manually ' .
|
||||
'by clearing the cache, or closing the current browser session. Error: '
|
||||
);?>' + ': ' + e.getMessage() ;
|
||||
msg.setAttribute('class', 'alert alert-danger');
|
||||
}
|
||||
msg.innerHTML = '<?= t('Logout successful!'); ?>';
|
||||
msg.setAttribute('class', 'alert alert-success');
|
||||
};
|
||||
msg.html('<?= t('Logout successful!'); ?>');
|
||||
msg.removeClass();
|
||||
msg.addClass('alert alert-success');
|
||||
});
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
; authentication.ini
|
||||
;
|
||||
; Each section listed in this configuration represents a backend used to authenticate users. The backend configurations
|
||||
: must define a resource referring to a configured database or LDAP connection in the INI file resources.ini.
|
||||
; must define a resource referring to a configured database or LDAP connection in the INI file resources.ini.
|
||||
;
|
||||
; The backends will be processed from top to bottom using the first backend for authentication which reports that
|
||||
; the user trying to log in is available.
|
||||
|
@ -23,13 +23,17 @@ sort = host_severity
|
||||
title = "Landing page"
|
||||
|
||||
[Landing.Hostgroups]
|
||||
url = "monitoring/chart/hostgroup?height=400&width=500"
|
||||
url = "monitoring/chart/hostgroup"
|
||||
|
||||
[Landing.Servicegroups]
|
||||
url = "monitoring/chart/servicegroup?height=360&width=450"
|
||||
url = "monitoring/chart/servicegroup"
|
||||
|
||||
[Landing.Unhandled Problem Services]
|
||||
url = "monitoring/list/services?service_handled=0&service_problem=1"
|
||||
url = "monitoring/list/services"
|
||||
service_handled = 0
|
||||
service_problem = 1
|
||||
|
||||
[Landing.Unhandled Problem Hosts]
|
||||
url = "monitoring/list/hosts?host_handled=0&host_problem=1"
|
||||
url = "monitoring/list/hosts"
|
||||
host_handled = 0
|
||||
host_problem = 1
|
||||
|
@ -18,10 +18,15 @@ title = "Configuration"
|
||||
url = "config"
|
||||
priority = 300
|
||||
|
||||
[System.Modules]
|
||||
title = "Modules"
|
||||
url = "config/modules"
|
||||
priority = 400
|
||||
|
||||
[System.ApplicationLog]
|
||||
title = "Application log"
|
||||
url = "list/applicationlog"
|
||||
priority = 400
|
||||
priority = 500
|
||||
|
||||
[Logout]
|
||||
url = "authentication/logout"
|
||||
|
5
config/modules/doc/menu.ini
Normal file
5
config/modules/doc/menu.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[Documentation]
|
||||
title = "Documentation"
|
||||
icon = "img/icons/comment.png"
|
||||
url = "doc"
|
||||
priority = 80
|
@ -68,7 +68,7 @@ priority = 70
|
||||
|
||||
[Overview.Comments]
|
||||
title = "Comments"
|
||||
url = "monitoring/list/comments"
|
||||
url = "monitoring/list/comments?comment_type=(comment|ack)"
|
||||
priority = 70
|
||||
|
||||
[Overview.Contacts]
|
||||
|
@ -3,8 +3,19 @@ Alias @web_path@ "@prefix@/public"
|
||||
<Directory "@prefix@/public">
|
||||
Options SymLinksIfOwnerMatch
|
||||
AllowOverride None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
|
||||
<IfModule mod_authz_core.c>
|
||||
# Apache 2.4
|
||||
<RequireAll>
|
||||
Require all granted
|
||||
</RequireAll>
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_authz_core.c>
|
||||
# Apache 2.2
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</IfModule>
|
||||
|
||||
SetEnv ICINGAWEB_CONFIGDIR @icingaweb_config_path@
|
||||
|
||||
|
109
icingaweb2.spec
109
icingaweb2.spec
@ -1,16 +1,37 @@
|
||||
# $Id$
|
||||
# Authority: The icinga devel team <icinga-devel at lists.icinga.org>
|
||||
# Upstream: The icinga devel team <icinga-devel at lists.icinga.org>
|
||||
# ExcludeDist: el4 el3
|
||||
#/**
|
||||
# * This file is part of Icinga Web 2.
|
||||
# *
|
||||
# * Icinga Web 2 - Head for multiple monitoring backends.
|
||||
# * Copyright (C) 2014 Icinga Development Team
|
||||
# *
|
||||
# * This program is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of the GNU General Public License
|
||||
# * as published by the Free Software Foundation; either version 2
|
||||
# * of the License, or (at your option) any later version.
|
||||
# *
|
||||
# * This program is distributed in the hope that it will be useful,
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# * GNU General Public License for more details.
|
||||
# *
|
||||
# * You should have received a copy of the GNU General Public License
|
||||
# * along with this program; if not, write to the Free Software
|
||||
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# *
|
||||
# * @copyright 2014 Icinga Development Team <info@icinga.org>
|
||||
# * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
||||
# * @author Icinga Development Team <info@icinga.org>
|
||||
# *
|
||||
# */
|
||||
|
||||
%define revision 0
|
||||
%define revision 1
|
||||
|
||||
%define configdir %{_sysconfdir}/icingaweb
|
||||
%define sharedir %{_datadir}/icingaweb
|
||||
%define prefixdir %{_datadir}/icingaweb
|
||||
%define logdir %{sharedir}/log
|
||||
%define usermodparam -a -G
|
||||
#%define logdir %{_localstatedir}/log/icingaweb
|
||||
%define logdir %{_localstatedir}/log/icingaweb
|
||||
|
||||
%if "%{_vendor}" == "suse"
|
||||
%define phpname php5
|
||||
@ -19,11 +40,12 @@
|
||||
%endif
|
||||
# SLE 11 = 1110
|
||||
%if 0%{?suse_version} == 1110
|
||||
%define phpname php53
|
||||
%define apache2modphpname apache2-mod_php53
|
||||
%define usermodparam -A
|
||||
%endif
|
||||
|
||||
%if "%{_vendor}" == "redhat" || 0%{?suse_version} == 1110
|
||||
%if "%{_vendor}" == "redhat"
|
||||
%define phpname php
|
||||
%define phpzendname php-ZendFramework
|
||||
%endif
|
||||
@ -37,15 +59,15 @@
|
||||
%define apacheconfdir %{_sysconfdir}/apache2/conf.d
|
||||
%define apacheuser wwwrun
|
||||
%define apachegroup www
|
||||
%define extcmdfile1x %{_localstatedir}/icinga/rw/icinga.cmd
|
||||
%define livestatussocket1x %{_localstatedir}/icinga/rw/live
|
||||
%define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd
|
||||
%define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus
|
||||
%endif
|
||||
%if "%{_vendor}" == "redhat"
|
||||
%define apacheconfdir %{_sysconfdir}/httpd/conf.d
|
||||
%define apacheuser apache
|
||||
%define apachegroup apache
|
||||
%define extcmdfile-1x %{_localstatedir}/spool/icinga/cmd/icinga.cmd
|
||||
%define livestatussocket1x %{_localstatedir}/spool/icinga/cmd/live
|
||||
%define extcmdfile %{_localstatedir}/run/icinga2/cmd/icinga.cmd
|
||||
%define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus
|
||||
%endif
|
||||
|
||||
Summary: Open Source host, service and network monitoring Web UI
|
||||
@ -109,8 +131,8 @@ Requires: php-Icinga
|
||||
|
||||
|
||||
%description
|
||||
IcingaWeb for Icinga 2 or Icinga 1.x using status data,
|
||||
IDOUtils or Livestatus as backend provider.
|
||||
Icinga Web 2 for Icinga 2 or Icinga 1.x using multiple backends
|
||||
for example DB IDO.
|
||||
|
||||
%package -n icingacli
|
||||
Summary: Icinga CLI
|
||||
@ -130,58 +152,54 @@ Requires: %{phpzendname}
|
||||
|
||||
|
||||
%description -n php-Icinga
|
||||
Icinga Web 2 PHP Libraries shared with icingacli.
|
||||
|
||||
|
||||
Icinga Web 2 PHP Libraries required by the web frontend and cli tool.
|
||||
|
||||
|
||||
%prep
|
||||
#%setup -q -n %{name}-%{version}
|
||||
%setup -q -n %{name}
|
||||
#VERSION=0.0.1; git archive --format=tar --prefix=icingaweb2-$VERSION/ HEAD | gzip >icingaweb2-$VERSION.tar.gz
|
||||
%setup -q -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
|
||||
cat > README.RHEL.SUSE <<"EOF"
|
||||
IcingaWeb for RHEL and SUSE
|
||||
===========================
|
||||
|
||||
Please check ./doc/installation.md
|
||||
for requirements and database setup.
|
||||
EOF
|
||||
|
||||
%install
|
||||
[ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot}
|
||||
|
||||
# prepare configuration for sub packages
|
||||
|
||||
# install rhel apache config
|
||||
install -D -m0644 packages/rhel/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf
|
||||
install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf
|
||||
|
||||
# install public, library, modules
|
||||
%{__mkdir} -p %{buildroot}/%{sharedir}
|
||||
%{__mkdir} -p %{buildroot}/%{logdir}
|
||||
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb
|
||||
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard
|
||||
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules
|
||||
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring
|
||||
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules
|
||||
|
||||
%{__cp} -r application library modules public %{buildroot}/%{sharedir}/
|
||||
|
||||
# install index.php
|
||||
install -m0644 packages/rhel/usr/share/icingaweb/public/index.php %{buildroot}/%{sharedir}/public/index.php
|
||||
|
||||
# use the vagrant config for configuration for now - TODO
|
||||
%{__cp} -r .vagrant-puppet/files/etc/icingaweb %{buildroot}/%{_sysconfdir}/
|
||||
|
||||
# we use the default 'icinga' database
|
||||
sed -i 's/icinga2/icinga/g' %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini
|
||||
## config
|
||||
# use the default menu.ini for application and monitoring mobule
|
||||
install -D -m0644 config/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/menu.ini
|
||||
install -D -m0644 config/modules/monitoring/menu.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/menu.ini
|
||||
# authentication is db only
|
||||
install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini
|
||||
# custom resource paths
|
||||
install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini
|
||||
# dashboard
|
||||
install -D -m0644 config/dashboard/dashboard.ini %{buildroot}/%{_sysconfdir}/icingaweb/dashboard/dashboard.ini
|
||||
# monitoring module (icinga2)
|
||||
install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/backends.ini
|
||||
install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/instances.ini
|
||||
|
||||
# enable the monitoring module by default
|
||||
ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring
|
||||
## config
|
||||
|
||||
# install icingacli
|
||||
install -D -m0755 bin/icingacli %{buildroot}/usr/bin/icingacli
|
||||
|
||||
# install sql schema files as example
|
||||
|
||||
# delete all *.in files
|
||||
install -D -m0755 packages/rpm/usr/bin/icingacli %{buildroot}/usr/bin/icingacli
|
||||
|
||||
%pre
|
||||
# Add apacheuser in the icingacmd group
|
||||
@ -196,9 +214,6 @@ if [ $? -eq 0 ]; then
|
||||
%{_sbindir}/usermod %{usermodparam} icingacmd %{apacheuser}
|
||||
fi
|
||||
|
||||
# uncomment if building from git
|
||||
# %{__rm} -rf %{buildroot}%{_datadir}/icinga2-web/.git
|
||||
|
||||
%preun
|
||||
|
||||
%post
|
||||
@ -209,14 +224,13 @@ fi
|
||||
%files
|
||||
# main dirs
|
||||
%defattr(-,root,root)
|
||||
%doc etc/schema doc packages/rhel/README
|
||||
%doc etc/schema doc packages/rpm/README.md
|
||||
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/public
|
||||
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/modules
|
||||
# configs
|
||||
%defattr(-,root,root)
|
||||
%config(noreplace) %attr(-,root,root) %{apacheconfdir}/icingaweb.conf
|
||||
%dir %{configdir}
|
||||
%config(noreplace) %attr(775,%{apacheuser},%{apachegroup}) %{configdir}
|
||||
%config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir}
|
||||
# logs
|
||||
%attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir}
|
||||
|
||||
@ -228,6 +242,3 @@ fi
|
||||
%attr(0755,root,root) /usr/bin/icingacli
|
||||
|
||||
%changelog
|
||||
* Tue May 11 2014 Michael Friedrich <michael.friedrich@netways.de> - 0.0.1-1
|
||||
- initial creation
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
use Exception;
|
||||
use Zend_Controller_Router_Route_Abstract;
|
||||
use Zend_Controller_Router_Route as Route;
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Application\Config;
|
||||
@ -135,6 +136,16 @@ class Module
|
||||
*/
|
||||
private $app;
|
||||
|
||||
|
||||
/**
|
||||
* Routes to add to the route chain
|
||||
*
|
||||
* @var array Array of name-route pairs
|
||||
*
|
||||
* @see addRoute()
|
||||
*/
|
||||
protected $routes = array();
|
||||
|
||||
/**
|
||||
* Create a new module object
|
||||
*
|
||||
@ -166,8 +177,7 @@ class Module
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->registerAutoloader()
|
||||
->registerWebIntegration();
|
||||
$this->registerAutoloader();
|
||||
try {
|
||||
$this->launchRunScript();
|
||||
} catch (Exception $e) {
|
||||
@ -179,6 +189,7 @@ class Module
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$this->registerWebIntegration();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -658,24 +669,29 @@ class Module
|
||||
}
|
||||
|
||||
/**
|
||||
* Register routes for web access
|
||||
* Add routes for static content and any route added via addRoute() to the route chain
|
||||
*
|
||||
* @return self
|
||||
* @return self
|
||||
* @see addRoute()
|
||||
*/
|
||||
protected function registerRoutes()
|
||||
{
|
||||
$this->app->getFrontController()->getRouter()->addRoute(
|
||||
$router = $this->app->getFrontController()->getRouter();
|
||||
foreach ($this->routes as $name => $route) {
|
||||
$router->addRoute($name, $route);
|
||||
}
|
||||
$router->addRoute(
|
||||
$this->name . '_jsprovider',
|
||||
new Route(
|
||||
'js/' . $this->name . '/:file',
|
||||
array(
|
||||
'controller' => 'static',
|
||||
'action' =>'javascript',
|
||||
'module_name' => $this->name
|
||||
'module_name' => $this->name
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->app->getFrontController()->getRouter()->addRoute(
|
||||
$router->addRoute(
|
||||
$this->name . '_img',
|
||||
new Route(
|
||||
'img/' . $this->name . '/:file',
|
||||
@ -750,4 +766,19 @@ class Module
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a route which will be added to the route chain
|
||||
*
|
||||
* @param string $name Name of the route
|
||||
* @param Zend_Controller_Router_Route_Abstract $route Instance of the route
|
||||
*
|
||||
* @return self
|
||||
* @see registerRoutes()
|
||||
*/
|
||||
protected function addRoute($name, Zend_Controller_Router_Route_Abstract $route)
|
||||
{
|
||||
$this->routes[$name] = $route;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ class AutoLoginBackend extends UserBackend
|
||||
{
|
||||
if (isset($_SERVER['REMOTE_USER'])) {
|
||||
$username = $_SERVER['REMOTE_USER'];
|
||||
$user->setRemoteUserInformation($username, 'REMOTE_USER');
|
||||
if ($this->stripUsernameRegexp !== null) {
|
||||
$stripped = preg_replace($this->stripUsernameRegexp, '', $username);
|
||||
if ($stripped !== false) {
|
||||
|
@ -30,12 +30,6 @@ class Manager
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* If the user was authenticated from the REMOTE_USER server variable
|
||||
*
|
||||
* @var Boolean
|
||||
*/
|
||||
private $fromRemoteUser = false;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
@ -117,6 +111,13 @@ class Manager
|
||||
public function authenticateFromSession()
|
||||
{
|
||||
$this->user = Session::getSession()->get('user');
|
||||
|
||||
if ($this->user !== null && $this->user->isRemoteUser() === true) {
|
||||
list($originUsername, $field) = $this->user->getRemoteUserInformation();
|
||||
if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) {
|
||||
$this->removeAuthorization();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,35 +205,4 @@ class Manager
|
||||
{
|
||||
return $this->user->getGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to authenticate the user from the session, and then from the REMOTE_USER superglobal, that can be set by
|
||||
* an external authentication provider.
|
||||
*/
|
||||
public function authenticateFromRemoteUser()
|
||||
{
|
||||
if (array_key_exists('REMOTE_USER', $_SERVER)) {
|
||||
$this->fromRemoteUser = true;
|
||||
}
|
||||
$this->authenticateFromSession();
|
||||
if ($this->user !== null) {
|
||||
if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) {
|
||||
// Remote user has changed, clear all sessions
|
||||
$this->removeAuthorization();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (array_key_exists('REMOTE_USER', $_SERVER) && $_SERVER["REMOTE_USER"]) {
|
||||
$this->user = new User($_SERVER["REMOTE_USER"]);
|
||||
$this->persistCurrentUser();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the session was established from the REMOTE_USER server variable.
|
||||
*/
|
||||
public function isAuthenticatedFromRemoteUser()
|
||||
{
|
||||
return $this->fromRemoteUser;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use Icinga\Chart\Primitive\Drawable;
|
||||
use Icinga\Chart\Primitive\Line;
|
||||
use Icinga\Chart\Primitive\Text;
|
||||
use Icinga\Chart\Render\RenderContext;
|
||||
use Icinga\Chart\Render\Rotator;
|
||||
use Icinga\Chart\Unit\AxisUnit;
|
||||
use Icinga\Chart\Unit\CalendarUnit;
|
||||
use Icinga\Chart\Unit\LinearUnit;
|
||||
@ -188,11 +189,11 @@ class Axis implements Drawable
|
||||
$labelField->setFontSize('2.5em');
|
||||
}
|
||||
|
||||
if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) {
|
||||
$labelField = new Rotator($labelField, 45);
|
||||
}
|
||||
$labelField = $labelField->toSvg($ctx);
|
||||
|
||||
if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) {
|
||||
$labelField = $this->rotate($ctx, $labelField, 45);
|
||||
}
|
||||
$group->appendChild($labelField);
|
||||
|
||||
if ($this->drawYGrid) {
|
||||
@ -214,34 +215,6 @@ class Axis implements Drawable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the given element.
|
||||
*
|
||||
* @param RenderContext $ctx The rendering context
|
||||
* @param DOMElement $el The element to rotate
|
||||
* @param $degrees The rotation degrees
|
||||
*
|
||||
* @return DOMElement
|
||||
*/
|
||||
private function rotate(RenderContext $ctx, DOMElement $el, $degrees)
|
||||
{
|
||||
// Create a box containing the rotated element relative to the original text position
|
||||
$container = $ctx->getDocument()->createElement('g');
|
||||
$x = $el->getAttribute('x');
|
||||
$y = $el->getAttribute('y');
|
||||
$container->setAttribute('transform', 'translate(' . $x . ',' . $y . ')');
|
||||
$el->removeAttribute('x');
|
||||
$el->removeAttribute('y');
|
||||
|
||||
// Create a rotated box containing the text
|
||||
$rotate = $ctx->getDocument()->createElement('g');
|
||||
$rotate->setAttribute('transform', 'rotate(' . $degrees . ')');
|
||||
$rotate->appendChild($el);
|
||||
|
||||
$container->appendChild($rotate);
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the vertical axis
|
||||
*
|
||||
@ -275,9 +248,9 @@ class Axis implements Drawable
|
||||
if ($this->yLabel) {
|
||||
$axisLabel = new Text(-8, 50, $this->yLabel);
|
||||
$axisLabel->setFontSize('2em')
|
||||
->setAdditionalStyle(Text::ORIENTATION_VERTICAL)
|
||||
->setFontWeight('bold')
|
||||
->setAlignment(Text::ALIGN_MIDDLE);
|
||||
$axisLabel = new Rotator($axisLabel, 90);
|
||||
|
||||
$group->appendChild($axisLabel->toSvg($ctx));
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class BarGraph extends Styleable implements Drawable
|
||||
$group = $doc->createElement('g');
|
||||
$idx = 0;
|
||||
foreach ($this->dataSet as $point) {
|
||||
$rect = new Rect($point[0] - 1, $point[1], 2, 100 - $point[1]);
|
||||
$rect = new Rect($point[0] - 2, $point[1], 4, 100 - $point[1]);
|
||||
$rect->setFill($this->fill);
|
||||
$rect->setStrokeWidth($this->strokeWidth);
|
||||
$rect->setStrokeColor('black');
|
||||
|
@ -30,16 +30,6 @@ class Text extends Styleable implements Drawable
|
||||
*/
|
||||
const ALIGN_MIDDLE = 'middle';
|
||||
|
||||
/**
|
||||
* Normal left to right orientation
|
||||
*/
|
||||
const ORIENTATION_HORIZONTAL = "";
|
||||
|
||||
/**
|
||||
* Top down orientation (rotated by 90°)
|
||||
*/
|
||||
const ORIENTATION_VERTICAL = "writing-mode: tb;";
|
||||
|
||||
/**
|
||||
* The x position of the Text
|
||||
*
|
||||
|
86
library/Icinga/Chart/Render/Rotator.php
Normal file
86
library/Icinga/Chart/Render/Rotator.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: mjentsch
|
||||
* Date: 22.07.14
|
||||
* Time: 10:17
|
||||
*/
|
||||
|
||||
namespace Icinga\Chart\Render;
|
||||
|
||||
|
||||
use Icinga\Chart\Render\RenderContext;
|
||||
use Icinga\Chart\Primitive\Drawable;
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* Class Rotator
|
||||
* @package Icinga\Chart\Render
|
||||
*/
|
||||
class Rotator implements Drawable
|
||||
{
|
||||
/**
|
||||
* The drawable element to rotate
|
||||
*
|
||||
* @var Drawable
|
||||
*/
|
||||
private $element;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $degrees;
|
||||
|
||||
/**
|
||||
* Wrap an element into a new instance of Rotator
|
||||
*
|
||||
* @param Drawable $element The element to rotate
|
||||
* @param int $degrees The amount of degrees
|
||||
*/
|
||||
public function __construct(Drawable $element, $degrees)
|
||||
{
|
||||
$this->element = $element;
|
||||
$this->degrees = $degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the given element.
|
||||
*
|
||||
* @param RenderContext $ctx The rendering context
|
||||
* @param DOMElement $el The element to rotate
|
||||
* @param $degrees The amount of degrees
|
||||
*
|
||||
* @return DOMElement The rotated DOMElement
|
||||
*/
|
||||
private function rotate(RenderContext $ctx, DOMElement $el, $degrees)
|
||||
{
|
||||
// Create a box containing the rotated element relative to the original element position
|
||||
$container = $ctx->getDocument()->createElement('g');
|
||||
$x = $el->getAttribute('x');
|
||||
$y = $el->getAttribute('y');
|
||||
$container->setAttribute('transform', 'translate(' . $x . ',' . $y . ')');
|
||||
$el->removeAttribute('x');
|
||||
$el->removeAttribute('y');
|
||||
|
||||
// Put the element into a rotated group
|
||||
//$rotate = $ctx->getDocument()->createElement('g');
|
||||
$el->setAttribute('transform', 'rotate(' . $degrees . ')');
|
||||
//$rotate->appendChild($el);
|
||||
|
||||
$container->appendChild($el);
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the SVG representation from this Drawable
|
||||
*
|
||||
* @param RenderContext $ctx The context to use for rendering
|
||||
*
|
||||
* @return DOMElement The SVG Element
|
||||
*/
|
||||
public function toSvg(RenderContext $ctx)
|
||||
{
|
||||
$el = $this->element->toSvg($ctx);
|
||||
return $this->rotate($ctx, $el, $this->degrees);
|
||||
}
|
||||
}
|
@ -90,17 +90,52 @@ class LinearUnit implements AxisUnit
|
||||
}
|
||||
sort($datapoints);
|
||||
if (!$this->staticMax) {
|
||||
$this->max = max($this->max, $datapoints[count($datapoints)-1]);
|
||||
$this->max = max($this->max, $datapoints[count($datapoints) - 1]);
|
||||
}
|
||||
if (!$this->staticMin) {
|
||||
$this->min = min($this->min, $datapoints[0]);
|
||||
}
|
||||
|
||||
if (!$this->staticMin || !$this->staticMax) {
|
||||
$this->updateMaxValue();
|
||||
}
|
||||
$this->currentTick = 0;
|
||||
$this->currentValue = $this->min;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the range depending on the current values of min, max and nrOfTicks
|
||||
*/
|
||||
private function updateMaxValue()
|
||||
{
|
||||
$this->max = $this->calculateTickRange($this->max - $this->min, $this->nrOfTicks) *
|
||||
$this->nrOfTicks + $this->min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the minimum tick range that is necessary to display the given value range
|
||||
* correctly
|
||||
*
|
||||
* @param int range The range to display
|
||||
* @param int ticks The amount of ticks to use
|
||||
*
|
||||
* @return int The value for each tick
|
||||
*/
|
||||
private function calculateTickRange($range, $ticks)
|
||||
{
|
||||
$factor = 1;
|
||||
$steps = array(1, 2, 5);
|
||||
$step = 0;
|
||||
while ($range / ($factor * $steps[$step]) > $ticks) {
|
||||
$step++;
|
||||
if ($step === count($steps)) {
|
||||
$step = 0;
|
||||
$factor *= 10;
|
||||
}
|
||||
}
|
||||
return $steps[$step] * $factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the absolute value to an axis relative value
|
||||
*
|
||||
@ -114,7 +149,7 @@ class LinearUnit implements AxisUnit
|
||||
} elseif ($value > $this->max) {
|
||||
return 100;
|
||||
} else {
|
||||
return 100 * ($value - $this->min) / ($this->max - $this->min);
|
||||
return 100 * ($value - $this->min) / $this->max - $this->min;
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,6 +211,7 @@ class LinearUnit implements AxisUnit
|
||||
if ($max !== null) {
|
||||
$this->max = $max;
|
||||
$this->staticMax = true;
|
||||
$this->updateMaxValue();
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +225,7 @@ class LinearUnit implements AxisUnit
|
||||
if ($min !== null) {
|
||||
$this->min = $min;
|
||||
$this->staticMin = true;
|
||||
$this->updateMaxValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ namespace Icinga\Cli;
|
||||
use Icinga\Cli\Screen;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Cli\Params;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\ApplicationBootstrap as App;
|
||||
use Exception;
|
||||
|
||||
@ -23,6 +24,10 @@ abstract class Command
|
||||
protected $commandName;
|
||||
protected $actionName;
|
||||
|
||||
private $config;
|
||||
|
||||
private $configs;
|
||||
|
||||
protected $defaultActionName = 'default';
|
||||
|
||||
public function __construct(App $app, $moduleName, $commandName, $actionName, $initialize = true)
|
||||
@ -41,6 +46,51 @@ abstract class Command
|
||||
}
|
||||
}
|
||||
|
||||
public function Config($file = null)
|
||||
{
|
||||
if ($this->isModule()) {
|
||||
return $this->getModuleConfig($file);
|
||||
} else {
|
||||
return $this->getMainConfig($file);
|
||||
}
|
||||
}
|
||||
|
||||
private function getModuleConfig($file = null)
|
||||
{
|
||||
if ($file === null) {
|
||||
if ($this->config === null) {
|
||||
$this->config = Config::module($this->moduleName);
|
||||
}
|
||||
return $this->config;
|
||||
} else {
|
||||
if (! array_key_exists($file, $this->configs)) {
|
||||
$this->configs[$file] = Config::module($this->moduleName, $file);
|
||||
}
|
||||
return $this->configs[$file];
|
||||
}
|
||||
}
|
||||
|
||||
private function getMainConfig($file = null)
|
||||
{
|
||||
if ($file === null) {
|
||||
if ($this->config === null) {
|
||||
$this->config = Config::app();
|
||||
}
|
||||
return $this->config;
|
||||
} else {
|
||||
if (! array_key_exists($file, $this->configs)) {
|
||||
$this->configs[$file] = Config::module($module, $file);
|
||||
}
|
||||
return $this->configs[$file];
|
||||
}
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function isModule()
|
||||
{
|
||||
return substr(get_class($this), 0, 14) === 'Icinga\\Module\\';
|
||||
}
|
||||
|
||||
public function setParams(Params $params)
|
||||
{
|
||||
$this->params = $params;
|
||||
|
@ -192,6 +192,24 @@ class DbQuery extends SimpleQuery
|
||||
return $this->escapeForSql(date('Y-m-d H:i:s', $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for timestamp fields
|
||||
*
|
||||
* TODO: This is not here to do automagic timestamp stuff. One may
|
||||
* override this function for custom voodoo, IdoQuery right now
|
||||
* does. IMO we need to split whereToSql functionality, however
|
||||
* I'd prefer to wait with this unless we understood how other
|
||||
* backends will work. We probably should also rename this
|
||||
* function to isTimestampColumn().
|
||||
*
|
||||
* @param string $field Field Field name to checked
|
||||
* @return bool Whether this field expects timestamps
|
||||
*/
|
||||
public function isTimestamp($field)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function whereToSql($col, $sign, $expression)
|
||||
{
|
||||
if ($this->isTimestamp($col)) {
|
||||
|
18
library/Icinga/Data/Identifiable.php
Normal file
18
library/Icinga/Data/Identifiable.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
/**
|
||||
* Interface for objects that are identifiable by an ID of any type
|
||||
*/
|
||||
interface Identifiable
|
||||
{
|
||||
/**
|
||||
* Get the ID associated with this Identifiable object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId();
|
||||
}
|
79
library/Icinga/Data/Tree/Node.php
Normal file
79
library/Icinga/Data/Tree/Node.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data\Tree;
|
||||
|
||||
use SplDoublyLinkedList;
|
||||
|
||||
class Node extends SplDoublyLinkedList implements NodeInterface
|
||||
{
|
||||
/**
|
||||
* The node's value
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Create a new node
|
||||
*
|
||||
* @param mixed $value The node's value
|
||||
*/
|
||||
public function __construct($value = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node's value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new node from the given value and insert the node as the last child of this node
|
||||
*
|
||||
* @param mixed $value The node's value
|
||||
*
|
||||
* @return NodeInterface The appended node
|
||||
*/
|
||||
public function appendChild($value)
|
||||
{
|
||||
$child = new static($value);
|
||||
$this->push($child);
|
||||
return $child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this node has child nodes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
$current = $this->current();
|
||||
if ($current === null) {
|
||||
$current = $this;
|
||||
}
|
||||
return ! $current->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node's child nodes
|
||||
*
|
||||
* @return NodeInterface
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
$current = $this->current();
|
||||
if ($current === null) {
|
||||
$current = $this;
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
}
|
26
library/Icinga/Data/Tree/NodeInterface.php
Normal file
26
library/Icinga/Data/Tree/NodeInterface.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data\Tree;
|
||||
|
||||
use RecursiveIterator;
|
||||
|
||||
interface NodeInterface extends RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* Create a new node from the given value and insert the node as the last child of this node
|
||||
*
|
||||
* @param mixed $value The node's value
|
||||
*
|
||||
* @return NodeInterface The appended node
|
||||
*/
|
||||
public function appendChild($value);
|
||||
|
||||
/**
|
||||
* Get the node's value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue();
|
||||
}
|
@ -109,7 +109,19 @@ class CommandPipe
|
||||
*/
|
||||
public function send($command)
|
||||
{
|
||||
$this->transport->send($command);
|
||||
$this->transport->send($this->escape($command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given command string with escaped newlines
|
||||
*
|
||||
* @param string $command The command string to escape
|
||||
*
|
||||
* @return string The escaped command string
|
||||
*/
|
||||
public function escape($command)
|
||||
{
|
||||
return str_replace(array("\r", "\n"), array('\r', '\n'), $command);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,16 +133,14 @@ class CommandPipe
|
||||
public function sendCommand(Command $command, array $objects = array())
|
||||
{
|
||||
if ($command->provideGlobalCommand() === true) {
|
||||
$this->transport->send($command->getGlobalCommand());
|
||||
$this->send($command->getGlobalCommand());
|
||||
} else {
|
||||
foreach ($objects as $object) {
|
||||
$objectType = $this->getObjectType($object);
|
||||
if ($objectType === self::TYPE_SERVICE) {
|
||||
$this->transport->send(
|
||||
$command->getServiceCommand($object->host_name, $object->service_description)
|
||||
);
|
||||
$this->send($command->getServiceCommand($object->host_name, $object->service_description));
|
||||
} else {
|
||||
$this->transport->send($command->getHostCommand($object->host_name));
|
||||
$this->send($command->getHostCommand($object->host_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Protocol\File;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown if a file reader specific error occurs
|
||||
*/
|
||||
class FileReaderException extends RuntimeException {}
|
@ -5,6 +5,7 @@
|
||||
namespace Icinga\Protocol\File;
|
||||
|
||||
use Icinga\Data\SimpleQuery;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
|
||||
/**
|
||||
* Class Query
|
||||
@ -32,7 +33,7 @@ class Query extends SimpleQuery
|
||||
/**
|
||||
* Nothing to do here
|
||||
*/
|
||||
public function applyFilter()
|
||||
public function applyFilter(Filter $filter)
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -42,9 +43,11 @@ class Query extends SimpleQuery
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function order($dir)
|
||||
public function order($field, $direction = null)
|
||||
{
|
||||
$this->sortDir = ($dir === null || strtoupper(trim($dir)) === 'DESC') ? self::SORT_DESC : self::SORT_ASC;
|
||||
$this->sortDir = (
|
||||
$direction === null || strtoupper(trim($direction)) === 'DESC'
|
||||
) ? self::SORT_DESC : self::SORT_ASC;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -80,4 +83,4 @@ class Query extends SimpleQuery
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,38 +4,92 @@
|
||||
|
||||
namespace Icinga\Protocol\File;
|
||||
|
||||
use Icinga\Data\DatasourceInterface;
|
||||
use FilterIterator;
|
||||
use Iterator;
|
||||
use Zend_Config;
|
||||
use Icinga\Protocol\File\FileReaderException;
|
||||
use Icinga\Util\File;
|
||||
|
||||
/**
|
||||
* Class Reader
|
||||
*
|
||||
* Read file line by line
|
||||
*
|
||||
* @package Icinga\Protocol\File
|
||||
*/
|
||||
class Reader implements DatasourceInterface
|
||||
class Reader extends FilterIterator
|
||||
{
|
||||
/**
|
||||
* Name of the file to read
|
||||
* A PCRE string with the fields to extract from the file's lines as named subpatterns
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $filename;
|
||||
protected $fields;
|
||||
|
||||
/**
|
||||
* Configuration for this Datasource
|
||||
* An associative array of the current line's fields ($field => $value)
|
||||
*
|
||||
* @var \Zend_Config
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
protected $currentData;
|
||||
|
||||
/**
|
||||
* @param \Zend_Config $config
|
||||
* Create a new reader
|
||||
*
|
||||
* @param Zend_Config $config
|
||||
*
|
||||
* @throws FileReaderException If a required $config directive (filename or fields) is missing
|
||||
*/
|
||||
public function __construct($config)
|
||||
public function __construct(Zend_Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->filename = $config->filename;
|
||||
foreach (array('filename', 'fields') as $key) {
|
||||
if (! isset($config->{$key})) {
|
||||
throw new FileReaderException('The directive `' . $key . '\' is required');
|
||||
}
|
||||
}
|
||||
$this->fields = $config->fields;
|
||||
$f = new File($config->filename);
|
||||
$f->setFlags(
|
||||
File::DROP_NEW_LINE |
|
||||
File::READ_AHEAD |
|
||||
File::SKIP_EMPTY
|
||||
);
|
||||
parent::__construct($f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->currentData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept lines matching the given PCRE pattern
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws FileReaderException If PHP failed parsing the PCRE pattern
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
$data = array();
|
||||
$matched = @preg_match(
|
||||
$this->fields,
|
||||
$this->getInnerIterator()->current(),
|
||||
$data
|
||||
);
|
||||
if ($matched === false) {
|
||||
throw new FileReaderException('Failed parsing regular expression!');
|
||||
} else if ($matched === 1) {
|
||||
foreach ($data as $key) {
|
||||
if (is_int($key)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
$this->currentData = $data;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,27 +102,78 @@ class Reader implements DatasourceInterface
|
||||
return new Query($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of available valid lines.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return iterator_count($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch result as an array of objects
|
||||
*
|
||||
* @return array
|
||||
* @param Query $query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll(Query $query)
|
||||
{
|
||||
$all = array();
|
||||
foreach ($this->fetchPairs($query) as $index => $value) {
|
||||
$all[$index] = new \stdClass();
|
||||
foreach ($value as $key => $value_2) {
|
||||
$all[$index]->{$key} = $value_2;
|
||||
}
|
||||
$all[$index] = (object) $value;
|
||||
}
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch result as a key/value pair array
|
||||
*
|
||||
* @param Query $query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs(Query $query)
|
||||
{
|
||||
$skipLines = $query->getOffset();
|
||||
$readLines = $query->getLimit();
|
||||
if ($skipLines === null) {
|
||||
$skipLines = 0;
|
||||
}
|
||||
$lines = array();
|
||||
if ($query->sortDesc()) {
|
||||
$count = $this->count($query);
|
||||
if ($count <= $skipLines) {
|
||||
return $lines;
|
||||
} else if ($count < ($skipLines + $readLines)) {
|
||||
$readLines = $count - $skipLines;
|
||||
$skipLines = 0;
|
||||
} else {
|
||||
$skipLines = $count - ($skipLines + $readLines);
|
||||
}
|
||||
}
|
||||
foreach ($this as $index => $line) {
|
||||
if ($index >= $skipLines) {
|
||||
if ($index >= $skipLines + $readLines) {
|
||||
break;
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
}
|
||||
if ($query->sortDesc()) {
|
||||
$lines = array_reverse($lines);
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch first result row
|
||||
*
|
||||
* @return object
|
||||
* @param Query $query
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function fetchRow(Query $query)
|
||||
{
|
||||
@ -82,14 +187,16 @@ class Reader implements DatasourceInterface
|
||||
/**
|
||||
* Fetch first result column
|
||||
*
|
||||
* @return array
|
||||
* @param Query $query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchColumn(Query $query)
|
||||
{
|
||||
$column = array();
|
||||
foreach ($this->fetchPairs($query) as $value) {
|
||||
foreach ($value as $value_2) {
|
||||
$column[] = $value_2;
|
||||
foreach ($this->fetchPairs($query) as $pair) {
|
||||
foreach ($pair as $value) {
|
||||
$column[] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -99,7 +206,9 @@ class Reader implements DatasourceInterface
|
||||
/**
|
||||
* Fetch first column value from first result row
|
||||
*
|
||||
* @return mixed
|
||||
* @param Query $query
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetchOne(Query $query)
|
||||
{
|
||||
@ -111,215 +220,4 @@ class Reader implements DatasourceInterface
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch result as a key/value pair array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs(Query $query)
|
||||
{
|
||||
return $this->read($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* If given $line matches the $query's PCRE pattern and contains all the strings in the $query's filters array,
|
||||
* return an associative array of the matches of the PCRE pattern.
|
||||
* Otherwise, return false.
|
||||
* If preg_match returns false, it failed parsing the PCRE pattern.
|
||||
* In that case, throw an exception.
|
||||
*
|
||||
* @param string $line
|
||||
* @param Query $query
|
||||
*
|
||||
* @return array|bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function validateLine($line, Query $query)
|
||||
{
|
||||
$data = array();
|
||||
$PCRE_result = @preg_match($this->config->fields, $line, $data);
|
||||
if ($PCRE_result === false) {
|
||||
throw new \Exception('Failed parsing regular expression!');
|
||||
} else if ($PCRE_result === 1) {
|
||||
foreach ($query->getFilters() as $filter) {
|
||||
if (strpos($line, $filter) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip and read as many lines as needed according to given $query.
|
||||
*
|
||||
* @param Query $query
|
||||
*
|
||||
* @return array result
|
||||
*/
|
||||
public function read(Query $query)
|
||||
{
|
||||
$skip_lines = $query->getOffset();
|
||||
$read_lines = $query->getLimit();
|
||||
if ($skip_lines === null) {
|
||||
$skip_lines = 0;
|
||||
}
|
||||
return $this->{$query->sortDesc() ? 'readFromEnd' : 'readFromStart'}($skip_lines, $read_lines, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend for $this->read
|
||||
* Direction: LIFO
|
||||
*/
|
||||
public function readFromEnd($skip_lines, $read_lines, Query $query)
|
||||
{
|
||||
$PHP_EOL_len = strlen(PHP_EOL);
|
||||
$lines = array();
|
||||
$s = '';
|
||||
$f = @fopen($this->filename, 'rb');
|
||||
if ($f !== false) {
|
||||
$buffer = '';
|
||||
fseek($f, 0, SEEK_END);
|
||||
if (ftell($f) === 0) {
|
||||
return array();
|
||||
}
|
||||
while ($read_lines === null || count($lines) < $read_lines) {
|
||||
$c = $this->fgetc($f, $buffer);
|
||||
if ($c === false) {
|
||||
$l = $this->validateLine($s, $query);
|
||||
if (!($l === false || $skip_lines)) {
|
||||
$lines[] = $l;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$s = $c . $s;
|
||||
if (strpos($s, PHP_EOL) === 0) {
|
||||
$l = $this->validateLine((string)substr($s, $PHP_EOL_len), $query);
|
||||
if ($l !== false) {
|
||||
if ($skip_lines) {
|
||||
$skip_lines--;
|
||||
} else {
|
||||
$lines[] = $l;
|
||||
}
|
||||
}
|
||||
$s = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend for $this->readFromEnd
|
||||
*/
|
||||
public function fgetc($file, &$buffer)
|
||||
{
|
||||
$strlen = strlen($buffer);
|
||||
if ($strlen === 0) {
|
||||
$pos = ftell($file);
|
||||
if ($pos === 0) {
|
||||
return false;
|
||||
}
|
||||
if ($pos < 4096) {
|
||||
fseek($file, 0);
|
||||
$buffer = fread($file, $pos);
|
||||
fseek($file, 0);
|
||||
} else {
|
||||
fseek($file, -4096, SEEK_CUR);
|
||||
$buffer = fread($file, 4096);
|
||||
fseek($file, -4096, SEEK_CUR);
|
||||
}
|
||||
return $this->fgetc($file, $buffer);
|
||||
} else {
|
||||
$char = substr($buffer, -1);
|
||||
$buffer = substr($buffer, 0, $strlen - 1);
|
||||
return $char;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend for $this->read
|
||||
* Direction: FIFO
|
||||
*/
|
||||
public function readFromStart($skip_lines, $read_lines, Query $query)
|
||||
{
|
||||
$PHP_EOL_len = strlen(PHP_EOL);
|
||||
$lines = array();
|
||||
$s = '';
|
||||
$f = @fopen($this->filename, 'rb');
|
||||
if ($f !== false) {
|
||||
$buffer = '';
|
||||
while ($read_lines === null || count($lines) < $read_lines) {
|
||||
if (strlen($buffer) === 0) {
|
||||
$buffer = fread($f, 4096);
|
||||
if (strlen($buffer) === 0) {
|
||||
$l = $this->validateLine($s, $query);
|
||||
if (!($l === false || $skip_lines)) {
|
||||
$lines[] = $l;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$s .= substr($buffer, 0, 1);
|
||||
$buffer = substr($buffer, 1);
|
||||
if (strpos($s, PHP_EOL) !== false) {
|
||||
$l = $this->validateLine((string)substr($s, 0, strlen($s) - $PHP_EOL_len), $query);
|
||||
if ($l !== false) {
|
||||
if ($skip_lines) {
|
||||
$skip_lines--;
|
||||
} else {
|
||||
$lines[] = $l;
|
||||
}
|
||||
}
|
||||
$s = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of available valid lines.
|
||||
*
|
||||
* @param Query $query
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(Query $query) {
|
||||
$PHP_EOL_len = strlen(PHP_EOL);
|
||||
$lines = 0;
|
||||
$s = '';
|
||||
$f = @fopen($this->filename, 'rb');
|
||||
if ($f !== false) {
|
||||
$buffer = '';
|
||||
while (true) {
|
||||
if (strlen($buffer) === 0) {
|
||||
$buffer = fread($f, 4096);
|
||||
if (strlen($buffer) === 0) {
|
||||
if ($this->validateLine($s, $query) !== false) {
|
||||
$lines++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$s .= substr($buffer, 0, 1);
|
||||
$buffer = substr($buffer, 1);
|
||||
if (strpos($s, PHP_EOL) !== false) {
|
||||
if ($this->validateLine((string)substr($s, 0, strlen($s) - $PHP_EOL_len), $query) !== false) {
|
||||
$lines++;
|
||||
}
|
||||
$s = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,18 @@ class User
|
||||
*/
|
||||
protected $additionalInformation = array();
|
||||
|
||||
/**
|
||||
* Information if the user is external authenticated
|
||||
*
|
||||
* Keys:
|
||||
*
|
||||
* 0: origin username
|
||||
* 1: origin field name
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $remoteUserInformation = array();
|
||||
|
||||
/**
|
||||
* Set of permissions
|
||||
*
|
||||
@ -401,4 +413,35 @@ class User
|
||||
{
|
||||
$this->messages = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional remote user information
|
||||
*
|
||||
* @param stirng $username
|
||||
* @param string $field
|
||||
*/
|
||||
public function setRemoteUserInformation($username, $field)
|
||||
{
|
||||
$this->remoteUserInformation = array($username, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get additional remote user information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRemoteUserInformation()
|
||||
{
|
||||
return $this->remoteUserInformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if user has remote user information set
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRemoteUser()
|
||||
{
|
||||
return (count($this->remoteUserInformation)) ? true : false;
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ class Translator
|
||||
*/
|
||||
public static function getAvailableLocaleCodes()
|
||||
{
|
||||
$codes = array();
|
||||
$codes = array(static::DEFAULT_LOCALE);
|
||||
foreach (array_values(self::$knownDomains) as $directory) {
|
||||
$dh = opendir($directory);
|
||||
while (false !== ($name = readdir($dh))) {
|
||||
|
@ -254,10 +254,27 @@ class ActionController extends Zend_Controller_Action
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function redirectToLogin($afterLogin = '/dashboard')
|
||||
protected function redirectToLogin($afterLogin = null)
|
||||
{
|
||||
$redir = null;
|
||||
if ($afterLogin !== null) {
|
||||
if (! $afterLogin instanceof Url) {
|
||||
$afterLogin = Url::fromPath($afterLogin);
|
||||
}
|
||||
if ($this->isXhr()) {
|
||||
$redir = '__SELF__';
|
||||
} else {
|
||||
// TODO: Ignore /?
|
||||
$redir = $afterLogin->getRelativeUrl();
|
||||
}
|
||||
}
|
||||
|
||||
$url = Url::fromPath('authentication/login');
|
||||
$url->setParam('redirect', $afterLogin);
|
||||
|
||||
if ($redir) {
|
||||
$url->setParam('redirect', $redir);
|
||||
}
|
||||
|
||||
$this->rerenderLayout()->redirectNow($url);
|
||||
}
|
||||
|
||||
@ -273,6 +290,27 @@ class ActionController extends Zend_Controller_Action
|
||||
return $this->getRequest()->isXmlHttpRequest();
|
||||
}
|
||||
|
||||
protected function redirectXhr($url)
|
||||
{
|
||||
if (! $url instanceof Url) {
|
||||
$url = Url::fromPath($url);
|
||||
}
|
||||
|
||||
if ($this->rerenderLayout) {
|
||||
$this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
|
||||
}
|
||||
if ($this->reloadCss) {
|
||||
$this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
|
||||
}
|
||||
|
||||
$this->getResponse()
|
||||
->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl()))
|
||||
->sendHeaders();
|
||||
|
||||
// TODO: Session shutdown?
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to a specific url, updating the browsers URL field
|
||||
*
|
||||
@ -280,26 +318,13 @@ class ActionController extends Zend_Controller_Action
|
||||
**/
|
||||
public function redirectNow($url)
|
||||
{
|
||||
if (! $url instanceof Url) {
|
||||
$url = Url::fromPath($url);
|
||||
}
|
||||
$url = preg_replace('~&~', '&', $url);
|
||||
if ($this->isXhr()) {
|
||||
if ($this->rerenderLayout) {
|
||||
$this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes');
|
||||
}
|
||||
if ($this->reloadCss) {
|
||||
$this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now');
|
||||
}
|
||||
|
||||
$this->getResponse()
|
||||
->setHeader('X-Icinga-Redirect', rawurlencode($url))
|
||||
->sendHeaders();
|
||||
|
||||
// TODO: Session shutdown?
|
||||
exit;
|
||||
$this->redirectXhr($url);
|
||||
} else {
|
||||
$this->_helper->Redirector->gotoUrlAndExit(Url::fromPath($url)->getRelativeUrl());
|
||||
if (! $url instanceof Url) {
|
||||
$url = Url::fromPath($url);
|
||||
}
|
||||
$this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,6 +387,8 @@ class ActionController extends Zend_Controller_Action
|
||||
'X-Icinga-Title',
|
||||
rawurlencode($this->view->title . ' :: Icinga Web')
|
||||
);
|
||||
} else {
|
||||
$resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web'));
|
||||
}
|
||||
|
||||
if ($this->rerenderLayout) {
|
||||
|
@ -127,10 +127,6 @@ class Url
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
$urlObject->setBaseUrl($baseUrl);
|
||||
|
||||
// Fetch fragment manually and remove it from the url, to 'help' the parse_url() function
|
||||
// parsing the url properly. Otherwise calling the function with a fragment, but without a
|
||||
// query will cause unpredictable behaviour.
|
||||
$url = self::stripUrlFragment($url);
|
||||
$urlParts = parse_url($url);
|
||||
if (isset($urlParts['path'])) {
|
||||
if ($baseUrl !== '' && strpos($urlParts['path'], $baseUrl) === 0) {
|
||||
@ -144,44 +140,14 @@ class Url
|
||||
$params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params);
|
||||
}
|
||||
|
||||
$fragment = self::getUrlFragment($url);
|
||||
if ($fragment !== '') {
|
||||
$urlObject->setAnchor($fragment);
|
||||
if (isset($urlParts['fragment'])) {
|
||||
$urlObject->setAnchor($urlParts['fragment']);
|
||||
}
|
||||
|
||||
$urlObject->setParams($params);
|
||||
return $urlObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fragment of a given url
|
||||
*
|
||||
* @param string $url The url containing the fragment.
|
||||
*
|
||||
* @return string The fragment without the '#'
|
||||
*/
|
||||
protected static function getUrlFragment($url)
|
||||
{
|
||||
$url = parse_url($url);
|
||||
if (isset($url['fragment'])) {
|
||||
return $url['fragment'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the fragment-part of a given url
|
||||
*
|
||||
* @param string $url The url to strip from its fragment
|
||||
*
|
||||
* @return string The url without the fragment
|
||||
*/
|
||||
protected static function stripUrlFragment($url)
|
||||
{
|
||||
return preg_replace('/#.*$/', '', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the baseUrl
|
||||
*
|
||||
@ -241,12 +207,12 @@ class Url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeUrl()
|
||||
public function getRelativeUrl($separator = '&')
|
||||
{
|
||||
if ($this->params->isEmpty()) {
|
||||
return $this->path . $this->anchor;
|
||||
} else {
|
||||
return $this->path . '?' . $this->params->setSeparator('&') . $this->anchor;
|
||||
return $this->path . '?' . $this->params->toString($separator) . $this->anchor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,9 +232,9 @@ class Url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl()
|
||||
public function getAbsoluteUrl($separator = '&')
|
||||
{
|
||||
return $this->baseUrl . ($this->baseUrl !== '/' ? '/' : '') . $this->getRelativeUrl();
|
||||
return $this->baseUrl . ($this->baseUrl !== '/' ? '/' : '') . $this->getRelativeUrl($separator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,11 +297,11 @@ class Url
|
||||
/**
|
||||
* Return all parameters that will be used in the query part
|
||||
*
|
||||
* @return array An associative key => value array containing all parameters
|
||||
* @return UrlParams An instance of UrlParam containing all parameters
|
||||
*/
|
||||
public function getParams()
|
||||
{
|
||||
return $this->params->asArray();
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -450,6 +416,6 @@ class Url
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getAbsoluteUrl();
|
||||
return $this->getAbsoluteUrl('&');
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +114,18 @@ class UrlParams
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function addEncoded($param, $value = true)
|
||||
{
|
||||
$this->params[] = array($param, $this->cleanupValue($value));
|
||||
$this->indexLastOne();
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function urlEncode($value)
|
||||
{
|
||||
return rawurlencode((string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given parameter with the given value
|
||||
*
|
||||
@ -127,9 +139,7 @@ class UrlParams
|
||||
*/
|
||||
public function add($param, $value = true)
|
||||
{
|
||||
$this->params[] = array($param, $this->cleanupValue($value));
|
||||
$this->indexLastOne();
|
||||
return $this;
|
||||
return $this->addEncoded($this->urlEncode($param), $this->urlEncode($value));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,7 +208,7 @@ class UrlParams
|
||||
*/
|
||||
public function unshift($param, $value)
|
||||
{
|
||||
array_unshift($this->params, array($param, $this->cleanupValue($value)));
|
||||
array_unshift($this->params, array($this->urlEncode($param), $this->urlEncode($value)));
|
||||
$this->reIndexAll();
|
||||
return $this;
|
||||
}
|
||||
@ -224,7 +234,10 @@ class UrlParams
|
||||
unset($this->params[$remove]);
|
||||
}
|
||||
|
||||
$this->params[$this->index[$param][0]] = array($param, $this->cleanupValue($value));
|
||||
$this->params[$this->index[$param][0]] = array(
|
||||
$this->urlEncode($param),
|
||||
$this->urlEncode($this->cleanupValue($value))
|
||||
);
|
||||
$this->reIndexAll();
|
||||
|
||||
return $this;
|
||||
@ -243,7 +256,7 @@ class UrlParams
|
||||
foreach ($this->index[$p] as $key) {
|
||||
unset($this->params[$key]);
|
||||
}
|
||||
$this->changed = true;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,20 +316,23 @@ class UrlParams
|
||||
protected function parseQueryStringPart($part)
|
||||
{
|
||||
if (strpos($part, '=') === false) {
|
||||
$this->add($part, true);
|
||||
$this->addEncoded($part, true);
|
||||
} else {
|
||||
list($key, $val) = preg_split('/=/', $part, 2);
|
||||
$this->add($key, $val);
|
||||
$this->addEncoded($key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
public function asArray()
|
||||
public function toArray()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
public function toString($separator = null)
|
||||
{
|
||||
if ($separator === null) {
|
||||
$separator = $this->separator;
|
||||
}
|
||||
$parts = array();
|
||||
foreach ($this->params as $p) {
|
||||
if ($p[1] === true) {
|
||||
@ -325,13 +341,18 @@ class UrlParams
|
||||
$parts[] = $p[0] . '=' . $p[1];
|
||||
}
|
||||
}
|
||||
return implode($this->separator, $parts);
|
||||
return implode($separator, $parts);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public static function fromQueryString($queryString = null)
|
||||
{
|
||||
if ($queryString === null) {
|
||||
$queryString = $_SERVER['QUERY_STRING'];
|
||||
$queryString = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
|
||||
}
|
||||
$params = new static();
|
||||
$params->parseQueryString($queryString);
|
||||
|
@ -126,7 +126,7 @@ EOD;
|
||||
public function toArray()
|
||||
{
|
||||
$array = array('url' => $this->url->getPath());
|
||||
foreach ($this->url->getParams() as $param) {
|
||||
foreach ($this->url->getParams()->toArray() as $param) {
|
||||
$array[$param[0]] = $param[1];
|
||||
}
|
||||
return $array;
|
||||
|
@ -81,7 +81,7 @@ class Limiter extends AbstractWidget
|
||||
$this->url->setParam('limit', $limit),
|
||||
null,
|
||||
array(
|
||||
'title' => t(sprintf('Show %s rows on one page', $caption))
|
||||
'title' => sprintf(t('Show %s rows on one page'), $caption)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class DashboardAction implements Tabextension
|
||||
'title' => 'Add To Dashboard',
|
||||
'url' => Url::fromPath('dashboard/addurl'),
|
||||
'urlParams' => array(
|
||||
'url' => Url::fromRequest()->getRelativeUrl()
|
||||
'url' => rawurlencode(Url::fromRequest()->getRelativeUrl())
|
||||
)
|
||||
)
|
||||
);
|
||||
|
48
modules/doc/application/controllers/IcingawebController.php
Normal file
48
modules/doc/application/controllers/IcingawebController.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use \Zend_Controller_Action_Exception;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Module\Doc\DocController;
|
||||
|
||||
class Doc_IcingawebController extends DocController
|
||||
{
|
||||
/**
|
||||
* View the toc of Icinga Web 2's documentation
|
||||
*/
|
||||
public function tocAction()
|
||||
{
|
||||
$this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter');
|
||||
}
|
||||
|
||||
/**
|
||||
* View a chapter of Icinga Web 2's documentation
|
||||
*
|
||||
* @throws Zend_Controller_Action_Exception If the required parameter 'chapterId' is missing
|
||||
*/
|
||||
public function chapterAction()
|
||||
{
|
||||
$chapterId = $this->getParam('chapterId');
|
||||
if ($chapterId === null) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
$this->translate('Missing parameter \'chapterId\''),
|
||||
404
|
||||
);
|
||||
}
|
||||
$this->renderChapter(
|
||||
Icinga::app()->getApplicationDir('/../doc'),
|
||||
$chapterId,
|
||||
'doc/icingaweb/toc',
|
||||
'doc/icingaweb/chapter'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* View Icinga Web 2's documentation as PDF
|
||||
*/
|
||||
public function pdfAction()
|
||||
{
|
||||
$this->renderPdf(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter');
|
||||
}
|
||||
}
|
@ -2,34 +2,9 @@
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use Icinga\Module\Doc\Controller as DocController;
|
||||
|
||||
use Icinga\Module\Doc\DocParser;
|
||||
use Icinga\Module\Doc\DocController;
|
||||
|
||||
class Doc_IndexController extends DocController
|
||||
{
|
||||
protected $parser;
|
||||
|
||||
|
||||
public function init()
|
||||
{
|
||||
$module = null;
|
||||
$this->parser = new DocParser($module);
|
||||
}
|
||||
|
||||
|
||||
public function tocAction()
|
||||
{
|
||||
// Temporary workaround
|
||||
list($html, $toc) = $this->parser->getDocumentation();
|
||||
$this->view->toc = $toc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the application's documentation
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$this->populateView();
|
||||
}
|
||||
public function indexAction() {}
|
||||
}
|
||||
|
@ -2,44 +2,131 @@
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use \Zend_Controller_Action_Exception;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Module\Doc\Controller as DocController;
|
||||
use Icinga\Module\Doc\DocController;
|
||||
use Icinga\Module\Doc\Exception\DocException;
|
||||
|
||||
class Doc_ModuleController extends DocController
|
||||
{
|
||||
/**
|
||||
* Display module documentations index
|
||||
* List modules which are enabled and having the 'doc' directory
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->enabledModules = Icinga::app()->getModuleManager()->listEnabledModules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a module's documentation
|
||||
*/
|
||||
public function viewAction()
|
||||
{
|
||||
$this->populateView($this->getParam('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide run-time dispatching of module documentation
|
||||
*
|
||||
* @param string $methodName
|
||||
* @param array $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($methodName, $args)
|
||||
{
|
||||
// TODO(el): Setup routing to retrieve module name as param and point route to moduleAction
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
$moduleName = substr($methodName, 0, -6); // Strip 'Action' suffix
|
||||
if (!$moduleManager->hasEnabled($moduleName)) {
|
||||
// TODO(el): Throw a not found exception once the code has been moved to the moduleAction (see TODO above)
|
||||
return parent::__call($methodName, $args);
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
$modules = array();
|
||||
foreach (Icinga::app()->getModuleManager()->listEnabledModules() as $enabledModule) {
|
||||
$docDir = $moduleManager->getModuleDir($enabledModule, '/doc');
|
||||
if (is_dir($docDir)) {
|
||||
$modules[] = $enabledModule;
|
||||
}
|
||||
}
|
||||
$this->_helper->redirector->gotoSimpleAndExit('view', null, null, array('name' => $moduleName));
|
||||
$this->view->modules = $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given module is enabled
|
||||
*
|
||||
* @param $moduleName
|
||||
*
|
||||
* @throws Zend_Controller_Action_Exception If the required parameter 'moduleName' is empty or either if the
|
||||
* given module is neither installed nor enabled
|
||||
*/
|
||||
protected function assertModuleEnabled($moduleName)
|
||||
{
|
||||
if (empty($moduleName)) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
$this->translate('Missing parameter \'moduleName\''),
|
||||
404
|
||||
);
|
||||
}
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
if (! $moduleManager->hasInstalled($moduleName)) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
sprintf($this->translate('Module \'%s\' is not installed'), $moduleName),
|
||||
404
|
||||
);
|
||||
}
|
||||
if (! $moduleManager->hasEnabled($moduleName)) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
sprintf($this->translate('Module \'%s\' is not enabled'), $moduleName),
|
||||
404
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* View the toc of a module's documentation
|
||||
*
|
||||
* @see assertModuleEnabled()
|
||||
*/
|
||||
public function tocAction()
|
||||
{
|
||||
$moduleName = $this->getParam('moduleName');
|
||||
$this->assertModuleEnabled($moduleName);
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
try {
|
||||
$this->renderToc(
|
||||
$moduleManager->getModuleDir($moduleName, '/doc'),
|
||||
$moduleName,
|
||||
'doc/module/chapter',
|
||||
array('moduleName' => $moduleName)
|
||||
);
|
||||
} catch (DocException $e) {
|
||||
throw new Zend_Controller_Action_Exception($e->getMessage(), 404);
|
||||
}
|
||||
$this->view->moduleName = $moduleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* View a chapter of a module's documentation
|
||||
*
|
||||
* @throws Zend_Controller_Action_Exception If the required parameter 'chapterId' is missing or if an error in
|
||||
* the documentation module's library occurs
|
||||
* @see assertModuleEnabled()
|
||||
*/
|
||||
public function chapterAction()
|
||||
{
|
||||
$moduleName = $this->getParam('moduleName');
|
||||
$this->assertModuleEnabled($moduleName);
|
||||
$chapterId = $this->getParam('chapterId');
|
||||
if ($chapterId === null) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
$this->translate('Missing parameter \'chapterId\''),
|
||||
404
|
||||
);
|
||||
}
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
try {
|
||||
$this->renderChapter(
|
||||
$moduleManager->getModuleDir($moduleName, '/doc'),
|
||||
$chapterId,
|
||||
$this->_helper->url->url(array('moduleName' => $moduleName), 'doc/module/toc'),
|
||||
'doc/module/chapter',
|
||||
array('moduleName' => $moduleName)
|
||||
);
|
||||
} catch (DocException $e) {
|
||||
throw new Zend_Controller_Action_Exception($e->getMessage(), 404);
|
||||
}
|
||||
$this->view->moduleName = $moduleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* View a module's documentation as PDF
|
||||
*
|
||||
* @see assertModuleEnabled()
|
||||
*/
|
||||
public function pdfAction()
|
||||
{
|
||||
$moduleName = $this->getParam('moduleName');
|
||||
$this->assertModuleEnabled($moduleName);
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
$this->renderPdf(
|
||||
$moduleManager->getModuleDir($moduleName, '/doc'),
|
||||
$moduleName,
|
||||
'doc/module/chapter',
|
||||
array('moduleName' => $moduleName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
3
modules/doc/application/views/scripts/chapter.phtml
Normal file
3
modules/doc/application/views/scripts/chapter.phtml
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="chapter">
|
||||
<?= $sectionRenderer->render($this, $this->getHelper('Url')); ?>
|
||||
</div>
|
@ -1,5 +1,6 @@
|
||||
<h1>Icinga 2 Documentation</h1>
|
||||
<?= $this->partial('module/view.phtml', 'doc', array(
|
||||
'toc' => $toc,
|
||||
'html' => $html
|
||||
)); ?>
|
||||
<div class="controls"></div>
|
||||
<h1><?= $this->translate('Available documentations'); ?></h1>
|
||||
<ul>
|
||||
<li><a href="<?= $this->href('doc/icingaweb/toc'); ?>">Icinga Web 2</a></li>
|
||||
<li><a href="<?= $this->href('doc/module/'); ?>"><?= $this->translate('Module documentations'); ?></a></li>
|
||||
</ul>
|
||||
|
@ -1,14 +0,0 @@
|
||||
<div class="controls">
|
||||
<h1>Module documentations</h1>
|
||||
</div>
|
||||
<div class="content" data-base-target="_next">
|
||||
<?= $this->partial(
|
||||
'layout/menu.phtml',
|
||||
'default',
|
||||
array(
|
||||
'items' => $toc->getChildren(),
|
||||
'sub' => false,
|
||||
'url' => ''
|
||||
)
|
||||
) ?>
|
||||
</div>
|
@ -1,6 +1,10 @@
|
||||
<h1>Module documentations</h1>
|
||||
<h1><?= $this->translate('Module documentations'); ?></h1>
|
||||
<ul>
|
||||
<?php foreach ($enabledModules as $module): ?>
|
||||
<li><a href="<?= $this->href('doc/module/view', array('name' => $module)); ?>"><?= $module ?></a></li>
|
||||
<?php endforeach ?>
|
||||
<?php foreach ($modules as $module): ?>
|
||||
<li>
|
||||
<a href="<?= $this->getHelper('Url')->url(array('moduleName' => $module), 'doc/module/toc', false, false); ?>">
|
||||
<?= $module ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
|
@ -1,7 +0,0 @@
|
||||
<?php if ($html === null): ?>
|
||||
<p>No documentation available.</p>
|
||||
<?php else: ?>
|
||||
<div class="content">
|
||||
<?= $html ?>
|
||||
</div>
|
||||
<?php endif ?>
|
7
modules/doc/application/views/scripts/pdf.phtml
Normal file
7
modules/doc/application/views/scripts/pdf.phtml
Normal file
@ -0,0 +1,7 @@
|
||||
<h1><?= $docName ?> <?= $this->translate('Documentation'); ?></h1>
|
||||
<div class="toc">
|
||||
<?= $tocRenderer->render($this, $this->getHelper('Url')); ?>
|
||||
</div>
|
||||
<div class="chapter">
|
||||
<?= $sectionRenderer->render($this, $this->getHelper('Url')); ?>
|
||||
</div>
|
6
modules/doc/application/views/scripts/toc.phtml
Normal file
6
modules/doc/application/views/scripts/toc.phtml
Normal file
@ -0,0 +1,6 @@
|
||||
<div class="controls">
|
||||
<h1><?= $title ?></h1>
|
||||
</div>
|
||||
<div class="content toc">
|
||||
<?= $tocRenderer->render($this, $this->getHelper('Url')); ?>
|
||||
</div>
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use Icinga\Web\Controller\ModuleActionController;
|
||||
|
||||
class Controller extends ModuleActionController
|
||||
{
|
||||
/**
|
||||
* Set HTML and toc
|
||||
*
|
||||
* @param string $module
|
||||
*/
|
||||
protected function populateView($module = null)
|
||||
{
|
||||
$parser = new DocParser($module);
|
||||
list($html, $toc) = $parser->getDocumentation();
|
||||
$this->view->html = $html;
|
||||
$this->view->toc = $toc;
|
||||
}
|
||||
}
|
76
modules/doc/library/Doc/DocController.php
Normal file
76
modules/doc/library/Doc/DocController.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use Icinga\Web\Controller\ModuleActionController;
|
||||
|
||||
class DocController extends ModuleActionController
|
||||
{
|
||||
/**
|
||||
* Render a chapter
|
||||
*
|
||||
* @param string $path Path to the documentation
|
||||
* @param string $chapterId ID of the chapter
|
||||
* @param string $tocUrl
|
||||
* @param string $url
|
||||
* @param array $urlParams
|
||||
*/
|
||||
protected function renderChapter($path, $chapterId, $tocUrl, $url, array $urlParams = array())
|
||||
{
|
||||
$parser = new DocParser($path);
|
||||
$this->view->sectionRenderer = new SectionRenderer(
|
||||
$parser->getDocTree(),
|
||||
SectionRenderer::decodeUrlParam($chapterId),
|
||||
$tocUrl,
|
||||
$url,
|
||||
$urlParams
|
||||
);
|
||||
$this->view->title = $chapterId;
|
||||
$this->_helper->viewRenderer('chapter', null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a toc
|
||||
*
|
||||
* @param string $path Path to the documentation
|
||||
* @param string $name Name of the documentation
|
||||
* @param string $url
|
||||
* @param array $urlParams
|
||||
*/
|
||||
protected function renderToc($path, $name, $url, array $urlParams = array())
|
||||
{
|
||||
$parser = new DocParser($path);
|
||||
$this->view->tocRenderer = new TocRenderer($parser->getDocTree(), $url, $urlParams);
|
||||
$name = ucfirst($name);
|
||||
$this->view->docName = $name;
|
||||
$this->view->title = sprintf($this->translate('%s Documentation'), $name);
|
||||
$this->_helper->viewRenderer('toc', null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a pdf
|
||||
*
|
||||
* @param string $path Path to the documentation
|
||||
* @param string $name Name of the documentation
|
||||
* @param string $url
|
||||
* @param array $urlParams
|
||||
*/
|
||||
protected function renderPdf($path, $name, $url, array $urlParams = array())
|
||||
{
|
||||
$parser = new DocParser($path);
|
||||
$docTree = $parser->getDocTree();
|
||||
$this->view->tocRenderer = new TocRenderer($docTree, $url, $urlParams);
|
||||
$this->view->sectionRenderer = new SectionRenderer(
|
||||
$docTree,
|
||||
null,
|
||||
null,
|
||||
$url,
|
||||
$urlParams
|
||||
);
|
||||
$this->view->docName = $name;
|
||||
$this->_helper->viewRenderer('pdf', null, true);
|
||||
$this->_request->setParam('format', 'pdf');
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use \Exception;
|
||||
|
||||
class DocException extends Exception
|
||||
{
|
||||
}
|
62
modules/doc/library/Doc/DocIterator.php
Normal file
62
modules/doc/library/Doc/DocIterator.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
|
||||
/**
|
||||
* Iterator over non-empty Markdown files ordered by the case insensitive "natural order" of file names
|
||||
*/
|
||||
class DocIterator implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Ordered files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fileInfo;
|
||||
|
||||
/**
|
||||
* Create a new DocIterator
|
||||
*
|
||||
* @param string $path Path to the documentation
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$it = new RecursiveIteratorIterator(
|
||||
new NonEmptyFileIterator(
|
||||
new MarkdownFileIterator(
|
||||
new RecursiveDirectoryIterator($path)
|
||||
)
|
||||
)
|
||||
);
|
||||
// Unfortunately we have no chance to sort the iterator
|
||||
$fileInfo = iterator_to_array($it);
|
||||
natcasesort($fileInfo);
|
||||
$this->fileInfo = $fileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see Countable::count()
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->fileInfo);
|
||||
}
|
||||
}
|
@ -4,146 +4,65 @@
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use Parsedown;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
require_once 'IcingaVendor/Parsedown/Parsedown.php';
|
||||
use SplDoublyLinkedList;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Module\Doc\Exception\DocEmptyException;
|
||||
use Icinga\Module\Doc\Exception\DocException;
|
||||
|
||||
/**
|
||||
* Parser for documentation written in Markdown
|
||||
*/
|
||||
class DocParser
|
||||
{
|
||||
protected $dir;
|
||||
|
||||
protected $module;
|
||||
/**
|
||||
* Path to the documentation
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Create a new documentation parser for the given module or the application
|
||||
* Iterator over documentation files
|
||||
*
|
||||
* @param string $module
|
||||
*
|
||||
* @throws DocException
|
||||
* @var DocIterator
|
||||
*/
|
||||
public function __construct($module = null)
|
||||
{
|
||||
if ($module === null) {
|
||||
$dir = Icinga::app()->getApplicationDir('/../doc');
|
||||
} else {
|
||||
$mm = Icinga::app()->getModuleManager();
|
||||
if (!$mm->hasInstalled($module)) {
|
||||
throw new DocException('Module is not installed');
|
||||
}
|
||||
if (!$mm->hasEnabled($module)) {
|
||||
throw new DocException('Module is not enabled');
|
||||
}
|
||||
$dir = $mm->getModuleDir($module, '/doc');
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
throw new DocException('Doc directory does not exist');
|
||||
}
|
||||
$this->dir = $dir;
|
||||
$this->module = $module;
|
||||
}
|
||||
protected $docIterator;
|
||||
|
||||
/**
|
||||
* Retrieve table of contents and HTML converted from markdown files sorted by filename
|
||||
* Create a new documentation parser for the given path
|
||||
*
|
||||
* @return array
|
||||
* @throws DocException
|
||||
* @param string $path Path to the documentation
|
||||
*
|
||||
* @throws DocException If the documentation directory does not exist
|
||||
* @throws NotReadableError If the documentation directory is not readable
|
||||
* @throws DocEmptyException If the documentation directory is empty
|
||||
*/
|
||||
public function getDocumentation()
|
||||
public function __construct($path)
|
||||
{
|
||||
$iter = new RecursiveIteratorIterator(
|
||||
new MarkdownFileIterator(
|
||||
new RecursiveDirectoryIterator($this->dir)
|
||||
)
|
||||
);
|
||||
$fileInfos = iterator_to_array($iter);
|
||||
natcasesort($fileInfos);
|
||||
$cat = array();
|
||||
$toc = array((object) array(
|
||||
'level' => 0,
|
||||
'item' => new Menu('doc')
|
||||
));
|
||||
$itemPriority = 1;
|
||||
foreach ($fileInfos as $fileInfo) {
|
||||
try {
|
||||
$fileObject = $fileInfo->openFile();
|
||||
} catch (RuntimeException $e) {
|
||||
throw new DocException($e->getMessage());
|
||||
}
|
||||
if ($fileObject->flock(LOCK_SH) === false) {
|
||||
throw new DocException('Couldn\'t get the lock');
|
||||
}
|
||||
$line = null;
|
||||
while (!$fileObject->eof()) {
|
||||
// Save last line for setext-style headers
|
||||
$lastLine = $line;
|
||||
$line = $fileObject->fgets();
|
||||
$header = $this->extractHeader($line, $lastLine);
|
||||
if ($header !== null) {
|
||||
list($header, $level) = $header;
|
||||
$id = $this->extractHeaderId($header);
|
||||
$attribs = array();
|
||||
$this->reduceToc($toc, $level);
|
||||
if ($id === null) {
|
||||
$path = array();
|
||||
foreach (array_slice($toc, 1) as $entry) {
|
||||
$path[] = $entry->item->getTitle();
|
||||
}
|
||||
$path[] = $header;
|
||||
$id = implode('-', $path);
|
||||
$attribs['rel'] = 'nofollow';
|
||||
}
|
||||
$id = urlencode(str_replace('.', '.', strip_tags($id)));
|
||||
$item = end($toc)->item->addChild(
|
||||
$id,
|
||||
array(
|
||||
'url' => Url::fromPath(
|
||||
'doc/module/view',
|
||||
array(
|
||||
'name' => $this->module
|
||||
)
|
||||
)->setAnchor($id)->getRelativeUrl(),
|
||||
'title' => htmlspecialchars($header),
|
||||
'priority' => $itemPriority++,
|
||||
'attribs' => $attribs
|
||||
)
|
||||
);
|
||||
$toc[] = ((object) array(
|
||||
'level' => $level,
|
||||
'item' => $item
|
||||
));
|
||||
$line = '<a name="' . $id . '"></a>' . PHP_EOL . $line;
|
||||
}
|
||||
$cat[] = $line;
|
||||
}
|
||||
$fileObject->flock(LOCK_UN);
|
||||
if (! is_dir($path)) {
|
||||
throw new DocException(
|
||||
sprintf(mt('doc', 'Documentation directory \'%s\' does not exist'), $path)
|
||||
);
|
||||
}
|
||||
$html = Parsedown::instance()->parse(implode('', $cat));
|
||||
$html = preg_replace_callback(
|
||||
'#<pre><code class="language-php">(.*?)\</code></pre>#s',
|
||||
array($this, 'highlight'),
|
||||
$html
|
||||
);
|
||||
return array($html, $toc[0]->item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax highlighting for PHP code
|
||||
*
|
||||
* @param $match
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function highlight($match)
|
||||
{
|
||||
return highlight_string(htmlspecialchars_decode($match[1]), true);
|
||||
if (! is_readable($path)) {
|
||||
throw new DocException(
|
||||
sprintf(mt('doc', 'Documentation directory \'%s\' is not readable'), $path)
|
||||
);
|
||||
}
|
||||
$docIterator = new DocIterator($path);
|
||||
if ($docIterator->count() === 0) {
|
||||
throw new DocEmptyException(
|
||||
sprintf(
|
||||
mt(
|
||||
'doc',
|
||||
'Documentation directory \'%s\' does not contain any non-empty Markdown file (\'.md\' suffix)'
|
||||
),
|
||||
$path
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->path = $path;
|
||||
$this->docIterator = $docIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,28 +75,28 @@ class DocParser
|
||||
*/
|
||||
protected function extractHeader($line, $lastLine)
|
||||
{
|
||||
if (!$line) {
|
||||
if (! $line) {
|
||||
return null;
|
||||
}
|
||||
$header = null;
|
||||
if ($line &&
|
||||
$line[0] === '#' &&
|
||||
preg_match('/^#+/', $line, $match) === 1
|
||||
if ($line
|
||||
&& $line[0] === '#'
|
||||
&& preg_match('/^#+/', $line, $match) === 1
|
||||
) {
|
||||
// Atx-style
|
||||
// Atx
|
||||
$level = strlen($match[0]);
|
||||
$header = trim(substr($line, $level));
|
||||
if (!$header) {
|
||||
if (! $header) {
|
||||
return null;
|
||||
}
|
||||
} elseif (
|
||||
$line &&
|
||||
($line[0] === '=' || $line[0] === '-') &&
|
||||
preg_match('/^[=-]+\s*$/', $line, $match) === 1
|
||||
$line
|
||||
&& ($line[0] === '=' || $line[0] === '-')
|
||||
&& preg_match('/^[=-]+\s*$/', $line, $match) === 1
|
||||
) {
|
||||
// Setext
|
||||
$header = trim($lastLine);
|
||||
if (!$header) {
|
||||
if (! $header) {
|
||||
return null;
|
||||
}
|
||||
if ($match[0][0] === '=') {
|
||||
@ -189,36 +108,67 @@ class DocParser
|
||||
if ($header === null) {
|
||||
return null;
|
||||
}
|
||||
return array($header, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract header id in an a or a span tag
|
||||
*
|
||||
* @param string &$header
|
||||
*
|
||||
* @return id|null
|
||||
*/
|
||||
protected function extractHeaderId(&$header)
|
||||
{
|
||||
if ($header[0] === '<' &&
|
||||
preg_match('#(?:<(?P<tag>a|span) id="(?P<id>.+)"></(?P=tag)>)#u', $header, $match)
|
||||
if ($header[0] === '<'
|
||||
&& preg_match('#(?:<(?P<tag>a|span) (?:id|name)="(?P<id>.+)"></(?P=tag)>)\s*#u', $header, $match)
|
||||
) {
|
||||
$header = str_replace($match[0], '', $header);
|
||||
return $match['id'];
|
||||
$id = $match['id'];
|
||||
} else {
|
||||
$id = null;
|
||||
}
|
||||
return null;
|
||||
return array($header, $id, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the toc to the given level
|
||||
* Get the documentation tree
|
||||
*
|
||||
* @param array &$toc
|
||||
* @param int $level
|
||||
* @return DocTree
|
||||
*/
|
||||
protected function reduceToc(array &$toc, $level) {
|
||||
while (end($toc)->level >= $level) {
|
||||
array_pop($toc);
|
||||
public function getDocTree()
|
||||
{
|
||||
$tree = new DocTree();
|
||||
$stack = new SplDoublyLinkedList();
|
||||
foreach ($this->docIterator as $fileInfo) {
|
||||
/* @var $file \SplFileInfo */
|
||||
$file = $fileInfo->openFile();
|
||||
/* @var $file \SplFileObject */
|
||||
$lastLine = null;
|
||||
foreach ($file as $line) {
|
||||
$header = $this->extractHeader($line, $lastLine);
|
||||
if ($header !== null) {
|
||||
list($title, $id, $level) = $header;
|
||||
while (! $stack->isEmpty() && $stack->top()->getLevel() >= $level) {
|
||||
$stack->pop();
|
||||
}
|
||||
if ($id === null) {
|
||||
$path = array();
|
||||
foreach ($stack as $section) {
|
||||
/* @var $section Section */
|
||||
$path[] = $section->getTitle();
|
||||
}
|
||||
$path[] = $title;
|
||||
$id = implode('-', $path);
|
||||
$noFollow = true;
|
||||
} else {
|
||||
$noFollow = false;
|
||||
}
|
||||
if ($stack->isEmpty()) {
|
||||
$chapterId = $id;
|
||||
$section = new Section($id, $title, $level, $noFollow, $chapterId);
|
||||
$tree->addRoot($section);
|
||||
} else {
|
||||
$chapterId = $stack->bottom()->getId();
|
||||
$section = new Section($id, $title, $level, $noFollow, $chapterId);
|
||||
$tree->addChild($section, $stack->top());
|
||||
}
|
||||
$stack->push($section);
|
||||
} else {
|
||||
$stack->top()->appendContent($line);
|
||||
}
|
||||
// Save last line for setext-style headers
|
||||
$lastLine = $line;
|
||||
}
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
}
|
||||
|
80
modules/doc/library/Doc/DocTree.php
Normal file
80
modules/doc/library/Doc/DocTree.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use LogicException;
|
||||
use Icinga\Data\Identifiable;
|
||||
use Icinga\Data\Tree\Node;
|
||||
|
||||
/**
|
||||
* Documentation tree
|
||||
*/
|
||||
class DocTree extends Node
|
||||
{
|
||||
/**
|
||||
* All nodes of the tree
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes = array();
|
||||
|
||||
/**
|
||||
* Append a root node to the tree
|
||||
*
|
||||
* @param Identifiable $root
|
||||
*/
|
||||
public function addRoot(Identifiable $root)
|
||||
{
|
||||
$rootId = $root->getId();
|
||||
if (isset($this->nodes[$rootId])) {
|
||||
$rootId = uniqid($rootId);
|
||||
// throw new LogicException(
|
||||
// sprintf('Can\'t add root node: a root node with the id \'%s\' already exists', $rootId)
|
||||
// );
|
||||
}
|
||||
$this->nodes[$rootId] = $this->appendChild($root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a child node to a parent node
|
||||
*
|
||||
* @param Identifiable $child
|
||||
* @param Identifiable $parent
|
||||
*
|
||||
* @throws LogicException If the the tree does not contain the parent node
|
||||
*/
|
||||
public function addChild(Identifiable $child, Identifiable $parent)
|
||||
{
|
||||
$childId = $child->getId();
|
||||
$parentId = $parent->getId();
|
||||
if (isset($this->nodes[$childId])) {
|
||||
$childId = uniqid($childId);
|
||||
// throw new LogicException(
|
||||
// sprintf('Can\'t add child node: a child node with the id \'%s\' already exists', $childId)
|
||||
// );
|
||||
}
|
||||
if (! isset($this->nodes[$parentId])) {
|
||||
throw new LogicException(
|
||||
sprintf(mt('doc', 'Can\'t add child node: there\'s no parent node having the id \'%s\''), $parentId)
|
||||
);
|
||||
}
|
||||
$this->nodes[$childId] = $this->nodes[$parentId]->appendChild($child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a node
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return Node|null
|
||||
*/
|
||||
public function getNode($id)
|
||||
{
|
||||
if (! isset($this->nodes[$id])) {
|
||||
return null;
|
||||
}
|
||||
return $this->nodes[$id];
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown if a chapter was not found
|
||||
*/
|
||||
class ChapterNotFoundException extends DocException {}
|
10
modules/doc/library/Doc/Exception/DocEmptyException.php
Normal file
10
modules/doc/library/Doc/Exception/DocEmptyException.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown if a documentation directory is empty
|
||||
*/
|
||||
class DocEmptyException extends DocException {}
|
12
modules/doc/library/Doc/Exception/DocException.php
Normal file
12
modules/doc/library/Doc/Exception/DocException.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown if an error in the documentation module's library occurs
|
||||
*/
|
||||
class DocException extends RuntimeException {}
|
@ -4,15 +4,15 @@
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use \RecursiveFilterIterator;
|
||||
use RecursiveFilterIterator;
|
||||
|
||||
/**
|
||||
* Iterator over Markdown files recursively
|
||||
* Recursive iterator over Markdown files
|
||||
*/
|
||||
class MarkdownFileIterator extends RecursiveFilterIterator
|
||||
{
|
||||
/**
|
||||
* Accept files with .md suffix
|
||||
* Accept files with '.md' suffix
|
||||
*
|
||||
* @return bool Whether the current element of the iterator is acceptable
|
||||
* through this filter
|
||||
@ -20,7 +20,8 @@ class MarkdownFileIterator extends RecursiveFilterIterator
|
||||
public function accept()
|
||||
{
|
||||
$current = $this->getInnerIterator()->current();
|
||||
if (!$current->isFile()) {
|
||||
/* @var $current \SplFileInfo */
|
||||
if (! $current->isFile()) {
|
||||
return false;
|
||||
}
|
||||
$filename = $current->getFilename();
|
||||
|
31
modules/doc/library/Doc/NonEmptyFileIterator.php
Normal file
31
modules/doc/library/Doc/NonEmptyFileIterator.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use RecursiveFilterIterator;
|
||||
|
||||
/**
|
||||
* Recursive iterator over non-empty files
|
||||
*/
|
||||
class NonEmptyFileIterator extends RecursiveFilterIterator
|
||||
{
|
||||
/**
|
||||
* Accept non-empty files
|
||||
*
|
||||
* @return bool Whether the current element of the iterator is acceptable
|
||||
* through this filter
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
$current = $this->getInnerIterator()->current();
|
||||
/* @var $current \SplFileInfo */
|
||||
if (! $current->isFile()
|
||||
|| $current->getSize() === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
75
modules/doc/library/Doc/Renderer.php
Normal file
75
modules/doc/library/Doc/Renderer.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use RecursiveIteratorIterator;
|
||||
use Zend_View_Helper_Url;
|
||||
use Icinga\Web\View;
|
||||
|
||||
/**
|
||||
* Base class for toc and section renderer
|
||||
*/
|
||||
abstract class Renderer extends RecursiveIteratorIterator
|
||||
{
|
||||
/**
|
||||
* Encode an anchor identifier
|
||||
*
|
||||
* @param string $anchor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encodeAnchor($anchor)
|
||||
{
|
||||
return rawurlencode($anchor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an anchor identifier
|
||||
*
|
||||
* @param string $anchor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function decodeAnchor($anchor)
|
||||
{
|
||||
return rawurldecode($anchor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a URL parameter
|
||||
*
|
||||
* @param string $param
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function encodeUrlParam($param)
|
||||
{
|
||||
return str_replace(array('%2F','%5C'), array('%252F','%255C'), rawurlencode($param));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a URL parameter
|
||||
*
|
||||
* @param string $param
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function decodeUrlParam($param)
|
||||
{
|
||||
return str_replace(array('%2F', '%5C'), array('/', '\\'), $param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render to HTML
|
||||
*
|
||||
* Meant to be overwritten by concrete classes.
|
||||
*
|
||||
* @param View $view
|
||||
* @param Zend_View_Helper_Url $zendUrlHelper
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function render(View $view, Zend_View_Helper_Url $zendUrlHelper);
|
||||
}
|
143
modules/doc/library/Doc/Section.php
Normal file
143
modules/doc/library/Doc/Section.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use Icinga\Data\Identifiable;
|
||||
|
||||
/**
|
||||
* A section of a documentation
|
||||
*/
|
||||
class Section implements Identifiable
|
||||
{
|
||||
/**
|
||||
* The ID of the section
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The title of the section
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The header level
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $level;
|
||||
|
||||
/**
|
||||
* Whether to instruct search engines to not index the link to the section
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $noFollow;
|
||||
|
||||
/**
|
||||
* The ID of the chapter the section is part of
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $chapterId;
|
||||
|
||||
/**
|
||||
* The content of the section
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $content = array();
|
||||
|
||||
/**
|
||||
* Create a new section
|
||||
*
|
||||
* @param string $id The ID of the section
|
||||
* @param string $title The title of the section
|
||||
* @param int $level The header level
|
||||
* @param bool $noFollow Whether to instruct search engines to not index the link to the section
|
||||
* @param string $chapterId The ID of the chapter the section is part of
|
||||
*/
|
||||
public function __construct($id, $title, $level, $noFollow, $chapterId)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
$this->level = $level;
|
||||
$this->noFollow = $noFollow;
|
||||
$this->chapterId= $chapterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of the section
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the section
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header level
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel()
|
||||
{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to instruct search engines to not index the link to the section
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNoFollow()
|
||||
{
|
||||
return $this->noFollow;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the chapter the section is part of
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getChapterId()
|
||||
{
|
||||
return $this->chapterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append content
|
||||
*
|
||||
* @param string $content
|
||||
*/
|
||||
public function appendContent($content)
|
||||
{
|
||||
$this->content[] = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of the section
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
68
modules/doc/library/Doc/SectionFilterIterator.php
Normal file
68
modules/doc/library/Doc/SectionFilterIterator.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use Countable;
|
||||
use RecursiveFilterIterator;
|
||||
use Icinga\Data\Tree\NodeInterface;
|
||||
|
||||
/**
|
||||
* Recursive iterator over sections that are part of a particular chapter
|
||||
*/
|
||||
class SectionFilterIterator extends RecursiveFilterIterator implements Countable
|
||||
{
|
||||
/**
|
||||
* The chapter ID to filter for
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $chapterId;
|
||||
|
||||
/**
|
||||
* Create a new SectionFilterIterator
|
||||
*
|
||||
* @param NodeInterface $node Node
|
||||
* @param string $chapterId The chapter ID to filter for
|
||||
*/
|
||||
public function __construct(NodeInterface $node, $chapterId)
|
||||
{
|
||||
parent::__construct($node);
|
||||
$this->chapterId = $chapterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept sections that are part of the given chapter
|
||||
*
|
||||
* @return bool Whether the current element of the iterator is acceptable
|
||||
* through this filter
|
||||
*/
|
||||
public function accept()
|
||||
{
|
||||
$section = $this->getInnerIterator()->current()->getValue();
|
||||
/* @var $section \Icinga\Module\Doc\Section */
|
||||
if ($section->getChapterId() === $this->chapterId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see RecursiveFilterIterator::getChildren()
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return new static($this->getInnerIterator()->getChildren(), $this->chapterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPDoc)
|
||||
* @see Countable::count()
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return iterator_count($this);
|
||||
}
|
||||
}
|
292
modules/doc/library/Doc/SectionRenderer.php
Normal file
292
modules/doc/library/Doc/SectionRenderer.php
Normal file
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
require_once 'IcingaVendor/Parsedown/Parsedown.php';
|
||||
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use RecursiveIteratorIterator;
|
||||
use Parsedown;
|
||||
use Zend_View_Helper_Url;
|
||||
use Icinga\Module\Doc\Exception\ChapterNotFoundException;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\View;
|
||||
|
||||
/**
|
||||
* preg_replace_callback helper to replace links
|
||||
*/
|
||||
class Callback
|
||||
{
|
||||
protected $docTree;
|
||||
|
||||
protected $view;
|
||||
|
||||
protected $zendUrlHelper;
|
||||
|
||||
protected $url;
|
||||
|
||||
protected $urlParams;
|
||||
|
||||
public function __construct(
|
||||
DocTree $docTree,
|
||||
View $view,
|
||||
Zend_View_Helper_Url $zendUrlHelper,
|
||||
$url,
|
||||
array $urlParams)
|
||||
{
|
||||
$this->docTree = $docTree;
|
||||
$this->view = $view;
|
||||
$this->zendUrlHelper = $zendUrlHelper;
|
||||
$this->url = $url;
|
||||
$this->urlParams = $urlParams;
|
||||
}
|
||||
|
||||
public function render($match)
|
||||
{
|
||||
$node = $this->docTree->getNode(Renderer::decodeAnchor($match['fragment']));
|
||||
/* @var $node \Icinga\Data\Tree\Node */
|
||||
if ($node === null) {
|
||||
return $match[0];
|
||||
}
|
||||
$section = $node->getValue();
|
||||
/* @var $section \Icinga\Module\Doc\Section */
|
||||
$path = $this->zendUrlHelper->url(
|
||||
array_merge(
|
||||
$this->urlParams,
|
||||
array(
|
||||
'chapterId' => SectionRenderer::encodeUrlParam($section->getChapterId())
|
||||
)
|
||||
),
|
||||
$this->url,
|
||||
false,
|
||||
false
|
||||
);
|
||||
$url = $this->view->url($path);
|
||||
$url->setAnchor(SectionRenderer::encodeAnchor($section->getId()));
|
||||
return sprintf(
|
||||
'<a %s%shref="%s"',
|
||||
strlen($match['attribs']) ? trim($match['attribs']) . ' ' : '',
|
||||
$section->isNoFollow() ? 'rel="nofollow" ' : '',
|
||||
$url->getAbsoluteUrl()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Section renderer
|
||||
*/
|
||||
class SectionRenderer extends Renderer
|
||||
{
|
||||
/**
|
||||
* The documentation tree
|
||||
*
|
||||
* @var DocTree
|
||||
*/
|
||||
protected $docTree;
|
||||
|
||||
protected $tocUrl;
|
||||
|
||||
/**
|
||||
* The URL to replace links with
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Additional URL parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urlParams;
|
||||
|
||||
/**
|
||||
* Parsedown instance
|
||||
*
|
||||
* @var Parsedown
|
||||
*/
|
||||
protected $parsedown;
|
||||
|
||||
/**
|
||||
* Content
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $content = array();
|
||||
|
||||
/**
|
||||
* Create a new section renderer
|
||||
*
|
||||
* @param DocTree $docTree The documentation tree
|
||||
* @param string|null $chapterId If not null, the chapter ID to filter for
|
||||
* @param string $tocUrl
|
||||
* @param string $url The URL to replace links with
|
||||
* @param array $urlParams Additional URL parameters
|
||||
*
|
||||
* @throws ChapterNotFoundException If the chapter to filter for was not found
|
||||
*/
|
||||
public function __construct(DocTree $docTree, $chapterId, $tocUrl, $url, array $urlParams)
|
||||
{
|
||||
if ($chapterId !== null) {
|
||||
$filter = new SectionFilterIterator($docTree, $chapterId);
|
||||
if ($filter->count() === 0) {
|
||||
throw new ChapterNotFoundException(
|
||||
sprintf(mt('doc', 'Chapter \'%s\' not found'), $chapterId)
|
||||
);
|
||||
}
|
||||
parent::__construct(
|
||||
$filter,
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
} else {
|
||||
parent::__construct($docTree, RecursiveIteratorIterator::SELF_FIRST);
|
||||
}
|
||||
$this->docTree = $docTree;
|
||||
$this->tocUrl = $tocUrl;
|
||||
$this->url = $url;
|
||||
$this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams);
|
||||
$this->parsedown = Parsedown::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax highlighting for PHP code
|
||||
*
|
||||
* @param $match
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function highlightPhp($match)
|
||||
{
|
||||
return '<pre>' . highlight_string(htmlspecialchars_decode($match[1]), true) . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace img src tags
|
||||
*
|
||||
* @param $match
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceImg($match)
|
||||
{
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML($match[0]);
|
||||
$xpath = new DOMXPath($doc);
|
||||
$img = $xpath->query('//img[1]')->item(0);
|
||||
/* @var $img \DOMElement */
|
||||
$img->setAttribute('src', Url::fromPath($img->getAttribute('src'))->getAbsoluteUrl());
|
||||
return substr_replace($doc->saveXML($img), '', -2, 1); // Replace '/>' with '>'
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the section
|
||||
*
|
||||
* @param View $view
|
||||
* @param Zend_View_Helper_Url $zendUrlHelper
|
||||
* @param bool $renderNavigation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(View $view, Zend_View_Helper_Url $zendUrlHelper, $renderNavigation = true)
|
||||
{
|
||||
$callback = new Callback($this->docTree, $view, $zendUrlHelper, $this->url, $this->urlParams);
|
||||
$content = array();
|
||||
foreach ($this as $node) {
|
||||
$section = $node->getValue();
|
||||
/* @var $section \Icinga\Module\Doc\Section */
|
||||
$content[] = sprintf(
|
||||
'<a name="%1$s"></a><h%2$d>%3$s</h%2$d>',
|
||||
Renderer::encodeAnchor($section->getId()),
|
||||
$section->getLevel(),
|
||||
$view->escape($section->getTitle())
|
||||
);
|
||||
$html = preg_replace_callback(
|
||||
'#<pre><code class="language-php">(.*?)</code></pre>#s',
|
||||
array($this, 'highlightPhp'),
|
||||
$this->parsedown->text(implode('', $section->getContent()))
|
||||
);
|
||||
$html = preg_replace_callback(
|
||||
'/<img[^>]+>/',
|
||||
array($this, 'replaceImg'),
|
||||
$html
|
||||
);
|
||||
$content[] = preg_replace_callback(
|
||||
'/<a\s+(?P<attribs>[^>]*?\s+)?href="#(?P<fragment>[^"]+)"/',
|
||||
array($callback, 'render'),
|
||||
$html
|
||||
);
|
||||
}
|
||||
if ($renderNavigation) {
|
||||
foreach ($this->docTree as $chapter) {
|
||||
if ($chapter->getValue()->getId() === $section->getChapterId()) {
|
||||
$navigation = array('<ul class="navigation">');
|
||||
$this->docTree->prev();
|
||||
$prev = $this->docTree->current();
|
||||
if ($prev !== null) {
|
||||
$prev = $prev->getValue();
|
||||
$path = $zendUrlHelper->url(
|
||||
array_merge(
|
||||
$this->urlParams,
|
||||
array(
|
||||
'chapterId' => $this->encodeUrlParam($prev->getChapterId())
|
||||
)
|
||||
),
|
||||
$this->url,
|
||||
false,
|
||||
false
|
||||
);
|
||||
$url = $view->url($path);
|
||||
$url->setAnchor($this->encodeAnchor($prev->getId()));
|
||||
$navigation[] = sprintf(
|
||||
'<li class="prev"><a %shref="%s">%s</a></li>',
|
||||
$prev->isNoFollow() ? 'rel="nofollow" ' : '',
|
||||
$url->getAbsoluteUrl(),
|
||||
$view->escape($prev->getTitle())
|
||||
);
|
||||
$this->docTree->next();
|
||||
$this->docTree->next();
|
||||
} else {
|
||||
$this->docTree->rewind();
|
||||
$this->docTree->next();
|
||||
}
|
||||
$url = $view->url($this->tocUrl);
|
||||
$navigation[] = sprintf(
|
||||
'<li><a href="%s">%s</a></li>',
|
||||
$url->getAbsoluteUrl(),
|
||||
mt('doc', 'Index')
|
||||
);
|
||||
$next = $this->docTree->current();
|
||||
if ($next !== null) {
|
||||
$next = $next->getValue();
|
||||
$path = $zendUrlHelper->url(
|
||||
array_merge(
|
||||
$this->urlParams,
|
||||
array(
|
||||
'chapterId' => $this->encodeUrlParam($next->getChapterId())
|
||||
)
|
||||
),
|
||||
$this->url,
|
||||
false,
|
||||
false
|
||||
);
|
||||
$url = $view->url($path);
|
||||
$url->setAnchor($this->encodeAnchor($next->getId()));
|
||||
$navigation[] = sprintf(
|
||||
'<li class="next"><a %shref="%s">%s</a></li>',
|
||||
$next->isNoFollow() ? 'rel="nofollow" ' : '',
|
||||
$url->getAbsoluteUrl(),
|
||||
$view->escape($next->getTitle())
|
||||
);
|
||||
}
|
||||
$navigation[] = '</ul>';
|
||||
$content = array_merge($navigation, $content, $navigation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode("\n", $content);
|
||||
}
|
||||
}
|
109
modules/doc/library/Doc/TocRenderer.php
Normal file
109
modules/doc/library/Doc/TocRenderer.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}
|
||||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use RecursiveIteratorIterator;
|
||||
use Zend_View_Helper_Url;
|
||||
use Icinga\Web\View;
|
||||
|
||||
/**
|
||||
* TOC renderer
|
||||
*/
|
||||
class TocRenderer extends Renderer
|
||||
{
|
||||
/**
|
||||
* The URL to replace links with
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Additional URL parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urlParams;
|
||||
|
||||
/**
|
||||
* Content
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $content = array();
|
||||
|
||||
/**
|
||||
* Create a new toc renderer
|
||||
*
|
||||
* @param DocTree $docTree The documentation tree
|
||||
* @param string $url The URL to replace links with
|
||||
* @param array $urlParams Additional URL parameters
|
||||
*/
|
||||
public function __construct(DocTree $docTree, $url, array $urlParams)
|
||||
{
|
||||
parent::__construct($docTree, RecursiveIteratorIterator::SELF_FIRST);
|
||||
$this->url = $url;
|
||||
$this->urlParams = array_map(array($this, 'encodeUrlParam'), $urlParams);
|
||||
}
|
||||
|
||||
public function beginIteration()
|
||||
{
|
||||
$this->content[] = '<nav><ul>';
|
||||
}
|
||||
|
||||
public function endIteration()
|
||||
{
|
||||
$this->content[] = '</ul></nav>';
|
||||
}
|
||||
|
||||
public function beginChildren()
|
||||
{
|
||||
$this->content[] = '<ul>';
|
||||
}
|
||||
|
||||
public function endChildren()
|
||||
{
|
||||
$this->content[] = '</ul></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the toc
|
||||
*
|
||||
* @param View $view
|
||||
* @param Zend_View_Helper_Url $zendUrlHelper
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(View $view, Zend_View_Helper_Url $zendUrlHelper)
|
||||
{
|
||||
foreach ($this as $node) {
|
||||
$section = $node->getValue();
|
||||
/* @var $section \Icinga\Module\Doc\Section */
|
||||
$path = $zendUrlHelper->url(
|
||||
array_merge(
|
||||
$this->urlParams,
|
||||
array(
|
||||
'chapterId' => $this->encodeUrlParam($section->getChapterId())
|
||||
)
|
||||
),
|
||||
$this->url,
|
||||
false,
|
||||
false
|
||||
);
|
||||
$url = $view->url($path);
|
||||
$url->setAnchor($this->encodeAnchor($section->getId()));
|
||||
$this->content[] = sprintf(
|
||||
'<li><a %shref="%s">%s</a>',
|
||||
$section->isNoFollow() ? 'rel="nofollow" ' : '',
|
||||
$url->getAbsoluteUrl(),
|
||||
$view->escape($section->getTitle())
|
||||
);
|
||||
if (! $this->getInnerIterator()->current()->hasChildren()) {
|
||||
$this->content[] = '</li>';
|
||||
}
|
||||
}
|
||||
return implode("\n", $this->content);
|
||||
}
|
||||
}
|
62
modules/doc/public/css/module.less
Normal file
62
modules/doc/public/css/module.less
Normal file
@ -0,0 +1,62 @@
|
||||
// W3C Recommendation <http://www.w3.org/TR/CSS21/sample.html> (except h4)
|
||||
h1 { font-size: 2em !important; }
|
||||
h2 { font-size: 1.5em !important; }
|
||||
h3 { font-size: 1.17em !important; }
|
||||
h4 { font-size: 1em !important; }
|
||||
h5 { font-size: .83em !important; }
|
||||
h6 { font-size: .75em !important; }
|
||||
|
||||
div.chapter {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
border: solid 1px lightgray;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
code {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
padding: 0.2em;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.chapter > ul.navigation {
|
||||
margin: 0;
|
||||
padding: 0.4em;
|
||||
text-align: center;
|
||||
background-color: #888;
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
display: inline;
|
||||
margin: 0.2em;
|
||||
padding: 0;
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.prev {
|
||||
padding-right: 0.6em;
|
||||
border-right: 2px solid #fff;
|
||||
}
|
||||
|
||||
&.next {
|
||||
padding-left: 0.6em;
|
||||
border-left: 2px solid #fff;
|
||||
}
|
||||
}
|
||||
}
|
50
modules/doc/run.php
Normal file
50
modules/doc/run.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use \Zend_Controller_Router_Route;
|
||||
use Icinga\Application\Icinga;
|
||||
|
||||
if (Icinga::app()->isCli()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$docModuleChapter = new Zend_Controller_Router_Route(
|
||||
'doc/module/:moduleName/chapter/:chapterId',
|
||||
array(
|
||||
'controller' => 'module',
|
||||
'action' => 'chapter',
|
||||
'module' => 'doc'
|
||||
)
|
||||
);
|
||||
|
||||
$docIcingaWebChapter = new Zend_Controller_Router_Route(
|
||||
'doc/icingaweb/chapter/:chapterId',
|
||||
array(
|
||||
'controller' => 'icingaweb',
|
||||
'action' => 'chapter',
|
||||
'module' => 'doc'
|
||||
)
|
||||
);
|
||||
|
||||
$docModuleToc = new Zend_Controller_Router_Route(
|
||||
'doc/module/:moduleName/toc',
|
||||
array(
|
||||
'controller' => 'module',
|
||||
'action' => 'toc',
|
||||
'module' => 'doc'
|
||||
)
|
||||
);
|
||||
|
||||
$docModulePdf = new Zend_Controller_Router_Route(
|
||||
'doc/module/:moduleName/pdf',
|
||||
array(
|
||||
'controller' => 'module',
|
||||
'action' => 'pdf',
|
||||
'module' => 'doc'
|
||||
)
|
||||
);
|
||||
|
||||
$this->addRoute('doc/module/chapter', $docModuleChapter);
|
||||
$this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter);
|
||||
$this->addRoute('doc/module/toc', $docModuleToc);
|
||||
$this->addRoute('doc/module/pdf', $docModulePdf);
|
||||
|
@ -72,6 +72,7 @@ class ListCommand extends Command
|
||||
|
||||
protected function showFormatted($query, $format, $columns)
|
||||
{
|
||||
$query = $query->getQuery();
|
||||
switch($format) {
|
||||
case 'json':
|
||||
echo json_encode($query->fetchAll());
|
||||
@ -155,7 +156,7 @@ class ListCommand extends Command
|
||||
'service_perfdata',
|
||||
'service_last_state_change'
|
||||
);
|
||||
$query = $this->getQuery('status', $columns)
|
||||
$query = $this->getQuery('serviceStatus', $columns)
|
||||
->order('host_name');
|
||||
echo $this->renderStatusQuery($query);
|
||||
}
|
||||
@ -167,6 +168,7 @@ class ListCommand extends Command
|
||||
$screen = $this->screen;
|
||||
$utils = new CliUtils($screen);
|
||||
$maxCols = $screen->getColumns();
|
||||
$query = $query->getQuery();
|
||||
$rows = $query->fetchAll();
|
||||
$count = $query->count();
|
||||
$count = count($rows);
|
||||
|
@ -87,8 +87,8 @@ class Monitoring_ChartController extends Controller
|
||||
'services_pending'
|
||||
)
|
||||
)->getQuery()->fetchAll();
|
||||
$this->view->height = intval($this->getParam('height', 220));
|
||||
$this->view->width = intval($this->getParam('width', 520));
|
||||
$this->view->height = intval($this->getParam('height', 500));
|
||||
$this->view->width = intval($this->getParam('width', 500));
|
||||
if (count($query) === 1) {
|
||||
$this->drawGroupPie($query[0]);
|
||||
} else {
|
||||
@ -112,8 +112,8 @@ class Monitoring_ChartController extends Controller
|
||||
'services_pending'
|
||||
)
|
||||
)->getQuery()->fetchAll();
|
||||
$this->view->height = intval($this->getParam('height', 220));
|
||||
$this->view->width = intval($this->getParam('width', 520));
|
||||
$this->view->height = intval($this->getParam('height', 500));
|
||||
$this->view->width = intval($this->getParam('width', 500));
|
||||
|
||||
$this->drawServiceGroupChart($query);
|
||||
|
||||
@ -133,7 +133,8 @@ class Monitoring_ChartController extends Controller
|
||||
}
|
||||
$this->view->chart = new GridChart();
|
||||
$this->view->chart->setAxisLabel('', t('Services'))
|
||||
->setXAxis(new StaticAxis());
|
||||
->setXAxis(new StaticAxis())
|
||||
->setAxisMin(null, 0);
|
||||
|
||||
$this->view->chart->drawBars(
|
||||
array(
|
||||
@ -183,7 +184,9 @@ class Monitoring_ChartController extends Controller
|
||||
);
|
||||
}
|
||||
$this->view->chart = new GridChart();
|
||||
$this->view->chart->setAxisLabel('', t('Hosts'))->setXAxis(new StaticAxis());
|
||||
$this->view->chart->setAxisLabel('', t('Hosts'))
|
||||
->setXAxis(new StaticAxis())
|
||||
->setAxisMin(null, 0);
|
||||
$this->view->chart->drawBars(
|
||||
array(
|
||||
'label' => t('Up'),
|
||||
|
@ -94,7 +94,6 @@ class Monitoring_ListController extends Controller
|
||||
'host_last_check',
|
||||
'host_last_state_change' => $stateChangeColumn,
|
||||
'host_notifications_enabled',
|
||||
// 'host_unhandled_service_count',
|
||||
'host_unhandled_services',
|
||||
'host_action_url',
|
||||
'host_notes_url',
|
||||
@ -222,6 +221,7 @@ class Monitoring_ListController extends Controller
|
||||
'author' => 'downtime_author',
|
||||
'start' => 'downtime_start',
|
||||
'scheduled_start' => 'downtime_scheduled_start',
|
||||
'scheduled_end' => 'downtime_scheduled_end',
|
||||
'end' => 'downtime_end',
|
||||
'duration' => 'downtime_duration',
|
||||
'is_flexible' => 'downtime_is_flexible',
|
||||
@ -229,7 +229,9 @@ class Monitoring_ListController extends Controller
|
||||
'is_in_effect' => 'downtime_is_in_effect',
|
||||
'entry_time' => 'downtime_entry_time',
|
||||
'host' => 'downtime_host',
|
||||
'service' => 'downtime_service'
|
||||
'service' => 'downtime_service',
|
||||
'host_state' => 'downtime_host_state',
|
||||
'service_state' => 'downtime_service_state'
|
||||
))->order('downtime_is_in_effect', 'DESC')
|
||||
->order('downtime_scheduled_start', 'DESC');
|
||||
|
||||
|
@ -64,7 +64,7 @@ class Zend_View_Helper_MonitoringCommands extends Zend_View_Helper_Abstract
|
||||
|
||||
$out .= '</div>';
|
||||
|
||||
$out .= '<div class="clearfix"></div>';
|
||||
$out .= '<div style="clear: both;"></div>';
|
||||
|
||||
if ($type === Meta::TYPE_FULL) {
|
||||
return '<div>'. $out. '</div>';
|
||||
|
@ -21,7 +21,6 @@ class Monitoring_MultiController extends Controller
|
||||
array(
|
||||
'host_name',
|
||||
'host_in_downtime',
|
||||
'host_unhandled_service_count',
|
||||
'host_passive_checks_enabled',
|
||||
'host_obsessing',
|
||||
'host_state',
|
||||
|
@ -103,6 +103,40 @@ class Monitoring_ShowController extends Controller
|
||||
));
|
||||
}
|
||||
|
||||
public function contactAction()
|
||||
{
|
||||
$contact = $this->getParam('contact');
|
||||
if (! $contact) {
|
||||
throw new Zend_Controller_Action_Exception(
|
||||
$this->translate('The parameter `contact\' is required'),
|
||||
404
|
||||
);
|
||||
}
|
||||
$query = $this->backend->select()->from('contact', array(
|
||||
'contact_name',
|
||||
'contact_id',
|
||||
'contact_alias',
|
||||
'contact_email',
|
||||
'contact_pager',
|
||||
'contact_notify_service_timeperiod',
|
||||
'contact_notify_service_recovery',
|
||||
'contact_notify_service_warning',
|
||||
'contact_notify_service_critical',
|
||||
'contact_notify_service_unknown',
|
||||
'contact_notify_service_flapping',
|
||||
'contact_notify_service_downtime',
|
||||
'contact_notify_host_timeperiod',
|
||||
'contact_notify_host_recovery',
|
||||
'contact_notify_host_down',
|
||||
'contact_notify_host_unreachable',
|
||||
'contact_notify_host_flapping',
|
||||
'contact_notify_host_downtime',
|
||||
));
|
||||
$query->where('contact_name', $contact);
|
||||
$this->view->contacts = $query->paginate();
|
||||
$this->view->contact_name = $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating tabs for this controller
|
||||
* @return Tabs
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
<div style="width:<?= $this->width; ?>px;height:<?= $this->height; ?>px;margin:auto;">
|
||||
<div style="max-height:548px; margin:auto;">
|
||||
<?=
|
||||
$chart->render();
|
||||
?>
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
<div style="width:<?= $this->width; ?>px;height:<?= $this->height; ?>px">
|
||||
<div style="max-height:548px; margin:auto;">
|
||||
<?=
|
||||
$chart->render();
|
||||
?>
|
||||
|
@ -1,98 +1,114 @@
|
||||
<?php
|
||||
|
||||
$helper = $this->getHelper('CommandForm');
|
||||
|
||||
?>
|
||||
|
||||
<?php if (false === $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<div style="margin: 1em">
|
||||
<?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->paginationControl($comments, null, null, array('preserve' => $this->preserve)); ?>
|
||||
<?= $this->tabs->render($this); ?>
|
||||
<div style="margin: 1em" class="dontprint">
|
||||
<?= $this->translate('Sort by'); ?> <?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->widget('limiter', array('url' => $this->url, 'max' => $comments->count())); ?>
|
||||
<?= $this->paginationControl($comments, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="content">
|
||||
<table data-base-target="_next" class="action comments">
|
||||
<tbody>
|
||||
<?php
|
||||
<?php if (empty($comments)): ?>
|
||||
<?= $this->translate('No comments matching the filter') ?>
|
||||
</div>
|
||||
<?php return; endif ?>
|
||||
|
||||
$cf = $this->getHelper('CommandForm');
|
||||
|
||||
if (count($comments) === 0) {
|
||||
echo t('No comments matching the filter');
|
||||
}
|
||||
|
||||
foreach ($comments as $comment):
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td style="width: 5em; text-align: center;">
|
||||
<table data-base-target="_next" class="action comments">
|
||||
<tbody>
|
||||
<?php foreach ($comments as $comment): ?>
|
||||
<?php
|
||||
switch ($comment->type) {
|
||||
switch ($comment->type) {
|
||||
case 'flapping':
|
||||
$icon = 'flapping';
|
||||
$tooltip = 'Comment was caused by a flapping host or service.';
|
||||
break;
|
||||
$icon = 'flapping';
|
||||
$title = $this->translate('Flapping');
|
||||
$tooltip = $this->translate('Comment was caused by a flapping host or service.');
|
||||
break;
|
||||
case 'comment':
|
||||
$icon = 'user';
|
||||
$tooltip = 'Comment was created by an user.';
|
||||
break;
|
||||
$icon = 'user';
|
||||
$title = $this->translate('User Comment');
|
||||
$tooltip = $this->translate('Comment was created by an user.');
|
||||
break;
|
||||
case 'downtime':
|
||||
$icon = 'down';
|
||||
$tooltip = 'Comment was caused by a downtime.';
|
||||
$icon = 'down';
|
||||
$title = $this->translate('Downtime');
|
||||
$tooltip = $this->translate('Comment was caused by a downtime.');
|
||||
case 'ack':
|
||||
$icon = 'acknowledgement';
|
||||
$tooltip = 'Comment was caused by an acknowledgement.';
|
||||
}
|
||||
$icon = 'acknowledgement';
|
||||
$title = $this->translate('Acknowledgement');
|
||||
$tooltip = $this->translate('Comment was caused by an acknowledgement.');
|
||||
}
|
||||
?>
|
||||
<?= $this->icon($icon . '.png', $tooltip) ?><br />
|
||||
<?= $this->timeSince($comment->timestamp) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($comment->objecttype === 'service'): ?><?= $this->icon('service.png', 'Service comment') ?> <?= $this->qlink(
|
||||
$comment->service,
|
||||
'monitoring/show/service',
|
||||
array(
|
||||
<tr class="state invalid">
|
||||
<td class="state" style="width: 12em;">
|
||||
<?= $this->icon($icon . '.png', $tooltip) ?>
|
||||
<br>
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= $this->prefixedTimeSince($comment->timestamp); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($comment->objecttype === 'service'): ?>
|
||||
<?= $this->icon('service.png'); ?> <a href="<?= $this->href('monitoring/show/service', array(
|
||||
'host' => $comment->host,
|
||||
'service' => $comment->service,
|
||||
)
|
||||
) ?> on<?php else: ?><?= $this->icon('host.png', 'Host comment') ?> <?php endif ?> <?= $this->qlink(
|
||||
$comment->host,
|
||||
'monitoring/show/host',
|
||||
array(
|
||||
)); ?>">
|
||||
<?= $comment->service; ?>
|
||||
</a>
|
||||
<small>
|
||||
<?= $this->translate('on') . ' ' . $comment->host; ?>
|
||||
</small>
|
||||
<?php else: ?>
|
||||
<?= $this->icon('host.png'); ?> <a href="<?= $this->href('monitoring/show/host', array(
|
||||
'host' => $comment->host
|
||||
)
|
||||
) ?> by <strong><?= $this->escape($comment->author) ?></strong><br />
|
||||
<p><?= $this->escape($comment->comment) ?></p>
|
||||
<?php if ($comment->persistent): ?>Comment is persistent<br /><?php endif ?>
|
||||
<b>Expires: </b> <?=
|
||||
($comment->expiration) ?
|
||||
$this->timeUntil($comment->expiration) :
|
||||
'Never'
|
||||
)); ?>">
|
||||
<?= $comment->host; ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<?= $this->icon('comment.png'); ?> <?= isset($comment->author)
|
||||
? '[' . $comment->author . '] '
|
||||
: '';
|
||||
?><?= $this->escape($comment->comment); ?>
|
||||
<br>
|
||||
<?= $comment->persistent
|
||||
? $this->translate('This comment is persistent.')
|
||||
: $this->translate('This comment is not persistent.');
|
||||
?>
|
||||
<br>
|
||||
<?= $comment->expiration ? sprintf(
|
||||
$this->translate('This comment expires on %s at %s.'),
|
||||
date('d.m.y', $comment-expiration),
|
||||
date('H:i', $comment->expiration)
|
||||
) : $this->translate('This comment does not expire.'); ?>
|
||||
</td>
|
||||
<?php
|
||||
$data = array(
|
||||
'commentid' => $comment->id,
|
||||
'host' => $comment->host
|
||||
);
|
||||
if ($comment->objecttype === 'service') {
|
||||
$data['service'] = $comment->service;
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td style="width: 2em">
|
||||
<?php
|
||||
|
||||
$data = array(
|
||||
'commentid' => $comment->id,
|
||||
'host' => $comment->host
|
||||
);
|
||||
|
||||
if ($comment->objecttype === 'service') {
|
||||
$data['service'] = $comment->service;
|
||||
}
|
||||
|
||||
// echo $cf->iconSubmitForm(
|
||||
// 'img/icons/remove.png',
|
||||
echo $cf->labelSubmitForm(
|
||||
'X',
|
||||
'Remove comment',
|
||||
'link-like',
|
||||
'removecomment',
|
||||
$data
|
||||
);
|
||||
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<td style="width: 2em">
|
||||
<?= $helper->labelSubmitForm(
|
||||
'X',
|
||||
$this->translate('Remove Comment'),
|
||||
'link-like',
|
||||
'removecomment',
|
||||
$data
|
||||
); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -22,7 +22,7 @@ foreach ($groupData as $groupName => $groupInfo): ?>
|
||||
<div class="box entry">
|
||||
<img src="<?= $this->href('/static/gravatar', array('email' => $c->contact_email )) ?>" />
|
||||
<a href="<?= $this->href(
|
||||
'monitoring/show/contacts',
|
||||
'monitoring/show/contact',
|
||||
array('contact' => $c->contact_name)
|
||||
); ?>">
|
||||
<?= $this->escape($c->contact_alias) ?>
|
||||
|
@ -14,31 +14,41 @@ $contactHelper = $this->getHelper('ContactFlags');
|
||||
<?php
|
||||
if (count($contacts) === 0) {
|
||||
echo t('No contacts matching the filter');
|
||||
return;
|
||||
}
|
||||
foreach ($contacts as $contact):
|
||||
$periodLink = $this->href('monitoring/show/contacts', array('contact' => $contact->contact_name)); ?>
|
||||
<div style="background-color: lightgray; padding: 0.5em; border-radius: 0.5em; overflow: hidden; margin: 0.125em; float: left;">
|
||||
<img src="<?= $this->href('/static/gravatar', array('email' => $contact->contact_email )) ?>"
|
||||
style="width: 60px; border: 4px solid white; border-radius: 8px; margin-right: 8px; float: left;" />
|
||||
foreach ($contacts as $contact): ?>
|
||||
<div class="contact">
|
||||
<img src="<?= $this->href('/static/gravatar', array('email' => $contact->contact_email )) ?>" />
|
||||
<a href="<?= $this->href(
|
||||
'monitoring/show/contact',
|
||||
array('contact' => $contact->contact_name)
|
||||
) ?>"><strong><?= $contact->contact_name ?></strong></a> (<?= $contact->contact_alias ?>)
|
||||
<?php
|
||||
foreach (array(
|
||||
'eMail: <a href="mailto:%1$s">%1$s</a>' => $contact->contact_email,
|
||||
'Pager: %s' => $contact->contact_pager,
|
||||
'Service notification period: %s' => $contact->contact_notify_service_timeperiod,
|
||||
'Host notification period: %s' => $contact->contact_notify_host_timeperiod
|
||||
) as $format => $value):
|
||||
if ($value): ?>
|
||||
<br />
|
||||
<?php
|
||||
printf($format, htmlspecialchars($value));
|
||||
endif;
|
||||
endforeach;
|
||||
?>
|
||||
<div class="clearfix"></div>
|
||||
<div><?= sprintf(
|
||||
'%1$s: <a href="mailto:%2$s">%2$s</a>',
|
||||
t('Email'),
|
||||
$this->escape($contact->contact_email)
|
||||
) ?></div>
|
||||
<?php if ($contact->contact_pager): ?>
|
||||
<div>
|
||||
<?= t('Pager') ?>:
|
||||
<?= $this->escape($contact->contact_pager) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div style="clear: both;"></div>
|
||||
<div class="notification-periods">
|
||||
<div>
|
||||
<?= t('Service notification period') ?>:
|
||||
<?= $this->escape($contact->contact_notify_service_timeperiod) ?>
|
||||
</div>
|
||||
<div>
|
||||
<?= t('Host notification period') ?>:
|
||||
<?= $this->escape($contact->contact_notify_host_timeperiod) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
endforeach;
|
||||
if (true): /* The following piece of HTML MUST be nested in <?php */ ?>
|
||||
<div style="clear: both;"></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
@ -1,70 +1,121 @@
|
||||
<?php
|
||||
$helper = $this->getHelper('CommandForm');
|
||||
?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<div style="margin: 1em">
|
||||
<?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->paginationControl($downtimes, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
|
||||
<div class="content downtimes">
|
||||
<table data-base-target="_next" class="action">
|
||||
<tbody>
|
||||
<?php
|
||||
if (count($downtimes) === 0) {
|
||||
echo t('No downtimes matching the filter');
|
||||
}
|
||||
foreach ($this->downtimes as $downtime): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?= $this->dateFormat()->formatDateTime($downtime->start); ?> -
|
||||
<?= $this->dateFormat()->formatDateTime($downtime->end); ?>
|
||||
<br />
|
||||
<small>Duration: <?= $this->util()->showHourMin($downtime->duration); ?></small>
|
||||
<br />
|
||||
<small>The <?php if($downtime->is_flexible): ?>flexible<?php else: ?>fixed<?php endif; ?> downtime is <?php if(!$downtime->is_in_effect): ?>not <?php endif; ?>in effect</small>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($downtime->service)): ?>
|
||||
<a href="<?= $this->href('monitoring/show/service', array(
|
||||
'host' => (string) $downtime->host,
|
||||
'service' => (string) $downtime->service
|
||||
)); ?>"><?= $downtime->service ?></a>
|
||||
<small>on <?= $downtime->host ?></small>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->href('monitoring/show/host', array(
|
||||
'host' => (string) $downtime->host
|
||||
)); ?>"><?= $downtime->host ?> </a>
|
||||
<?php endif; ?>
|
||||
<br />
|
||||
<?= $downtime->author ?>: <?= $downtime->comment ?>
|
||||
<br />
|
||||
<small>Entry Time: <?= ($downtime->entry_time) ? $this->dateFormat()->formatDateTime((int) $downtime->entry_time) : ''; ?>
|
||||
</small>
|
||||
<td style="width: 2em">
|
||||
<?php
|
||||
$data = array(
|
||||
'downtimeid' => $downtime->id,
|
||||
'host' => $downtime->host
|
||||
);
|
||||
if (isset($downtime->service)) {
|
||||
$data['service'] = $downtime->service;
|
||||
}
|
||||
// echo $helper->iconSubmitForm(
|
||||
// 'img/icons/remove.png',
|
||||
echo $helper->labelSubmitForm(
|
||||
'X',
|
||||
'Remove Downtime',
|
||||
'link-like',
|
||||
'removedowntime',
|
||||
$data
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php if (false === $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs->render($this); ?>
|
||||
<div style="margin: 1em" class="dontprint">
|
||||
<?= $this->translate('Sort by'); ?> <?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->widget('limiter', array('url' => $this->url, 'max' => $downtimes->count())); ?>
|
||||
<?= $this->paginationControl($downtimes, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="content">
|
||||
<?php if (empty($downtimes)): ?>
|
||||
<?= $this->translate('No downtimes matching the filter'); ?>
|
||||
</div>
|
||||
<?php return; endif ?>
|
||||
|
||||
<table data-base-target="_next" class="action">
|
||||
<tbody>
|
||||
<?php foreach ($downtimes as $downtime): ?>
|
||||
<?php
|
||||
if (isset($downtime->service)) {
|
||||
$stateName = strtolower($this->util()->getServiceStateName($downtime->service_state));
|
||||
} else {
|
||||
$stateName = strtolower($this->util()->getHostStateName($downtime->host_state));
|
||||
}
|
||||
?>
|
||||
<tr class="state <?= $stateName; ?><?= $downtime->is_in_effect ? ' handled' : ''; ?>">
|
||||
<td class="state">
|
||||
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong>
|
||||
<br>
|
||||
<?= $this->prefixedTimeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($downtime->service)): ?>
|
||||
<a href="<?= $this->href('monitoring/show/service', array(
|
||||
'host' => $downtime->host,
|
||||
'service' => $downtime->service
|
||||
)); ?>">
|
||||
<?= $downtime->service; ?>
|
||||
</a>
|
||||
<small>
|
||||
<?= $this->translate('on'); ?> <?= $downtime->host; ?>
|
||||
</small>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->href('monitoring/show/host', array(
|
||||
'host' => $downtime->host
|
||||
)); ?>">
|
||||
<?= $downtime->host; ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<?= $this->icon('comment.png'); ?> [<?= $downtime->author; ?>] <?= $downtime->comment; ?>
|
||||
<br>
|
||||
<small>
|
||||
<?php if ($downtime->is_flexible): ?>
|
||||
<?php if ($downtime->is_in_effect): ?>
|
||||
<?= sprintf(
|
||||
$this->translate('This flexible downtime was started on %s at %s and lasts for %s until %s at %s.'),
|
||||
date('d.m.y', $downtime->start),
|
||||
date('H:i', $downtime->start),
|
||||
$this->format()->duration($downtime->duration),
|
||||
date('d.m.y', $downtime->end),
|
||||
date('H:i', $downtime->end)
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= sprintf(
|
||||
$this->translate('This flexible downtime has been scheduled to start between %s - %s and to last for %s.'),
|
||||
date('d.m.y H:i', $downtime->scheduled_start),
|
||||
date('d.m.y H:i', $downtime->scheduled_end),
|
||||
$this->format()->duration($downtime->duration)
|
||||
); ?>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<?php if ($downtime->is_in_effect): ?>
|
||||
<?= sprintf(
|
||||
$this->translate('This fixed downtime was started on %s at %s and expires on %s at %s.'),
|
||||
date('d.m.y', $downtime->start),
|
||||
date('H:i', $downtime->start),
|
||||
date('d.m.y', $downtime->end),
|
||||
date('H:i', $downtime->end)
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= sprintf(
|
||||
$this->translate('This fixed downtime has been scheduled to start on %s at %s and to end on %s at %s.'),
|
||||
date('d.m.y', $downtime->scheduled_start),
|
||||
date('H:i', $downtime->scheduled_start),
|
||||
date('d.m.y', $downtime->scheduled_end),
|
||||
date('H:i', $downtime->scheduled_end)
|
||||
); ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</small>
|
||||
</td>
|
||||
<?php
|
||||
$data = array(
|
||||
'downtimeid' => $downtime->id,
|
||||
'host' => $downtime->host
|
||||
);
|
||||
if (isset($downtime->service)) {
|
||||
$data['service'] = $downtime->service;
|
||||
}
|
||||
?>
|
||||
<td style="width: 2em">
|
||||
<?= $helper->labelSubmitForm(
|
||||
'X',
|
||||
'Remove Downtime',
|
||||
'link-like',
|
||||
'removedowntime',
|
||||
$data
|
||||
); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,122 +1,115 @@
|
||||
|
||||
<?php if (!$this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs->render($this); ?>
|
||||
<div style="margin: 1em" class="dontprint">
|
||||
<?= $this->translate('Sort by') ?> <?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->paginationControl($history, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (false === $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs->render($this); ?>
|
||||
<div style="margin: 1em" class="dontprint">
|
||||
<?= $this->translate('Sort by'); ?> <?= $this->sortControl->render($this); ?>
|
||||
</div>
|
||||
<?= $this->widget('limiter', array('url' => $this->url, 'max' => $this->history->count())); ?>
|
||||
<?= $this->paginationControl($history, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="content">
|
||||
<?php if (empty($history)): ?>
|
||||
<?= $this->translate('No entries found') ?>
|
||||
<?= $this->translate('No history events matching the filter') ?>
|
||||
</div>
|
||||
<?php return; endif ?>
|
||||
|
||||
<table data-base-target="_next" class="action">
|
||||
<tbody>
|
||||
<?php
|
||||
if (count($history) === 0) {
|
||||
echo t('No history events matching the filter');
|
||||
}
|
||||
?>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php
|
||||
$class = null;
|
||||
$isService = false;
|
||||
switch ($event->type) {
|
||||
case 'notify':
|
||||
$icon = 'notification';
|
||||
$title = 'Notification';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'comment':
|
||||
$icon = 'comment';
|
||||
$title = 'Comment';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'ack':
|
||||
$icon = 'acknowledgement';
|
||||
$title = 'Acknowledgement';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_comment':
|
||||
$icon = 'in-downtime';
|
||||
$title = 'In Downtime';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'flapping':
|
||||
$icon = 'flapping';
|
||||
$title = 'Flapping';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'hard_state':
|
||||
$icon = '{{HARDSTATE_ICON}}';
|
||||
$title = 'Hard State';
|
||||
$msg = $event->output . '<br /><small>Attempt ' . $event->attempt . '/' . $event->max_attempts . ' (Hard)</small>';
|
||||
$class = 'border-status-' . (
|
||||
$isService ?
|
||||
strtolower($this->util()->getServiceStateName($event->state)) :
|
||||
strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
break;
|
||||
case 'soft_state':
|
||||
$icon = '{{SOFTSTATE_ICON}}';
|
||||
$title = 'Soft State';
|
||||
$msg = $event->output . '<br /><small>Attempt ' . $event->attempt . '/' . $event->max_attempts . ' (Soft)</small>';
|
||||
$class = 'border-status-' . (
|
||||
$isService ?
|
||||
strtolower($this->util()->getServiceStateName($event->state)) :
|
||||
strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
break;
|
||||
case 'dt_start':
|
||||
$icon = 'downtime-start';
|
||||
$title = 'Downtime Start';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_end':
|
||||
$icon = 'downtime-end';
|
||||
$title = 'Downtime End';
|
||||
$msg = $event->output;
|
||||
break;
|
||||
}
|
||||
?>
|
||||
<tr class="state">
|
||||
<td style="width: 6em;" <?= !empty($class) ? 'class="' . $class . '"' : '' ?>">
|
||||
<?= date('d.m. H:i', $event->timestamp); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($event->service)): ?>
|
||||
<a href="<?= $this->href('monitoring/show/service', array(
|
||||
'host' => $event->host,
|
||||
'service' => $event->service
|
||||
)); ?>">
|
||||
<?= $event->service ?>
|
||||
</a>
|
||||
<small>
|
||||
on <?= $event->host ?>
|
||||
</small>
|
||||
<?php $isService = true; ?>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->href('monitoring/show/host', array(
|
||||
'host' => $event->host
|
||||
)); ?>">
|
||||
<?= $event->host ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<br />
|
||||
<div>
|
||||
<i title="<?= $title ?>" class="icinga-icon-<?= $icon ?>"></i>
|
||||
<?php if (!empty($msg)) { echo $msg; } ?>
|
||||
</div>
|
||||
</td>
|
||||
<table data-base-target="_next" class="action">
|
||||
<tbody>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php
|
||||
$stateName = 'invalid';
|
||||
$isService = isset($event->service);
|
||||
switch ($event->type) {
|
||||
case 'notify':
|
||||
$icon = 'notification';
|
||||
$title = $this->translate('Notification');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'comment':
|
||||
$icon = 'comment';
|
||||
$title = $this->translate('Comment');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'ack':
|
||||
$icon = 'acknowledgement';
|
||||
$title = $this->translate('Acknowledgement');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_comment':
|
||||
$icon = 'in_downtime';
|
||||
$title = $this->translate('In Downtime');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'flapping':
|
||||
$icon = 'flapping';
|
||||
$title = $this->translate('Flapping');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'hard_state':
|
||||
$icon = $isService ? 'service' : 'host';
|
||||
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $event->output;
|
||||
$stateName = (
|
||||
$isService
|
||||
? strtolower($this->util()->getServiceStateName($event->state))
|
||||
: strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
$title = strtoupper($stateName); // TODO: Should be translatable!
|
||||
break;
|
||||
case 'soft_state':
|
||||
$icon = 'softstate';
|
||||
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $event->output;
|
||||
$stateName = (
|
||||
$isService
|
||||
? strtolower($this->util()->getServiceStateName($event->state))
|
||||
: strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
$title = strtoupper($stateName); // TODO: Should be translatable!
|
||||
break;
|
||||
case 'dt_start':
|
||||
$icon = 'downtime_start';
|
||||
$title = $this->translate('Downtime Start');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_end':
|
||||
$icon = 'downtime_end';
|
||||
$title = $this->translate('Downtime End');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
}
|
||||
?>
|
||||
<tr class="state <?= $stateName; ?>">
|
||||
<td class="state">
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= date('d.m. H:i', $event->timestamp); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($isService): ?>
|
||||
<a href="<?= $this->href('monitoring/show/service', array(
|
||||
'host' => $event->host,
|
||||
'service' => $event->service
|
||||
)); ?>">
|
||||
<?= $event->service; ?>
|
||||
</a>
|
||||
<small>
|
||||
<?= $this->translate('on') . ' ' . $event->host; ?>
|
||||
</small>
|
||||
<?php else: ?>
|
||||
<a href="<?= $this->href('monitoring/show/host', array(
|
||||
'host' => $event->host
|
||||
)); ?>">
|
||||
<?= $event->host; ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<div>
|
||||
<?= $this->icon($icon . '.png', $title); ?> <?= empty($msg) ? '' : $msg; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<? endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
@ -10,28 +10,33 @@ $cf = $this->getHelper('CommandForm');
|
||||
|
||||
<div class="content processinfo">
|
||||
<p>Backend <strong><?= $this->backendName; ?></strong>
|
||||
<?= $ps->is_currently_running === '1' ? sprintf('has been running with PID %d ', $ps->process_id) . $this->prefixedTimeSince($ps->program_start_time) : 'is not running'; ?>.
|
||||
<?= $ps->is_currently_running === '1'
|
||||
? sprintf(
|
||||
$this->translate('has been running with PID %d '),
|
||||
$ps->process_id
|
||||
) . $this->prefixedTimeSince($ps->program_start_time)
|
||||
: $this->translate('is not running'); ?>.
|
||||
|
||||
<table class="avp">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Last status update</th>
|
||||
<th><?= $this->translate('Last status update'); ?></th>
|
||||
<td><?= $this->timeSince($ps->status_update_time) ?> ago</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Last check command</th>
|
||||
<th><?= $this->translate('Last check command'); ?></th>
|
||||
<td><?= $this->timeSince($ps->last_command_check) ?> ago</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Global host event handler</th>
|
||||
<td><?= $ps->global_host_event_handler ? $ps->global_host_event_handler : 'Not set' ?></td>
|
||||
<th><?= $this->translate('Global host event handler'); ?></th>
|
||||
<td><?= $ps->global_host_event_handler ? $ps->global_host_event_handler : $this->translate('Not set'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Global service event handler</th>
|
||||
<td><?= $ps->global_service_event_handler ? $ps->global_service_event_handler : 'Not set' ?></td>
|
||||
<th><?= $this->translate('Global service event handler'); ?></th>
|
||||
<td><?= $ps->global_service_event_handler ? $ps->global_service_event_handler : $this->translate('Not set'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Notifications enabled</th>
|
||||
<th><?= $this->translate('Notifications enabled'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->notifications_enabled,
|
||||
@ -41,15 +46,18 @@ $cf = $this->getHelper('CommandForm');
|
||||
array('global' => '1')
|
||||
) ?>
|
||||
<?php if ($ps->notifications_enabled === '1'): ?>
|
||||
<a rel="tooltip" title="Disable notifications for a specific time on a program-wide basis" href="<?= $this->href('monitoring/command/disablenotificationswithexpire') ?>" data-base-target="_next">Temporarily disable</a>
|
||||
<a rel="tooltip" title="<?= $this->translate('Disable notifications for a specific time on a program-wide basis'); ?>"
|
||||
href="<?= $this->href('monitoring/command/disablenotificationswithexpire') ?>" data-base-target="_next">
|
||||
<?= $this->translate('Temporarily disable'); ?>
|
||||
</a>
|
||||
<?php elseif ($ps->disable_notif_expire_time): ?>
|
||||
Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_time) ?></strong>
|
||||
<?= sprintf($this->translate('Will be re-enabled in %s'), '<strong>' . $this->timeUntil($ps->disable_notif_expire_time) . '</strong>'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Execute active service checks</th>
|
||||
<th><?= $this->translate('Execute active service checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->active_service_checks_enabled,
|
||||
@ -60,7 +68,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Accept passive service checks</th>
|
||||
<th><?= $this->translate('Accept passive service checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->passive_service_checks_enabled,
|
||||
@ -71,7 +79,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Execute active host checks</th>
|
||||
<th><?= $this->translate('Execute active host checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->active_host_checks_enabled,
|
||||
@ -82,7 +90,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Accept passive host checks</th>
|
||||
<th><?= $this->translate('Accept passive host checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->passive_host_checks_enabled,
|
||||
@ -93,7 +101,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Eventhandlers enabled</th>
|
||||
<th><?= $this->translate('Eventhandlers enabled'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->event_handlers_enabled,
|
||||
@ -104,7 +112,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Obsessing over host checks</th>
|
||||
<th><?= $this->translate('Obsessing over host checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->obsess_over_hosts,
|
||||
@ -115,7 +123,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Obsessing over service checks</th>
|
||||
<th><?= $this->translate('Obsessing over service checks'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->obsess_over_services,
|
||||
@ -126,7 +134,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Flap detection enabled</th>
|
||||
<th><?= $this->translate('Flap detection enabled'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->flap_detection_enabled,
|
||||
@ -137,7 +145,7 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Process performance data</th>
|
||||
<th><?= $this->translate('Process performance data'); ?></th>
|
||||
<td><?= $cf->toggleSubmitForm(
|
||||
'',
|
||||
$ps->process_performance_data,
|
||||
@ -148,17 +156,17 @@ Will be re-enabled in <strong><?= $this->timeUntil($ps->disable_notif_expire_tim
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Monitoring Process</th>
|
||||
<th><?= $this->translate('Monitoring Process'); ?></th>
|
||||
<td><?= $cf->labelSubmitForm(
|
||||
'Restart',
|
||||
'Restart the monitoring process',
|
||||
$this->translate('Restart'),
|
||||
$this->translate('Restart the monitoring process'),
|
||||
'',
|
||||
'restartprocess'
|
||||
) ?>
|
||||
<!--<?=
|
||||
$cf->labelSubmitForm(
|
||||
'Shutdown monitoring process',
|
||||
'Shutdown the monitoring process',
|
||||
$this->translate('Shutdown monitoring process'),
|
||||
$this->translate('Shutdown the monitoring process'),
|
||||
'btn-cta',
|
||||
'shutdownprocess'
|
||||
);
|
||||
|
@ -31,12 +31,12 @@ foreach ($object->comments as $comment) {
|
||||
);
|
||||
|
||||
$list[] = sprintf(
|
||||
'<tr><th>%s (%s)</th><td data-base-target="_self">%s (%s) %s</td></tr>',
|
||||
'<tr><th>%s (%s)</th><td><table><tr><td style="vertical-align: top;" data-base-target="_self">%s (%s):</td><td style="padding-left: .5em;">%s</td></tr></table></td></tr>',
|
||||
$this->escape($comment->author),
|
||||
$this->timeSince($comment->timestamp),
|
||||
$iconForm,
|
||||
ucfirst($comment->type),
|
||||
$text
|
||||
str_replace(array('\r\n', '\n'), '<br>', $text)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -42,11 +42,11 @@ foreach ($object->downtimes as $downtime) {
|
||||
) : $this->escape($downtime->comment);
|
||||
|
||||
$list[] = sprintf(
|
||||
'<tr><th>%s</th><td data-base-target="_self">%s %s: %s</td></tr>',
|
||||
'<tr><th>%s</th><td><table><tr><td style="vertical-align: top;" data-base-target="_self">%s %s:</td><td style="padding-left: .5em;">%s</td></tr></table></td></tr>',
|
||||
$this->escape($downtime->author),
|
||||
$iconForm,
|
||||
$state,
|
||||
$text
|
||||
str_replace(array('\r\n', '\n'), '<br>', $text)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
$contactHelper = $this->getHelper('ContactFlags');
|
||||
?>
|
||||
<div style="margin-top: 0.5em;">
|
||||
<?php foreach ($contacts as $contact): ?>
|
||||
<table style="border-spacing: 0.25em; border-collapse: separate;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2" style="text-align: left">
|
||||
<?= $this->escape($contact->contact_name) ?><span style="font-weight: normal;"> (<?=
|
||||
$this->escape($contact->contact_alias)
|
||||
?>)</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?= t('Email') ?></td>
|
||||
<td><?php printf(
|
||||
'<a href="mailto:%1$s">%1$s</a>',
|
||||
$this->escape($contact->contact_email)
|
||||
); ?></td>
|
||||
</tr>
|
||||
<?php if ($contact->contact_pager): ?>
|
||||
<tr>
|
||||
<td><?= t('Pager') ?></td>
|
||||
<td><?= $this->escape($contact->contact_pager) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
<td><?= t('Flags (service)') ?></td>
|
||||
<td><?= $this->escape($contactHelper->contactFlags($contact, 'service')) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= t('Flags (host)') ?></td>
|
||||
<td><?= $this->escape($contactHelper->contactFlags($contact, 'host')) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= t('Service notification period') ?></td>
|
||||
<td><?= $this->escape($contact->contact_notify_service_timeperiod) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?= t('Host notification period') ?></td>
|
||||
<td><?= $this->escape($contact->contact_notify_host_timeperiod) ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
return;
|
||||
endforeach;
|
||||
printf(
|
||||
'%s: `%s\'',
|
||||
t('No such contact'), $contact_name
|
||||
);
|
||||
?>
|
||||
</div>
|
@ -1,149 +1,127 @@
|
||||
<?php
|
||||
|
||||
use Icinga\Module\Monitoring\Object\Service;
|
||||
|
||||
if ($object instanceof Service) {
|
||||
$title = $object->service_description;
|
||||
$params = array(
|
||||
'host' => $object->host_name,
|
||||
'service' => $object->service_description
|
||||
);
|
||||
} else {
|
||||
$title = $object->host_name;
|
||||
$params = array('host' => $object->host_name);
|
||||
}
|
||||
|
||||
// TODO: Remove this once we have better helpers
|
||||
$states = array(
|
||||
'service' => array(
|
||||
'ok',
|
||||
'warning',
|
||||
'critical',
|
||||
'unknown',
|
||||
99 => 'pending',
|
||||
),
|
||||
'host' => array(
|
||||
'up',
|
||||
'down',
|
||||
'unreachable',
|
||||
99 => 'pending',
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
?><div class="controls">
|
||||
<?= $this->render('show/components/header.phtml') ?>
|
||||
<h1><?= $this->translate("This object's event history") ?></h1>
|
||||
<?= $this->widget('limiter', array('url' => $this->url, 'max' => $this->history->count())) ?>
|
||||
<?= $this->paginationControl($this->history, null, null, array('preserve' => $this->preserve)); ?>
|
||||
<div class="controls">
|
||||
<?= $this->render('show/components/header.phtml'); ?>
|
||||
<h1><?= $this->translate('This Object\'s Event History'); ?></h1>
|
||||
<?= $this->widget('limiter', array('url' => $url, 'max' => $history->count())); ?>
|
||||
<?= $this->paginationControl($history, null, null, array('preserve' => $this->preserve)); ?>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<?php if($this->history->count() === 0): ?>
|
||||
<?= $this->translate('No History Available For This Object') ?>
|
||||
<?php if (empty($history)): ?>
|
||||
<?= $this->translate('No history available for this object'); ?>
|
||||
</div>
|
||||
<?php return; endif ?>
|
||||
|
||||
<table data-base-target="_next" class="action objecthistory">
|
||||
<tbody>
|
||||
|
||||
<?php foreach ($this->history as $event):
|
||||
if (array_key_exists($event->state, $states[$event->object_type])) {
|
||||
$state_class = $states[$event->object_type][$event->state];
|
||||
} else {
|
||||
$state_class = 'invalid';
|
||||
}
|
||||
$extraTitle = false;
|
||||
switch ($event->type) {
|
||||
case 'notify':
|
||||
$icon = 'notification';
|
||||
$title = $this->translate('Notification');
|
||||
$state = $this->translate('ACK');
|
||||
break;
|
||||
case 'comment':
|
||||
$icon = 'comment';
|
||||
$title = $this->translate('Comment');
|
||||
break;
|
||||
case 'comment_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Comment deleted');
|
||||
break;
|
||||
case 'ack':
|
||||
$icon = 'acknowledgement';
|
||||
$title = $this->translate('Acknowledge');
|
||||
break;
|
||||
case 'ack_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Ack removed');
|
||||
break;
|
||||
case 'dt_comment':
|
||||
$icon = 'in_downtime';
|
||||
$title = $this->translate('In Downtime');
|
||||
break;
|
||||
case 'dt_comment_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Downtime removed');
|
||||
break;
|
||||
case 'flapping':
|
||||
$icon = 'flapping';
|
||||
$title = $this->translate('Flapping');
|
||||
break;
|
||||
case 'flapping_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Flapping stopped');
|
||||
break;
|
||||
case 'hard_state':
|
||||
$icon = 'submit';
|
||||
$title = strtoupper($state_class);
|
||||
break;
|
||||
case 'soft_state':
|
||||
$icon = 'softstate';
|
||||
$title = strtoupper($state_class);
|
||||
$extraTitle = $this->translate('Soft State');
|
||||
$state_class .= ' handled';
|
||||
break;
|
||||
case 'dt_start':
|
||||
$icon = 'downtime_start';
|
||||
$title = $this->translate('Downtime Start');
|
||||
break;
|
||||
case 'dt_end':
|
||||
$icon = 'downtime_end';
|
||||
$title = $this->translate('Downtime End');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
<tr class="state <?= $state_class ?>">
|
||||
<td class="state"><strong><?= $this->escape($title) ?></strong><br /><?= $this->prefixedTimeSince($event->timestamp) ?><?= $extraTitle === false ? '' : '<br />' . $this->escape($extraTitle) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
<table data-base-target="_next" class="action objecthistory">
|
||||
<tbody>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php
|
||||
$stateClass = 'invalid';
|
||||
$isService = isset($event->service_description);
|
||||
switch ($event->type) {
|
||||
case 'notify':
|
||||
$icon = 'notification';
|
||||
$title = $this->translate('Notification');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'comment':
|
||||
$icon = 'comment';
|
||||
$title = $this->translate('Comment');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'comment_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Comment deleted');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'ack':
|
||||
$icon = 'acknowledgement';
|
||||
$title = $this->translate('Acknowledge');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'ack_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Ack removed');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_comment':
|
||||
$icon = 'in_downtime';
|
||||
$title = $this->translate('In Downtime');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_comment_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Downtime removed');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'flapping':
|
||||
$icon = 'flapping';
|
||||
$title = $this->translate('Flapping');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'flapping_deleted':
|
||||
$icon = 'remove';
|
||||
$title = $this->translate('Flapping stopped');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'hard_state':
|
||||
$icon = $isService ? 'service' : 'host';
|
||||
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $event->output;
|
||||
$stateClass = (
|
||||
$isService
|
||||
? strtolower($this->util()->getServiceStateName($event->state))
|
||||
: strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
$title = strtoupper($stateClass); // TODO: Should be translatable!
|
||||
break;
|
||||
case 'soft_state':
|
||||
$icon = 'softstate';
|
||||
$msg = '[ ' . $event->attempt . '/' . $event->max_attempts . ' ] ' . $event->output;
|
||||
$stateClass = (
|
||||
$isService
|
||||
? strtolower($this->util()->getServiceStateName($event->state))
|
||||
: strtolower($this->util()->getHostStateName($event->state))
|
||||
);
|
||||
$title = strtoupper($stateClass); // TODO: Should be translatable!
|
||||
break;
|
||||
case 'dt_start':
|
||||
$icon = 'downtime_start';
|
||||
$title = $this->translate('Downtime Start');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
case 'dt_end':
|
||||
$icon = 'downtime_end';
|
||||
$title = $this->translate('Downtime End');
|
||||
$msg = $event->output;
|
||||
break;
|
||||
}
|
||||
?>
|
||||
<tr class="state <?= $stateClass; ?>">
|
||||
<td class="state">
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= date('d.m. H:i', $event->timestamp); ?>
|
||||
</td>
|
||||
<td><?php
|
||||
|
||||
$output = $this->tickets ? preg_replace_callback(
|
||||
$this->tickets->getPattern(),
|
||||
array($this->tickets, 'createLink'),
|
||||
$this->escape($event->output)
|
||||
) : $this->escape($event->output);
|
||||
|
||||
echo $this->icon($icon . '.png', $title) . ' ';
|
||||
|
||||
if ($object instanceof Host): ?>
|
||||
<?= $this->escape($event->service_description) ?>
|
||||
<?php elseif ($object instanceof Service): ?>
|
||||
<?php else: ?>
|
||||
<?= $this->escape($event->service_description) ?> on <?= $this->escape($event->host_name) ?>
|
||||
<?php endif;
|
||||
|
||||
if ($event->attempt !== null) {
|
||||
printf('[ %d/%d ] ', $event->attempt, $event->max_attempts);
|
||||
}
|
||||
echo $output;
|
||||
$this->escape($msg)
|
||||
) : $this->escape($msg);
|
||||
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if ($isService): ?>
|
||||
<?= $this->escape($event->service_description) . ' ' . $this->translate('on') . ' ' . $this->escape($event->host_name); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->escape($event->host_name); ?>
|
||||
<?php endif ?>
|
||||
<br>
|
||||
<div>
|
||||
<?= $this->icon($icon . '.png', $title); ?> <?= empty($msg) ? '' : $msg; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -48,25 +48,25 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g
|
||||
<?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?>
|
||||
<div class="circle-box" style="width: <?= $extrapolatedCircleWidth; ?>;">
|
||||
<div class="outer-circle extrapolated" style="<?= sprintf(
|
||||
'width: %4$s; height: %4$s; border-color: %3$s; background-color: %1$s; margin-top: -%2$s;',
|
||||
'width: %4$s; height: %4$s; border-color: %3$s; background-color: %1$s; margin-top: -%2$Fem;',
|
||||
Color::changeBrightness($timeInfo[1][$groupName]->getColor(), 0.7),
|
||||
((float) substr($extrapolatedCircleWidth, 0, -2) / 2) . 'em',
|
||||
(float) substr($extrapolatedCircleWidth, 0, -2) / 2,
|
||||
$timeInfo[1][$groupName]->getColor(),
|
||||
$extrapolatedCircleWidth
|
||||
); ?>">
|
||||
<?php else: ?>
|
||||
<div class="circle-box" style="width: <?= $circleWidth; ?>;">
|
||||
<div class="outer-circle" style="<?= sprintf(
|
||||
'width: %2$s; height: %2$s; margin-top: -%1$s;',
|
||||
((float) substr($circleWidth, 0, -2) / 2) . 'em',
|
||||
'width: %2$s; height: %2$s; margin-top: -%1$Fem;',
|
||||
(float) substr($circleWidth, 0, -2) / 2,
|
||||
$circleWidth
|
||||
); ?>">
|
||||
<?php endif ?>
|
||||
<a class="inner-circle" style="<?= sprintf(
|
||||
'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$s; margin-left: -%1$s;',
|
||||
((float) substr($circleWidth, 0, -2) / 2) . 'em',
|
||||
'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;',
|
||||
(float) substr($circleWidth, 0, -2) / 2,
|
||||
$timeInfo[1][$groupName]->getColor(),
|
||||
$circleWidth
|
||||
(string) $circleWidth
|
||||
); ?>" href="<?= $timeInfo[1][$groupName]->getDetailUrl()->overwriteParams(
|
||||
array(
|
||||
'timestamp<' => $timeInfo[0]->start->getTimestamp(),
|
||||
|
@ -24,13 +24,15 @@ class DowntimeQuery extends IdoQuery
|
||||
'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)',
|
||||
'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)',
|
||||
'downtime_start' => "UNIX_TIMESTAMP(CASE WHEN sd.trigger_time != '0000-00-00 00:00:00' then sd.trigger_time ELSE sd.scheduled_start_time END)",
|
||||
'downtime_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)',
|
||||
'downtime_end' => 'CASE WHEN sd.is_fixed THEN UNIX_TIMESTAMP(sd.scheduled_end_time) ELSE UNIX_TIMESTAMP(sd.trigger_time) + sd.duration END',
|
||||
'downtime_duration' => 'sd.duration',
|
||||
'downtime_is_in_effect' => 'sd.is_in_effect',
|
||||
'downtime_internal_id' => 'sd.internal_downtime_id',
|
||||
'downtime_host' => 'CASE WHEN ho.name1 IS NULL THEN so.name1 ELSE ho.name1 END COLLATE latin1_general_ci',
|
||||
'downtime_service' => 'so.name2 COLLATE latin1_general_ci',
|
||||
'downtime_objecttype' => "CASE WHEN ho.object_id IS NOT NULL THEN 'host' ELSE CASE WHEN so.object_id IS NOT NULL THEN 'service' ELSE NULL END END",
|
||||
'downtime_host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END',
|
||||
'downtime_service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END'
|
||||
),
|
||||
);
|
||||
|
||||
@ -53,6 +55,16 @@ class DowntimeQuery extends IdoQuery
|
||||
'sd.object_id = so.object_id AND so.is_active = 1 AND so.objecttype_id = 2',
|
||||
array()
|
||||
);
|
||||
$this->select->joinLeft(
|
||||
array('hs' => $this->prefix . 'hoststatus'),
|
||||
'ho.object_id = hs.host_object_id',
|
||||
array()
|
||||
);
|
||||
$this->select->joinLeft(
|
||||
array('ss' => $this->prefix . 'servicestatus'),
|
||||
'so.object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
$this->joinedVirtualTables = array('downtime' => true);
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,11 @@ class HoststatusQuery extends IdoQuery
|
||||
ELSE 4
|
||||
END
|
||||
END
|
||||
END
|
||||
+
|
||||
CASE WHEN hs.state_type = 1
|
||||
THEN 8
|
||||
ELSE 0
|
||||
END'
|
||||
),
|
||||
'hostgroups' => array(
|
||||
|
@ -130,6 +130,11 @@ class StatusQuery extends IdoQuery
|
||||
ELSE 4
|
||||
END
|
||||
END
|
||||
END
|
||||
+
|
||||
CASE WHEN hs.state_type = 1
|
||||
THEN 8
|
||||
ELSE 0
|
||||
END'
|
||||
),
|
||||
'hostgroups' => array(
|
||||
@ -266,6 +271,11 @@ class StatusQuery extends IdoQuery
|
||||
END
|
||||
END
|
||||
END
|
||||
END
|
||||
+
|
||||
CASE WHEN ss.state_type = 1
|
||||
THEN 8
|
||||
ELSE 0
|
||||
END'
|
||||
),
|
||||
'serviceproblemsummary' => array(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user