Merge branch 'master' into bugfix/Improve-tooltip-descriptions-8110

Conflicts:
	modules/monitoring/application/views/scripts/hosts/show.phtml
This commit is contained in:
Alexander Klimov 2015-01-30 10:26:47 +01:00
commit 868295110a
266 changed files with 4465 additions and 5287 deletions

View File

@ -1,7 +1,8 @@
---
icingaweb2::config: /etc/icingaweb
icingaweb2::log: /var/log/icingaweb/icingaweb.log
icingaweb2::web_path: icingaweb
icingaweb2::db_user: icingaweb
icingaweb2::db_pass: icingaweb
icingaweb2::db_name: icingaweb
icingaweb2::config: /etc/icingaweb2
icingaweb2::log: /var/log/icingaweb2/icingaweb2.log
icingaweb2::web_path: icingaweb2
icingaweb2::db_user: icingaweb2
icingaweb2::db_pass: icingaweb2
icingaweb2::db_name: icingaweb2
icingaweb2::group: icingaweb2

View File

@ -13,5 +13,5 @@ node default {
source => 'puppet:////vagrant/.puppet/files/etc/profile.d/env.sh'
}
@user { vagrant: ensure => present }
User <| title == vagrant |> { groups +> icingaweb }
User <| title == vagrant |> { groups +> hiera('icingaweb2::group') }
}

View File

@ -11,7 +11,8 @@ class icinga2_pgsql {
password => 'icinga2',
schemafile => '/usr/share/icinga2-ido-pgsql/schema/pgsql.sql',
}
-> icinga2::feature { 'ido-pgsql':
source => 'puppet:///modules/icinga2_pgsql',
}
# Because Icinga 2 does not handle more than one IDO connection properly, The ido-pgsql will not be enabled by default.
# -> icinga2::feature { 'ido-pgsql':
# source => 'puppet:///modules/icinga2_pgsql',
# }
}

View File

@ -1,14 +1,15 @@
class icingaweb2::config (
$config = hiera('icingaweb2::config')
$config = hiera('icingaweb2::config'),
$web_group = hiera('icingaweb2::group')
) {
group { 'icingaweb':
group { $web_group:
ensure => present,
}
file { [ "${config}", "${config}/enabledModules", "${config}/modules", "${config}/preferences" ]:
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2770',
}
}

View File

@ -1,14 +1,15 @@
define icingaweb2::config::general (
$source,
$config = hiera('icingaweb2::config'),
$replace = true
$config = hiera('icingaweb2::config'),
$web_group = hiera('icingaweb2::group'),
$replace = true
) {
include icingaweb2::config
file { "${config}/${name}.ini":
content => template("${source}/${name}.ini.erb"),
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => 0660,
replace => $replace,
}

View File

@ -1,8 +1,9 @@
define icingaweb2::config::module (
$module,
$source,
$config = hiera('icingaweb2::config'),
$replace = true
$config = hiera('icingaweb2::config'),
$web_group = hiera('icingaweb2::group'),
$replace = true
) {
include icingaweb2::config
@ -10,7 +11,7 @@ define icingaweb2::config::module (
file { "${config}/modules/${module}":
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2770',
}
}
@ -18,7 +19,7 @@ define icingaweb2::config::module (
file { "${config}/modules/${module}/${name}.ini":
source => "${source}/modules/${module}/${name}.ini",
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => 0660,
replace => $replace,
}

View File

@ -71,6 +71,11 @@ local icinga icinga trust
host icinga icinga 127.0.0.1/32 trust
host icinga icinga ::1/128 trust
# icinga2
local icinga2 icinga2 trust
host icinga2 icinga2 127.0.0.1/32 trust
host icinga2 icinga2 ::1/128 trust
# icinga_unittest
local icinga_unittest icinga_unittest trust
host icinga_unittest icinga_unittest 127.0.0.1/32 trust
@ -81,6 +86,11 @@ local icingaweb icingaweb trust
host icingaweb icingaweb 127.0.0.1/32 trust
host icingaweb icingaweb ::1/128 trust
# icingaweb2
local <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> trust
host <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> 127.0.0.1/32 trust
host <%= scope.function_hiera(['icingaweb2::db_user']) %> <%= scope.function_hiera(['icingaweb2::db_user']) %> ::1/128 trust
# "local" is for Unix domain socket connections only
local all all ident
# IPv4 local connections:

View File

@ -0,0 +1,29 @@
# Class: php_imagick
#
# This class installs the ImageMagick PHP module.
#
# Parameters:
#
# Actions:
#
# Requires:
#
# php
#
# Sample Usage:
#
# include php_imagick
#
class php_imagick {
include php
$php_imagick = $::operatingsystem ? {
/(Debian|Ubuntu)/ => 'php5-imagick',
/(RedHat|CentOS|Fedora)/ => 'php-pecl-imagick',
/(SLES|OpenSuSE)/ => 'php5-imagick',
}
package { $php_imagick:
ensure => latest,
}
}

View File

@ -1,13 +1,15 @@
class icingaweb2_dev (
$config = hiera('icingaweb2::config'),
$log = hiera('icingaweb2::log'),
$web_path = hiera('icingaweb2::web_path'),
$db_user = hiera('icingaweb2::db_user'),
$db_pass = hiera('icingaweb2::db_pass'),
$db_name = hiera('icingaweb2::db_name'),
$config = hiera('icingaweb2::config'),
$log = hiera('icingaweb2::log'),
$web_path = hiera('icingaweb2::web_path'),
$db_user = hiera('icingaweb2::db_user'),
$db_pass = hiera('icingaweb2::db_pass'),
$db_name = hiera('icingaweb2::db_name'),
$web_group = hiera('icingaweb2::group'),
) {
include apache
include php
include php_imagick
include icingaweb2::config
include icingacli
include icinga_packages
@ -19,7 +21,7 @@ class icingaweb2_dev (
}
# TODO(el): icinga-gui is not a icingaweb2_dev package
package { [ 'php-pdo', 'php-ldap', 'php-phpunit-PHPUnit', 'icinga-gui' ]:
package { [ 'php-gd', 'php-intl', 'php-pdo', 'php-ldap', 'php-phpunit-PHPUnit', 'icinga-gui' ]:
ensure => latest,
notify => Service['apache'],
require => Class['icinga_packages'],
@ -28,7 +30,7 @@ class icingaweb2_dev (
Exec { path => '/usr/local/bin:/usr/bin:/bin' }
# TODO(el): Enabling/disabling modules should be a resource
User <| alias == apache |> { groups +> 'icingaweb' }
User <| alias == apache |> { groups +> $web_group }
-> exec { 'enable-monitoring-module':
command => 'icingacli module enable monitoring',
user => 'apache',
@ -50,7 +52,7 @@ class icingaweb2_dev (
file { $log_dir:
ensure => directory,
owner => 'root',
group => 'icingaweb',
group => $web_group,
mode => '2775'
}

View File

@ -1,5 +1,5 @@
[autologin]
backend = autologin
backend = external
[icingaweb-mysql]
backend = db

263
README.md
View File

@ -2,267 +2,26 @@
## Table of Contents
0. [General Information](#general information)
0. [About](#about)
1. [Installation](#installation)
2. [Support](#support)
3. [Vagrant - Virtual development environment](#vagrant)
## General Information
## About
`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/).
**Icinga Web 2** is the next generation open source monitoring web interface, framework
and command-line interface developed by the [Icinga Project](https://www.icinga.org/), supporting Icinga 2,
Icinga Core and any other monitoring backend compatible with the Livestatus Protocol.
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!
![Icinga Web 2](https://www.icinga.org/wp-content/uploads/2014/06/service_detail.png "Icinga Web 2")
## Installation
Please navigate to [doc/installation.md](doc/installation.md) for updated details.
For installing Icinga Web 2 please read [doc/installation.md](doc/installation.md).
## 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
### 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,
MySQL and PostgreSQL backends as well as the LDAP authentication. All you
have to do is install Vagrant and run:
vagrant up
> **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.
After you should be able to browse [localhost:8080/icingaweb](http://localhost:8080/icingaweb).
### Environment
**Forwarded ports**:
<table>
<tr>
<th>Proctocol</th>
<th>Local port (virtual machine host)</th>
<th>Remote port (the virtual machine)</th>
</tr>
<tr>
<td>SSH</td>
<td>2222</td>
<td>22</td>
</tr>
<tr>
<td>HTTP</td>
<td>8080</td>
<td>80</td>
</tr>
</table>
**Installed packages**:
* Apache2 with PHP enabled
* PHP with MySQL and PostgreSQL libraries
* MySQL server and client software
* PostgreSQL server and client software
* [Icinga prerequisites](http://docs.icinga.org/latest/en/quickstart-idoutils.html#installpackages)
* OpenLDAP servers and clients
**Installed users and groups**:
* User icinga with group icinga and icinga-cmd
* Webserver user added to group icinga-cmd
**Installed software**:
* Icinga with IDOUtils using a MySQL database
* Icinga with IDOUtils using a PostgreSQL database
* Icinga 2
**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` Monitoring Plugins for all Icinga instances
#### Icinga with IDOUtils using a MySQL database
**Installation path**: `/usr/local/icinga-mysql`
**Services**:
* `icinga-mysql`
* `ido2db-mysql`
Connect to the **icinga mysql database** using the following command:
mysql -u icinga -p icinga icinga
Access the **Classic UI** (CGIs) via [localhost:8080/icinga-mysql](http://localhost:8080/icinga-mysql).
For **logging into** the Icinga classic web interface use user *icingaadmin* with password *icinga*.
#### Icinga with IDOUtils using a PostgreSQL database
**Installation path**: `/usr/local/icinga-pgsql`
**Services**:
* `icinga-pgsql`
* `ido2db-pgsql`
Connect to the **icinga mysql database** using the following command:
sudo -u postgres psql -U icinga -d icinga
Access the **Classic UI** (CGIs) via [localhost:8080/icinga-pgsql](http://localhost:8080/icinga-pgsql).
For **logging into** the Icinga classic web interface use user *icingaadmin* with password *icinga*.
#### Monitoring Test Config
Test config is added to both the MySQL and PostgreSQL Icinga instance utilizing the Perl module
**Monitoring::Generator::TestConfig** to generate test config to **/usr/local/share/misc/monitoring_test_config**
which is then copied to **<instance>/etc/conf.d/test_config/**.
Configuration can be adjusted and recreated with **/usr/local/share/misc/monitoring_test_config/recreate.pl**.
**Note** that you have to run
vagrant provision
in the host after any modification to the script just mentioned.
#### MK Livestatus
MK Livestatus is added to the Icinga installation using a MySQL database.
**Installation path**:
* `/usr/local/icinga-mysql/bin/unixcat`
* `/usr/local/icinga-mysql/lib/mk-livestatus/livecheck`
* `/usr/local/icinga-mysql/lib/mk-livestatus/livestatus.o`
* `/usr/local/icinga-mysql/etc/modules/mk-livestatus.cfg`
* `/usr/local/icinga-mysql/var/rw/live`
**Example usage**:
echo "GET hosts" | /usr/local/icinga-mysql/bin/unixcat /usr/local/icinga-mysql/var/rw/live
#### LDAP example data
The environment includes a openldap server with example data. *Domain* suffix is **dc=icinga,dc=org**.
Administrator (*rootDN*) of the slapd configuration database is **cn=admin,cn=config** and the
administrator (*rootDN*) of our database instance is **cn=admin,dc=icinga,dc=org**. Both share
the *password* `admin`.
Examples to query the slapd configuration database:
ldapsearch -x -W -LLL -D cn=admin,cn=config -b cn=config dn
ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -b cn=config dn
Examples to query our database instance:
ldapsearch -x -W -LLL -D cn=admin,dc=icinga,dc=org -b dc=icinga,dc=org dn
ldapsearch -Y EXTERNAL -H ldapi:/// -LLL -b dc=icinga,dc=org dn
This is what the **dc=icinga,dc=org** *DIT* looks like:
> dn: dc=icinga,dc=org
>
> dn: ou=people,dc=icinga,dc=org
>
> dn: ou=groups,dc=icinga,dc=org
>
> dn: cn=Users,ou=groups,dc=icinga,dc=org
> cn: Users
> uniqueMember: cn=Jon Doe,ou=people,dc=icinga,dc=org
> uniqueMember: cn=Jane Smith,ou=people,dc=icinga,dc=org
> uniqueMember: cn=John Q. Public,ou=people,dc=icinga,dc=org
> uniqueMember: cn=Richard Roe,ou=people,dc=icinga,dc=org
>
> dn: cn=John Doe,ou=people,dc=icinga,dc=org
> cn: John Doe
> uid: jdoe
>
> dn: cn=Jane Smith,ou=people,dc=icinga,dc=org
> cn: Jane Smith
> uid: jsmith
>
> dn: cn=John Q. Public,ou=people,dc=icinga,dc=org
> cn: John Q. Public
> uid: jqpublic
>
> dn: cn=Richard Roe,ou=people,dc=icinga,dc=org
> cn: Richard Roe
> uid: rroe
All users share the password `password`.
#### Testing the code
All software required to run tests is installed in the virtual machine.
In order to run all tests you have to execute the following commands:
vagrant ssh -c /vagrant/test/php/runtests
vagrant ssh -c /vagrant/test/php/checkswag
vagrant ssh -c /vagrant/test/js/runtests
vagrant ssh -c /vagrant/test/js/checkswag
vagrant ssh -c /vagrant/test/frontend/runtests
`runtests` will execute unit and regression tests and `checkswag` will report
code style issues.
#### Icinga 2
Installed from the Icinga [snapshot package repository](http://packages.icinga.org/epel/).
The configuration is located in `/etc/icinga2`.
**Example usage**:
/etc/init.d/icinga2 (start|stop|restart|reload)
## Log into Icinga Web 2
If you've configure LDAP as authentication backend (which is the default) use the following login credentials:
> **Username**: jdoe
> **Password**: password
Have a look at [LDAP example data](#ldap example data) for more accounts.
Using MySQL as backend:
> **Username**: icingaadmin
> **Password**: icinga
If you come across problems at some time, the [community support channels](https://support.icinga.org/)
are good places to ask for advice from other users and give some in return.
For status updates check the [Icinga website](https://www.icinga.org/) and the
[Icinga Web 2 development roadmap](https://dev.icinga.org/projects/icingaweb2/roadmap).

1
Vagrantfile vendored
View File

@ -4,7 +4,6 @@
VAGRANTFILE_API_VERSION = "2"
VAGRANT_REQUIRED_VERSION = "1.5.0"
# Require 1.2.x at least
if ! defined? Vagrant.require_version
if Gem::Version.new(Vagrant::VERSION) < Gem::Version.new(VAGRANT_REQUIRED_VERSION)
puts "Vagrant >= " + VAGRANT_REQUIRED_VERSION + " required. Your version is " + Vagrant::VERSION

View File

@ -4,16 +4,17 @@
# namespace Icinga\Application\Controllers;
use Icinga\Authentication\Backend\AutoLoginBackend;
use Icinga\Web\Controller\ActionController;
use Icinga\Forms\Authentication\LoginForm;
use Icinga\Authentication\AuthChain;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Authentication\AuthChain;
use Icinga\Authentication\Backend\ExternalBackend;
use Icinga\Exception\AuthenticationException;
use Icinga\Exception\NotReadableError;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Forms\Authentication\LoginForm;
use Icinga\User;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Url;
/**
@ -33,10 +34,12 @@ class AuthenticationController extends ActionController
*/
public function loginAction()
{
if (@file_exists(Config::resolvePath('setup.token')) && !@file_exists(Config::resolvePath('config.ini'))) {
$icinga = Icinga::app();
if ($icinga->setupTokenExists() && $icinga->requiresSetup()) {
$this->redirectNow(Url::fromPath('setup'));
}
$triedOnlyExternalAuth = null;
$auth = $this->Auth();
$this->view->form = $form = new LoginForm();
$this->view->title = $this->translate('Icingaweb Login');
@ -80,7 +83,7 @@ class AuthenticationController extends ActionController
}
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
if ($backend instanceof ExternalBackend) {
continue;
}
++$backendsTried;
@ -124,7 +127,8 @@ class AuthenticationController extends ActionController
} elseif ($request->isGet()) {
$user = new User('');
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
$triedOnlyExternalAuth = $triedOnlyExternalAuth === null;
if ($backend instanceof ExternalBackend) {
$authenticated = $backend->authenticate($user);
if ($authenticated === true) {
$auth->setAuthenticated($user);
@ -132,6 +136,8 @@ class AuthenticationController extends ActionController
Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard'))
);
}
} else {
$triedOnlyExternalAuth = false;
}
}
}
@ -139,7 +145,8 @@ class AuthenticationController extends ActionController
$this->view->errorInfo = $e->getMessage();
}
$this->view->configMissing = is_dir(Config::$configDir) === false;
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated();
$this->view->requiresSetup = Icinga::app()->requiresSetup();
}
/**

View File

@ -7,7 +7,6 @@ use Icinga\Web\Url;
use Icinga\Web\Widget\Tab;
use Icinga\Application\Config;
use Icinga\Forms\PreferenceForm;
use Icinga\Exception\ConfigurationError;
use Icinga\User\Preferences\PreferencesStore;
/**
@ -40,14 +39,13 @@ class PreferenceController extends BasePreferenceController
public function indexAction()
{
$storeConfig = Config::app()->getSection('preferences');
if ($storeConfig->isEmpty()) {
throw new ConfigurationError(t('You need to configure how to store preferences first.'));
}
$user = $this->getRequest()->getUser();
$form = new PreferenceForm();
$form->setPreferences($user->getPreferences());
$form->setStore(PreferencesStore::create($storeConfig, $user));
if ($storeConfig->get('store', 'ini') !== 'none') {
$form->setStore(PreferencesStore::create($storeConfig, $user));
}
$form->handleRequest();
$this->view->form = $form;

View File

@ -0,0 +1,9 @@
# fontello-ifont font files moved
New target is: public/font
The font directory has been moved to the public structure because of
Internet Explorer version 8 compatibility. The common way for browsers is to
include the binary embeded font type in the javascript. IE8 falls back and
include one of the provided font sources. Therefore it is important to have
the font files available public and exported by the HTTP server.

View File

@ -18,7 +18,7 @@ class LoginForm extends Form
public function init()
{
$this->setName('form_login');
$this->setSubmitLabel(t('Login'));
$this->setSubmitLabel($this->translate('Login'));
}
/**
@ -31,8 +31,8 @@ class LoginForm extends Form
'username',
array(
'required' => true,
'label' => t('Username'),
'placeholder' => t('Please enter your username...'),
'label' => $this->translate('Username'),
'placeholder' => $this->translate('Please enter your username...'),
'class' => false === isset($formData['username']) ? 'autofocus' : ''
)
);
@ -41,8 +41,8 @@ class LoginForm extends Form
'password',
array(
'required' => true,
'label' => t('Password'),
'placeholder' => t('...and your password'),
'label' => $this->translate('Password'),
'placeholder' => $this->translate('...and your password'),
'class' => isset($formData['username']) ? 'autofocus' : ''
)
);

View File

@ -53,8 +53,8 @@ class DbBackendForm extends Form
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
)
@ -64,8 +64,10 @@ class DbBackendForm extends Form
'resource',
array(
'required' => true,
'label' => t('Database Connection'),
'description' => t('The database connection to use for authenticating with this provider'),
'label' => $this->translate('Database Connection'),
'description' => $this->translate(
'The database connection to use for authenticating with this provider'
),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
@ -107,11 +109,11 @@ class DbBackendForm extends Form
try {
$dbUserBackend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig()));
if ($dbUserBackend->count() < 1) {
$form->addError(t('No users found under the specified database backend'));
$form->addError($form->translate('No users found under the specified database backend'));
return false;
}
} catch (Exception $e) {
$form->addError(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
$form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $e->getMessage()));
return false;
}

View File

@ -8,16 +8,16 @@ use Zend_Validate_Callback;
use Icinga\Web\Form;
/**
* Form class for adding/modifying autologin authentication backends
* Form class for adding/modifying authentication backends of type "external"
*/
class AutologinBackendForm extends Form
class ExternalBackendForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_authbackend_autologin');
$this->setName('form_config_authbackend_external');
}
/**
@ -30,8 +30,8 @@ class AutologinBackendForm extends Form
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
@ -52,9 +52,11 @@ class AutologinBackendForm extends Form
'text',
'strip_username_regexp',
array(
'label' => t('Filter Pattern'),
'description' => t('The regular expression to use to strip specific parts off from usernames. Leave empty if you do not want to strip off anything'),
'value' => '/\@[^$]+$/',
'label' => $this->translate('Filter Pattern'),
'description' => $this->translate(
'The regular expression to use to strip specific parts off from usernames.'
. ' Leave empty if you do not want to strip off anything'
),
'validators' => array(
new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;
@ -67,7 +69,7 @@ class AutologinBackendForm extends Form
'backend',
array(
'disabled' => true,
'value' => 'autologin'
'value' => 'external'
)
);
@ -77,7 +79,7 @@ class AutologinBackendForm extends Form
/**
* Validate the configuration by creating a backend and requesting the user count
*
* Returns always true as autologin backends are just "passive" backends. (The webserver authenticates users.)
* Returns always true as backends of type "external" are just "passive" backends.
*
* @param Form $form The form to fetch the configuration values from
*

View File

@ -54,8 +54,8 @@ class LdapBackendForm extends Form
'name',
array(
'required' => true,
'label' => t('Backend Name'),
'description' => t(
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
)
)
@ -65,8 +65,8 @@ class LdapBackendForm extends Form
'resource',
array(
'required' => true,
'label' => t('LDAP Resource'),
'description' => t('The resource to use for authenticating with this provider'),
'label' => $this->translate('LDAP Resource'),
'description' => $this->translate('The resource to use for authenticating with this provider'),
'multiOptions' => false === empty($this->resources)
? array_combine($this->resources, $this->resources)
: array()
@ -77,8 +77,8 @@ class LdapBackendForm extends Form
'user_class',
array(
'required' => true,
'label' => t('LDAP User Object Class'),
'description' => t('The object class used for storing users on the ldap server'),
'label' => $this->translate('LDAP User Object Class'),
'description' => $this->translate('The object class used for storing users on the ldap server'),
'value' => 'inetOrgPerson'
)
);
@ -87,8 +87,10 @@ class LdapBackendForm extends Form
'user_name_attribute',
array(
'required' => true,
'label' => t('LDAP User Name Attribute'),
'description' => t('The attribute name used for storing the user name on the ldap server'),
'label' => $this->translate('LDAP User Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing the user name on the ldap server'
),
'value' => 'uid'
)
);
@ -105,9 +107,11 @@ class LdapBackendForm extends Form
'base_dn',
array(
'required' => false,
'label' => t('Base DN'),
'description' => t('The path where users can be found on the ldap server. ' .
' Leave empty to select all users available on the specified resource.')
'label' => $this->translate('Base DN'),
'description' => $this->translate(
'The path where users can be found on the ldap server. Leave ' .
'empty to select all users available on the specified resource.'
)
)
);
return $this;
@ -146,7 +150,7 @@ class LdapBackendForm extends Form
$form->addError($e->getMessage());
return false;
} catch (Exception $e) {
$form->addError(sprintf(t('Unable to validate authentication: %s'), $e->getMessage()));
$form->addError(sprintf($form->translate('Unable to validate authentication: %s'), $e->getMessage()));
return false;
}

View File

@ -14,7 +14,7 @@ use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Forms\Config\Authentication\DbBackendForm;
use Icinga\Forms\Config\Authentication\LdapBackendForm;
use Icinga\Forms\Config\Authentication\AutologinBackendForm;
use Icinga\Forms\Config\Authentication\ExternalBackendForm;
class AuthenticationBackendConfigForm extends ConfigForm
{
@ -31,7 +31,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_authbackend');
$this->setSubmitLabel(t('Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
@ -67,10 +67,10 @@ class AuthenticationBackendConfigForm extends ConfigForm
} elseif ($type === 'ldap') {
$form = new LdapBackendForm();
$form->setResources(isset($this->resources['ldap']) ? $this->resources['ldap'] : array());
} elseif ($type === 'autologin') {
$form = new AutologinBackendForm();
} elseif ($type === 'external') {
$form = new ExternalBackendForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid backend type "%s" provided'), $type));
throw new InvalidArgumentException(sprintf($this->translate('Invalid backend type "%s" provided'), $type));
}
return $form;
@ -91,9 +91,9 @@ class AuthenticationBackendConfigForm extends ConfigForm
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
throw new InvalidArgumentException($this->translate('Authentication backend name missing'));
} elseif ($this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Authentication backend already exists'));
throw new InvalidArgumentException($this->translate('Authentication backend already exists'));
}
unset($values['name']);
@ -114,11 +114,11 @@ class AuthenticationBackendConfigForm extends ConfigForm
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old authentication backend name missing'));
throw new InvalidArgumentException($this->translate('Old authentication backend name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New authentication backend name missing'));
throw new InvalidArgumentException($this->translate('New authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
throw new InvalidArgumentException($this->translate('Unknown authentication backend provided'));
}
$backendConfig = $this->config->getSection($name);
@ -144,9 +144,9 @@ class AuthenticationBackendConfigForm extends ConfigForm
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
throw new InvalidArgumentException($this->translate('Authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
throw new InvalidArgumentException($this->translate('Unknown authentication backend provided'));
}
$backendConfig = $this->config->getSection($name);
@ -167,9 +167,9 @@ class AuthenticationBackendConfigForm extends ConfigForm
public function move($name, $position)
{
if (! $name) {
throw new InvalidArgumentException(t('Authentication backend name missing'));
throw new InvalidArgumentException($this->translate('Authentication backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown authentication backend provided'));
throw new InvalidArgumentException($this->translate('Unknown authentication backend provided'));
}
$backendOrder = $this->config->keys();
@ -208,10 +208,10 @@ class AuthenticationBackendConfigForm extends ConfigForm
try {
if ($authBackend === null) { // create new backend
$this->add($this->getValues());
$message = t('Authentication backend "%s" has been successfully created');
$message = $this->translate('Authentication backend "%s" has been successfully created');
} else { // edit existing backend
$this->edit($authBackend, $this->getValues());
$message = t('Authentication backend "%s" has been successfully changed');
$message = $this->translate('Authentication backend "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
@ -237,11 +237,13 @@ class AuthenticationBackendConfigForm extends ConfigForm
$authBackend = $this->request->getQuery('auth_backend');
if ($authBackend !== null) {
if ($authBackend === '') {
throw new ConfigurationError(t('Authentication backend name missing'));
throw new ConfigurationError($this->translate('Authentication backend name missing'));
} elseif (! $this->config->hasSection($authBackend)) {
throw new ConfigurationError(t('Unknown authentication backend provided'));
throw new ConfigurationError($this->translate('Unknown authentication backend provided'));
} elseif ($this->config->getSection($authBackend)->backend === null) {
throw new ConfigurationError(sprintf(t('Backend "%s" has no `backend\' setting'), $authBackend));
throw new ConfigurationError(
sprintf($this->translate('Backend "%s" has no `backend\' setting'), $authBackend)
);
}
$configValues = $this->config->getSection($authBackend)->toArray();
@ -249,15 +251,15 @@ class AuthenticationBackendConfigForm extends ConfigForm
$configValues['name'] = $authBackend;
$this->populate($configValues);
} elseif (empty($this->resources)) {
$autologinBackends = array_filter(
$externalBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'external';
}
);
if (false === empty($autologinBackends)) {
throw new ConfigurationError(t('Could not find any resources for authentication'));
if (false === empty($externalBackends)) {
throw new ConfigurationError($this->translate('Could not find any resources for authentication'));
}
}
}
@ -276,8 +278,8 @@ class AuthenticationBackendConfigForm extends ConfigForm
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
'label' => $this->translate('Force Changes'),
'description' => $this->translate('Check this box to enforce changes without connectivity validation')
)
);
}
@ -291,20 +293,20 @@ class AuthenticationBackendConfigForm extends ConfigForm
$backendType = isset($formData['type']) ? $formData['type'] : null;
if (isset($this->resources['db'])) {
$backendTypes['db'] = t('Database');
$backendTypes['db'] = $this->translate('Database');
}
if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) {
$backendTypes['ldap'] = 'LDAP';
}
$autologinBackends = array_filter(
$externalBackends = array_filter(
$this->config->toArray(),
function ($authBackendCfg) {
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'autologin';
return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'external';
}
);
if ($backendType === 'autologin' || empty($autologinBackends)) {
$backendTypes['autologin'] = t('Autologin');
if ($backendType === 'external' || empty($externalBackends)) {
$backendTypes['external'] = $this->translate('External');
}
if ($backendType === null) {
@ -318,8 +320,10 @@ class AuthenticationBackendConfigForm extends ConfigForm
'ignore' => true,
'required' => true,
'autosubmit' => true,
'label' => t('Backend Type'),
'description' => t('The type of the resource to use for this authenticaton provider'),
'label' => $this->translate('Backend Type'),
'description' => $this->translate(
'The type of the resource to use for this authenticaton provider'
),
'multiOptions' => $backendTypes
)
);

View File

@ -52,7 +52,7 @@ class AuthenticationBackendReorderForm extends ConfigForm
try {
if ($configForm->move($backendName, $position)->save()) {
Notification::success(t('Authentication order updated!'));
Notification::success($this->translate('Authentication order updated!'));
} else {
return false;
}

View File

@ -4,10 +4,8 @@
namespace Icinga\Forms\Config\General;
use DateTimeZone;
use Icinga\Application\Icinga;
use Icinga\Data\ResourceFactory;
use Icinga\Util\Translator;
use Icinga\Web\Form;
@ -33,10 +31,10 @@ class ApplicationConfigForm extends Form
'text',
'global_module_path',
array(
'label' => t('Module Path'),
'label' => $this->translate('Module Path'),
'required' => true,
'value' => implode(':', Icinga::app()->getModuleManager()->getModuleDirs()),
'description' => t(
'description' => $this->translate(
'Contains the directories that will be searched for available modules, separated by '
. 'colons. Modules that don\'t exist in these directories can still be symlinked in '
. 'the module folder, but won\'t show up in the list of disabled modules.'
@ -46,19 +44,19 @@ class ApplicationConfigForm extends Form
$this->addElement(
'select',
'preferences_type',
'preferences_store',
array(
'required' => true,
'autosubmit' => true,
'label' => t('User Preference Storage Type'),
'label' => $this->translate('User Preference Storage Type'),
'multiOptions' => array(
'ini' => t('File System (INI Files)'),
'db' => t('Database'),
'null' => t('Don\'t Store Preferences')
'ini' => $this->translate('File System (INI Files)'),
'db' => $this->translate('Database'),
'none' => $this->translate('Don\'t Store Preferences')
)
)
);
if (isset($formData['preferences_type']) && $formData['preferences_type'] === 'db') {
if (isset($formData['preferences_store']) && $formData['preferences_store'] === 'db') {
$backends = array();
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
if ($resource['type'] === 'db') {
@ -72,7 +70,7 @@ class ApplicationConfigForm extends Form
array(
'required' => true,
'multiOptions' => $backends,
'label' => t('Database Connection')
'label' => $this->translate('Database Connection')
)
);
}

View File

@ -4,7 +4,6 @@
namespace Icinga\Forms\Config\General;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\WritablePathValidator;
@ -31,12 +30,12 @@ class LoggingConfigForm extends Form
array(
'required' => true,
'autosubmit' => true,
'label' => t('Logging Type'),
'description' => t('The type of logging to utilize.'),
'label' => $this->translate('Logging Type'),
'description' => $this->translate('The type of logging to utilize.'),
'multiOptions' => array(
'syslog' => 'Syslog',
'file' => t('File', 'app.config.logging.type'),
'none' => t('None', 'app.config.logging.type')
'file' => $this->translate('File', 'app.config.logging.type'),
'none' => $this->translate('None', 'app.config.logging.type')
)
)
);
@ -47,13 +46,13 @@ class LoggingConfigForm extends Form
'logging_level',
array(
'required' => true,
'label' => t('Logging Level'),
'description' => t('The maximum logging level to emit.'),
'label' => $this->translate('Logging Level'),
'description' => $this->translate('The maximum logging level to emit.'),
'multiOptions' => array(
Logger::$levels[Logger::ERROR] => t('Error', 'app.config.logging.level'),
Logger::$levels[Logger::WARNING] => t('Warning', 'app.config.logging.level'),
Logger::$levels[Logger::INFO] => t('Information', 'app.config.logging.level'),
Logger::$levels[Logger::DEBUG] => t('Debug', 'app.config.logging.level')
Logger::$levels[Logger::ERROR] => $this->translate('Error', 'app.config.logging.level'),
Logger::$levels[Logger::WARNING] => $this->translate('Warning', 'app.config.logging.level'),
Logger::$levels[Logger::INFO] => $this->translate('Information', 'app.config.logging.level'),
Logger::$levels[Logger::DEBUG] => $this->translate('Debug', 'app.config.logging.level')
)
)
);
@ -65,9 +64,11 @@ class LoggingConfigForm extends Form
'logging_application',
array(
'required' => true,
'label' => t('Application Prefix'),
'description' => t('The name of the application by which to prefix syslog messages.'),
'value' => 'icingaweb',
'label' => $this->translate('Application Prefix'),
'description' => $this->translate(
'The name of the application by which to prefix syslog messages.'
),
'value' => 'icingaweb2',
'validators' => array(
array(
'Regex',
@ -91,8 +92,8 @@ class LoggingConfigForm extends Form
// 'logging_facility',
// array(
// 'required' => true,
// 'label' => t('Facility'),
// 'description' => t('The syslog facility to utilize.'),
// 'label' => $this->translate('Facility'),
// 'description' => $this->translate('The syslog facility to utilize.'),
// 'multiOptions' => array(
// 'user' => 'LOG_USER'
// )
@ -104,9 +105,9 @@ class LoggingConfigForm extends Form
'logging_file',
array(
'required' => true,
'label' => t('File path'),
'description' => t('The full path to the log file to write messages to.'),
'value' => $this->getDefaultLogDir(),
'label' => $this->translate('File path'),
'description' => $this->translate('The full path to the log file to write messages to.'),
'value' => '/var/log/icingaweb2/icingaweb2.log',
'validators' => array(new WritablePathValidator())
)
);
@ -114,14 +115,4 @@ class LoggingConfigForm extends Form
return $this;
}
/**
* Return the default logging directory for type 'file'
*
* @return string
*/
protected function getDefaultLogDir()
{
return realpath(Icinga::app()->getApplicationDir('../var/log/icingaweb.log'));
}
}

View File

@ -20,7 +20,7 @@ class GeneralConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_general');
$this->setSubmitLabel(t('Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
@ -52,7 +52,7 @@ class GeneralConfigForm extends ConfigForm
}
if ($this->save()) {
Notification::success(t('New configuration has successfully been stored'));
Notification::success($this->translate('New configuration has successfully been stored'));
} else {
return false;
}

View File

@ -41,8 +41,8 @@ class DbResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -50,8 +50,8 @@ class DbResourceForm extends Form
'db',
array(
'required' => true,
'label' => t('Database Type'),
'description' => t('The type of SQL database'),
'label' => $this->translate('Database Type'),
'description' => $this->translate('The type of SQL database'),
'multiOptions' => $dbChoices
)
);
@ -60,8 +60,8 @@ class DbResourceForm extends Form
'host',
array (
'required' => true,
'label' => t('Host'),
'description' => t('The hostname of the database'),
'label' => $this->translate('Host'),
'description' => $this->translate('The hostname of the database'),
'value' => 'localhost'
)
);
@ -70,8 +70,8 @@ class DbResourceForm extends Form
'port',
array(
'required' => true,
'label' => t('Port'),
'description' => t('The port to use'),
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => 3306
)
);
@ -80,8 +80,8 @@ class DbResourceForm extends Form
'dbname',
array(
'required' => true,
'label' => t('Database Name'),
'description' => t('The name of the database to use')
'label' => $this->translate('Database Name'),
'description' => $this->translate('The name of the database to use')
)
);
$this->addElement(
@ -89,8 +89,8 @@ class DbResourceForm extends Form
'username',
array (
'required' => true,
'label' => t('Username'),
'description' => t('The user name to use for authentication')
'label' => $this->translate('Username'),
'description' => $this->translate('The user name to use for authentication')
)
);
$this->addElement(
@ -99,8 +99,8 @@ class DbResourceForm extends Form
array(
'required' => true,
'renderPassword' => true,
'label' => t('Password'),
'description' => t('The password to use for authentication')
'label' => $this->translate('Password'),
'description' => $this->translate('The password to use for authentication')
)
);
@ -132,7 +132,9 @@ class DbResourceForm extends Form
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
$resource->getConnection()->getConnection();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
$form->addError(
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
);
return false;
}

View File

@ -30,8 +30,8 @@ class FileResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -39,8 +39,8 @@ class FileResourceForm extends Form
'filename',
array(
'required' => true,
'label' => t('Filepath'),
'description' => t('The filename to fetch information from'),
'label' => $this->translate('Filepath'),
'description' => $this->translate('The filename to fetch information from'),
'validators' => array(new ReadablePathValidator())
)
);
@ -49,8 +49,8 @@ class FileResourceForm extends Form
'fields',
array(
'required' => true,
'label' => t('Pattern'),
'description' => t('The regular expression by which to identify columns')
'label' => $this->translate('Pattern'),
'description' => $this->translate('The regular expression by which to identify columns')
)
);

View File

@ -32,8 +32,8 @@ class LdapResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -41,8 +41,10 @@ class LdapResourceForm extends Form
'hostname',
array(
'required' => true,
'label' => t('Host'),
'description' => t('The hostname or address of the LDAP server to use for authentication'),
'label' => $this->translate('Host'),
'description' => $this->translate(
'The hostname or address of the LDAP server to use for authentication'
),
'value' => 'localhost'
)
);
@ -51,8 +53,8 @@ class LdapResourceForm extends Form
'port',
array(
'required' => true,
'label' => t('Port'),
'description' => t('The port of the LDAP server to use for authentication'),
'label' => $this->translate('Port'),
'description' => $this->translate('The port of the LDAP server to use for authentication'),
'value' => 389
)
);
@ -61,8 +63,10 @@ class LdapResourceForm extends Form
'root_dn',
array(
'required' => true,
'label' => t('Root DN'),
'description' => t('Only the root and its child nodes will be accessible on this resource.')
'label' => $this->translate('Root DN'),
'description' => $this->translate(
'Only the root and its child nodes will be accessible on this resource.'
)
)
);
$this->addElement(
@ -70,8 +74,8 @@ class LdapResourceForm extends Form
'bind_dn',
array(
'required' => true,
'label' => t('Bind DN'),
'description' => t('The user dn to use for querying the ldap server')
'label' => $this->translate('Bind DN'),
'description' => $this->translate('The user dn to use for querying the ldap server')
)
);
$this->addElement(
@ -80,8 +84,8 @@ class LdapResourceForm extends Form
array(
'required' => true,
'renderPassword' => true,
'label' => t('Bind Password'),
'description' => t('The password to use for querying the ldap server')
'label' => $this->translate('Bind Password'),
'description' => $this->translate('The password to use for querying the ldap server')
)
);
@ -119,7 +123,9 @@ class LdapResourceForm extends Form
throw new Exception();
}
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
$form->addError(
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
);
return false;
}

View File

@ -33,8 +33,8 @@ class LivestatusResourceForm extends Form
'name',
array(
'required' => true,
'label' => t('Resource Name'),
'description' => t('The unique name of this resource')
'label' => $this->translate('Resource Name'),
'description' => $this->translate('The unique name of this resource')
)
);
$this->addElement(
@ -42,9 +42,9 @@ class LivestatusResourceForm extends Form
'socket',
array(
'required' => true,
'label' => t('Socket'),
'description' => t('The path to your livestatus socket used for querying monitoring data'),
'value' => realpath(Icinga::app()->getApplicationDir() . '/../var/rw/livestatus')
'label' => $this->translate('Socket'),
'description' => $this->translate('The path to your livestatus socket used for querying monitoring data'),
'value' => '/var/run/icinga2/cmd/livestatus'
)
);
@ -75,8 +75,10 @@ class LivestatusResourceForm extends Form
try {
$resource = ResourceFactory::createResource(new ConfigObject($form->getValues()));
$resource->connect()->disconnect();
} catch (Exception $e) {
$form->addError(t('Connectivity validation failed, connection to the given resource not possible.'));
} catch (Exception $_) {
$form->addError(
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
);
return false;
}

View File

@ -22,7 +22,7 @@ class ResourceConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_resource');
$this->setSubmitLabel(t('Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
@ -43,7 +43,7 @@ class ResourceConfigForm extends ConfigForm
} elseif ($type === 'file') {
return new FileResourceForm();
} else {
throw new InvalidArgumentException(sprintf(t('Invalid resource type "%s" provided'), $type));
throw new InvalidArgumentException(sprintf($this->translate('Invalid resource type "%s" provided'), $type));
}
}
@ -62,9 +62,9 @@ class ResourceConfigForm extends ConfigForm
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
throw new InvalidArgumentException($this->translate('Resource name missing'));
} elseif ($this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Resource already exists'));
throw new InvalidArgumentException($this->translate('Resource already exists'));
}
unset($values['name']);
@ -85,11 +85,11 @@ class ResourceConfigForm extends ConfigForm
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(t('Old resource name missing'));
throw new InvalidArgumentException($this->translate('Old resource name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(t('New resource name missing'));
throw new InvalidArgumentException($this->translate('New resource name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown resource provided'));
throw new InvalidArgumentException($this->translate('Unknown resource provided'));
}
$resourceConfig = $this->config->getSection($name);
@ -111,9 +111,9 @@ class ResourceConfigForm extends ConfigForm
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(t('Resource name missing'));
throw new InvalidArgumentException($this->translate('Resource name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(t('Unknown resource provided'));
throw new InvalidArgumentException($this->translate('Unknown resource provided'));
}
$resourceConfig = $this->config->getSection($name);
@ -143,10 +143,10 @@ class ResourceConfigForm extends ConfigForm
try {
if ($resource === null) { // create new resource
$this->add($this->getValues());
$message = t('Resource "%s" has been successfully created');
$message = $this->translate('Resource "%s" has been successfully created');
} else { // edit existing resource
$this->edit($resource, $this->getValues());
$message = t('Resource "%s" has been successfully changed');
$message = $this->translate('Resource "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
@ -172,9 +172,9 @@ class ResourceConfigForm extends ConfigForm
$resource = $this->request->getQuery('resource');
if ($resource !== null) {
if ($resource === '') {
throw new ConfigurationError(t('Resource name missing'));
throw new ConfigurationError($this->translate('Resource name missing'));
} elseif (! $this->config->hasSection($resource)) {
throw new ConfigurationError(t('Unknown resource provided'));
throw new ConfigurationError($this->translate('Unknown resource provided'));
}
$configValues = $this->config->getSection($resource)->toArray();
@ -197,8 +197,8 @@ class ResourceConfigForm extends ConfigForm
array(
'order' => 0,
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
'label' => $this->translate('Force Changes'),
'description' => $this->translate('Check this box to enforce changes without connectivity validation')
)
);
}
@ -211,14 +211,14 @@ class ResourceConfigForm extends ConfigForm
$resourceType = isset($formData['type']) ? $formData['type'] : 'db';
$resourceTypes = array(
'file' => t('File'),
'file' => $this->translate('File'),
'livestatus' => 'Livestatus',
);
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
$resourceTypes['ldap'] = 'LDAP';
}
if ($resourceType === 'db' || Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
$resourceTypes['db'] = t('SQL Database');
$resourceTypes['db'] = $this->translate('SQL Database');
}
$this->addElement(
@ -227,8 +227,8 @@ class ResourceConfigForm extends ConfigForm
array(
'required' => true,
'autosubmit' => true,
'label' => t('Resource Type'),
'description' => t('The type of resource'),
'label' => $this->translate('Resource Type'),
'description' => $this->translate('The type of resource'),
'multiOptions' => $resourceTypes,
'value' => $resourceType
)

View File

@ -17,6 +17,6 @@ class ConfirmRemovalForm extends Form
public function init()
{
$this->setName('form_confirm_removal');
$this->setSubmitLabel(t('Confirm Removal'));
$this->setSubmitLabel($this->translate('Confirm Removal'));
}
}

View File

@ -26,7 +26,7 @@ class DashletForm extends Form
{
$this->setName('form_dashboard_addurl');
if (! $this->getSubmitLabel()) {
$this->setSubmitLabel(t('Add To Dashboard'));
$this->setSubmitLabel($this->translate('Add To Dashboard'));
}
$this->setAction(URL::fromRequest());
}
@ -66,9 +66,10 @@ class DashletForm extends Form
'url',
array(
'required' => true,
'label' => t('Url'),
'description' =>
t('Enter url being loaded in the dashlet. You can paste the full URL, including filters.')
'label' => $this->translate('Url'),
'description' => $this->translate(
'Enter url being loaded in the dashlet. You can paste the full URL, including filters.'
)
)
);
$this->addElement(
@ -76,8 +77,8 @@ class DashletForm extends Form
'dashlet',
array(
'required' => true,
'label' => t('Dashlet Title'),
'description' => t('Enter a title for the dashlet.')
'label' => $this->translate('Dashlet Title'),
'description' => $this->translate('Enter a title for the dashlet.')
)
);
$this->addElement(
@ -95,9 +96,8 @@ class DashletForm extends Form
'pane',
array(
'required' => true,
'label' => t("New Dashboard Title"),
'description' =>
t('Enter a title for the new pane.')
'label' => $this->translate("New Dashboard Title"),
'description' => $this->translate('Enter a title for the new pane.')
)
);
} else {
@ -106,10 +106,9 @@ class DashletForm extends Form
'pane',
array(
'required' => true,
'label' => t('Dashboard'),
'label' => $this->translate('Dashboard'),
'multiOptions' => $panes,
'description' =>
t('Select a pane you want to add the dashlet.')
'description' => $this->translate('Select a pane you want to add the dashlet.')
)
);
}
@ -119,9 +118,9 @@ class DashletForm extends Form
'create_new_pane',
array(
'required' => false,
'label' => t('New dashboard'),
'label' => $this->translate('New dashboard'),
'class' => 'autosubmit',
'description' => t('Check this box if you want to add the dashlet to a new dashboard')
'description' => $this->translate('Check this box if you want to add the dashlet to a new dashboard')
)
);
}

View File

@ -26,8 +26,8 @@ class LdapDiscoveryForm extends Form
'domain',
array(
'required' => true,
'label' => t('Search Domain'),
'description' => t('Search this domain for records of available servers.'),
'label' => $this->translate('Search Domain'),
'description' => $this->translate('Search this domain for records of available servers.'),
)
);
@ -36,7 +36,7 @@ class LdapDiscoveryForm extends Form
'note',
'additional_description',
array(
'value' => t('No Ldap servers found on this domain.'
'value' => $this->translate('No Ldap servers found on this domain.'
. ' You can try to specify host and port and try again, or just skip this step and '
. 'configure the server manually.'
)
@ -47,8 +47,8 @@ class LdapDiscoveryForm extends Form
'hostname',
array(
'required' => false,
'label' => t('Host'),
'description' => t('IP or host name to search.'),
'label' => $this->translate('Host'),
'description' => $this->translate('IP or hostname to search.'),
)
);
@ -57,8 +57,8 @@ class LdapDiscoveryForm extends Form
'port',
array(
'required' => false,
'label' => t('Port'),
'description' => t('Port', 389),
'label' => $this->translate('Port'),
'description' => $this->translate('Port', 389),
)
);
}

View File

@ -86,7 +86,7 @@ class PreferenceForm extends Form
*/
public function onSuccess()
{
$this->preferences = new Preferences($this->store->load());
$this->preferences = new Preferences($this->store ? $this->store->load() : array());
$webPreferences = $this->preferences->get('icingaweb', array());
foreach ($this->getValues() as $key => $value) {
@ -103,11 +103,11 @@ class PreferenceForm extends Form
Session::getSession()->user->setPreferences($this->preferences);
try {
if ($this->getElement('btn_submit_preferences')->isChecked()) {
if ($this->store && $this->getElement('btn_submit_preferences')->isChecked()) {
$this->save();
Notification::success(t('Preferences successfully saved'));
Notification::success($this->translate('Preferences successfully saved'));
} else {
Notification::success(t('Preferences successfully saved for the current session'));
Notification::success($this->translate('Preferences successfully saved for the current session'));
}
} catch (Exception $e) {
Logger::error($e);
@ -142,13 +142,13 @@ class PreferenceForm extends Form
public function createElements(array $formData)
{
$languages = array();
$languages['autodetect'] = sprintf(t('Browser (%s)', 'preferences.form'), $this->getLocale());
$languages['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getLocale());
foreach (Translator::getAvailableLocaleCodes() as $language) {
$languages[$language] = $language;
}
$tzList = array();
$tzList['autodetect'] = sprintf(t('Browser (%s)', 'preferences.form'), $this->getDefaultTimezone());
$tzList['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getDefaultTimezone());
foreach (DateTimeZone::listIdentifiers() as $tz) {
$tzList[$tz] = $tz;
}
@ -158,8 +158,8 @@ class PreferenceForm extends Form
'language',
array(
'required' => true,
'label' => t('Your Current Language'),
'description' => t('Use the following language to display texts and messages'),
'label' => $this->translate('Your Current Language'),
'description' => $this->translate('Use the following language to display texts and messages'),
'multiOptions' => $languages,
'value' => substr(setlocale(LC_ALL, 0), 0, 5)
)
@ -170,8 +170,8 @@ class PreferenceForm extends Form
'timezone',
array(
'required' => true,
'label' => t('Your Current Timezone'),
'description' => t('Use the following timezone for dates and times'),
'label' => $this->translate('Your Current Timezone'),
'description' => $this->translate('Use the following timezone for dates and times'),
'multiOptions' => $tzList,
'value' => $this->getDefaultTimezone()
)
@ -182,29 +182,31 @@ class PreferenceForm extends Form
'show_benchmark',
array(
'required' => true,
'label' => t('Use benchmark')
'label' => $this->translate('Use benchmark')
)
);
$this->addElement(
'submit',
'btn_submit_preferences',
array(
'ignore' => true,
'label' => t('Save to the Preferences'),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))
if ($this->store) {
$this->addElement(
'submit',
'btn_submit_preferences',
array(
'ignore' => true,
'label' => $this->translate('Save to the Preferences'),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))
)
)
)
);
);
}
$this->addElement(
'submit',
'btn_submit_session',
array(
'ignore' => true,
'label' => t('Save for the current Session'),
'label' => $this->translate('Save for the current Session'),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))

View File

@ -6,6 +6,7 @@ namespace Icinga\Forms\Security;
use InvalidArgumentException;
use LogicException;
use Zend_Form_Element;
use Icinga\Application\Icinga;
use Icinga\Forms\ConfigForm;
use Icinga\Util\String;
@ -18,14 +19,14 @@ class RoleForm extends ConfigForm
/**
* Provided permissions by currently loaded modules
*
* @var array
* @type array
*/
protected $providedPermissions = array();
protected $providedPermissions = array('*' => '*');
/**
* Provided restrictions by currently loaded modules
*
* @var array
* @type array
*/
protected $providedRestrictions = array();
@ -35,14 +36,26 @@ class RoleForm extends ConfigForm
*/
public function init()
{
$helper = new Zend_Form_Element('bogus');
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
foreach ($module->getProvidedPermissions() as $permission) {
/** @var object $permission */
/** @type object $permission */
$this->providedPermissions[$permission->name] = $permission->name . ': ' . $permission->description;
}
foreach ($module->getProvidedRestrictions() as $restriction) {
/** @var object $restriction */
$this->providedRestrictions[$restriction->name] = $restriction->description;
/** @type object $restriction */
$name = $helper->filterName($restriction->name); // Zend only permits alphanumerics, the underscore,
// the circumflex and any ASCII character in range
// \x7f to \xff (127 to 255)
while (isset($this->providedRestrictions[$name])) {
// Because Zend_Form_Element::filterName() replaces any not permitted character with the empty
// string we may have duplicate names, e.g. 're/striction' and 'restriction'
$name .= '_';
}
$this->providedRestrictions[$name] = array(
'description' => $restriction->description,
'name' => $restriction->name
);
}
}
}
@ -59,8 +72,8 @@ class RoleForm extends ConfigForm
'name',
array(
'required' => true,
'label' => t('Role Name'),
'description' => t('The name of the role'),
'label' => $this->translate('Role Name'),
'description' => $this->translate('The name of the role'),
'ignore' => true
),
),
@ -68,35 +81,37 @@ class RoleForm extends ConfigForm
'textarea',
'users',
array(
'label' => t('Users'),
'description' => t('Comma-separated list of users that are assigned to the role')
'label' => $this->translate('Users'),
'description' => $this->translate('Comma-separated list of users that are assigned to the role')
),
),
array(
'textarea',
'groups',
array(
'label' => t('Groups'),
'description' => t('Comma-separated list of groups that are assigned to the role')
'label' => $this->translate('Groups'),
'description' => $this->translate('Comma-separated list of groups that are assigned to the role')
),
),
array(
'multiselect',
'permissions',
array(
'label' => t('Permissions Set'),
'description' => t('The permissions to grant. You may select more than one permission'),
'label' => $this->translate('Permissions Set'),
'description' => $this->translate(
'The permissions to grant. You may select more than one permission'
),
'multiOptions' => $this->providedPermissions
)
)
));
foreach ($this->providedRestrictions as $name => $description) {
foreach ($this->providedRestrictions as $name => $spec) {
$this->addElement(
'text',
$name,
array(
'label' => $name,
'description' => $description
'label' => $spec['name'],
'description' => $spec['description']
)
);
}
@ -120,7 +135,7 @@ class RoleForm extends ConfigForm
}
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
t('Can\'t load role \'%s\'. Role does not exist'),
$this->translate('Can\'t load role \'%s\'. Role does not exist'),
$name
));
}
@ -129,6 +144,15 @@ class RoleForm extends ConfigForm
? String::trimSplit($role['permissions'])
: null;
$role['name'] = $name;
$restrictions = array();
foreach ($this->providedRestrictions as $name => $spec) {
if (isset($role[$spec['name']])) {
// Translate restriction names to filtered element names
$restrictions[$name] = $role[$spec['name']];
unset($role[$spec['name']]);
}
}
$role = array_merge($role, $restrictions);
$this->populate($role);
return $this;
}
@ -152,7 +176,7 @@ class RoleForm extends ConfigForm
}
if ($this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
t('Can\'t add role \'%s\'. Role already exists'),
$this->translate('Can\'t add role \'%s\'. Role already exists'),
$name
));
}
@ -178,7 +202,7 @@ class RoleForm extends ConfigForm
}
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
t('Can\'t remove role \'%s\'. Role does not exist'),
$this->translate('Can\'t remove role \'%s\'. Role does not exist'),
$name
));
}
@ -211,7 +235,7 @@ class RoleForm extends ConfigForm
} else {
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
t('Can\'t update role \'%s\'. Role does not exist'),
$this->translate('Can\'t update role \'%s\'. Role does not exist'),
$name
));
}
@ -230,6 +254,15 @@ class RoleForm extends ConfigForm
if (isset($values['permissions'])) {
$values['permissions'] = implode(', ', $values['permissions']);
}
$restrictions = array();
foreach ($this->providedRestrictions as $name => $spec) {
if (isset($values[$name])) {
// Translate filtered element names to restriction names
$restrictions[$spec['name']] = $values[$name];
unset($values[$name]);
}
}
$values = array_merge($values, $restrictions);
return $values;
}
}

View File

@ -3,7 +3,6 @@
use Icinga\Web\Url;
use Icinga\Web\Menu;
use Icinga\Web\MenuRenderer;
use Icinga\Web\Widget\SearchDashboard;
// Don't render a menu for unauthenticated users unless menu is auth aware
if (! $this->auth()->isAuthenticated()) {
@ -11,15 +10,16 @@ if (! $this->auth()->isAuthenticated()) {
}
?>
<div
id="menu" data-last-update="<?= (time() - 14) ?>000" data-base-target="_main" class="container" data-icinga-url="<?=$this->href('layout/menu');?>"
data-icinga-refresh="15"
>
<? if (SearchDashboard::search('dummy')->getPane('search')->hasDashlets()): ?>
<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>
<? endif; ?>
<?= new MenuRenderer(Menu::load(), Url::fromRequest()->without('renderLayout')->getRelativeUrl()); ?>
<div id="menu" data-last-update="<?= (time() - 14) ?>000" data-base-target="_main" class="container"
data-icinga-url="<?= $this->href('layout/menu') ?>" data-icinga-refresh="15">
<?= $this->partial(
'layout/menu.phtml',
'default',
array(
'menuRenderer' => new MenuRenderer(
Menu::load(),
Url::fromRequest()->without('renderLayout')->getRelativeUrl()
)
)
) ?>
</div>

View File

@ -57,7 +57,7 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
$type = $attribs['local'] === true ? 'datetime-local' : 'datetime';
unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs)
$html5 = sprintf(
'<input type="%s" name="%s" id="%s" value="%s"%s%s%s%s%s',
'<input type="%s" name="%s" id="%s" step="1" value="%s"%s%s%s%s%s',
$type,
$this->view->escape($name),
$this->view->escape($id),

View File

@ -1,11 +1,28 @@
<div id="login">
<div class="logo">
<div class="image">
<img class="fade-in one" src="<?= $this->baseUrl('img/logo_icinga_big.png') ?>" alt="<?= t('The Icinga logo') ?>" >
<img class="fade-in one" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
</div>
</div>
<div class="form" data-base-target="layout">
<h1>Welcome to Icinga Web 2</h1>
<?php if ($requiresSetup): ?>
<p class="config-note"><?= sprintf(
$this->translate(
'It appears that you did not configure Icinga Web 2 yet so it\'s not possible to log in without any defined '
. 'authentication method. Please define a authentication method by following the instructions in the'
. ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
),
'<a href="http://docs.icinga.org/" title="' . $this->translate('Icinga Web 2 Documentation') . '">', // TODO: More exact documentation link..
'<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
'</a>'
); ?></p>
<?php elseif ($requiresExternalAuth): ?>
<p class="info-box"><span class="icon-info"></span><?= $this->translate(
'You\'re currently not authenticated using any of the web server\'s authentication mechanisms.'
. ' Make sure you\'ll configure such, otherwise you\'ll not be able to login.'
); ?></p>
<?php endif ?>
<h1><?= $this->translate('Welcome to Icinga Web 2'); ?></h1>
<?php
/* TODO: remove this as soon as notifications and forms are ready */
if (isset($this->errorInfo)): ?>
@ -14,18 +31,6 @@
</div>
<?php endif ?>
<?= $this->form ?>
<div class="footer">Icinga Web 2 &copy; 2013-2014<br><a href="https://www.icinga.org">The Icinga Project</a></div>
<?php if ($configMissing): ?>
<div class="config-note"><?= sprintf(
t(
'You seem not to have Icinga Web 2 configured yet so it\'s not possible to log in without any defined '
. 'authentication method. Please define a authentication method by following the instructions in the'
. ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
),
'<a href="http://docs.icinga.org/" title="Icinga Web 2 Documentation">', // TODO: Documentation link
'<a href="' . $this->href('setup') . '" title="Icinga Web 2 Setup-Wizard">',
'</a>'
); ?></div>
<?php endif ?>
<div class="footer">Icinga Web 2 &copy; 2013-2015<br><a href="https://www.icinga.org"><?= $this->translate('The Icinga Project'); ?></a></div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<div class="controls">
<div class="controls" data-base-target="_main">
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">

View File

@ -1,4 +1,4 @@
<div class="controls">
<div class="controls" data-base-target="_main">
<?= $this->tabs->render($this); ?>
</div>

View File

@ -1,35 +0,0 @@
<div class="controls">
<?= $this->tabs->render($this); ?>
</div>
<div class="content">
<?php $errors = $this->form->getErrorMessages(); ?>
<?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?>
<?php endif ?>
<?php if ($this->successMessage): ?>
<div>
<i class="icinga-icon-success"></i>
<strong><?= $this->escape($this->successMessage); ?></strong>
</div>
<?php endif; ?>
<?php if (!empty($errors)) : ?>
<div>
<h4>Errors occured when trying to save the project.</h4>
<p>
The following errors occured when trying to save the configuration:
</p>
<ul>
<?php foreach($errors as $error): ?>
<li><?= $this->escape($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?= $this->form ?>
</div>

View File

@ -1,4 +1,4 @@
<div class="controls">
<div class="controls" data-base-target="_right">
<?= $this->tabs ?>
<h1><?= $this->escape($module->getTitle()) ?></h1>
</div>

View File

@ -1,4 +1,4 @@
<div class="controls">
<div class="controls" data-base-target="_main">
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">

View File

@ -5,7 +5,7 @@ use Icinga\Web\Widget\SearchDashboard;
<? if (SearchDashboard::search('dummy')->getPane('search')->hasDashlets()): ?>
<form action="<?= $this->href('search') ?>" method="get" role="search">
<input
type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>"
type="text" name="q" class="search" placeholder="<?= $this->translate('Search...') ?>"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
/>
</form>

View File

@ -1,4 +1,4 @@
<div class="controls">
<div class="controls" data-base-target="_main">
<?= $tabs ?>
<h1><?= $this->translate('Roles') ?></h1>
</div>
@ -30,7 +30,7 @@
<td>
<?php
// TODO(el): $role->without(...) or $role->shift(...) would be nice!
$restrictions = $role;
$restrictions = clone $role;
unset($restrictions['users']);
unset($restrictions['groups']);
unset($restrictions['permissions']);

View File

@ -24,7 +24,7 @@ For delegating authentication to the web server simply add `autologin` to your a
````
[autologin]
backend = autologin
backend = external
````
If your web server is not configured for authentication though the `autologin` section has no effect.

View File

@ -1,90 +1,83 @@
# Externel Authentication
# External Authentication
It is possible to use the authentication mechanism of the webserver,
instead of using the internal authentication-manager to
authenticate users. This might be useful if you only have very few users, and
user management over *.htaccess* is sufficient, or if you must use some other
authentication mechanism that is only available through your webserver.
It is possible to utilize the authentication mechanism of the webserver instead
of the internal authentication of Icinga Web 2 to authenticate users. This might
be useful if you only have very few users and user management over **.htaccess**
is not sufficient or if you are required to use some other authentication
mechanism that is only available by utilizing the webserver.
When external authentication is used, Icingaweb will entrust the
complete authentication process to the external authentication provider (the webserver):
The provider should take care of authenticating the user and declining
all requests with invalid or missing credentials. When the authentication
was succesful, it should provide the authenticated users name to its php-module
and Icingaweb will assume that the user is authorized to access the page.
Because of this it is very important that the webservers authentication is
configured correctly, as wrong configuration could lead to unauthorized
access to the site, or a broken login-process.
Icinga Web 2 will entrust the complete authentication process to the
authentication provider of the webserver, if external authentication is used.
So it is very important that the webserver's authentication is configured
correctly as wrong configuration might lead to unauthorized access or a
malfunction in the login-process.
## Using External Authentication
## Use External Authentication
External authentication in Icinga Web 2 requires the following preparations:
Using external authentication in Icingaweb requires two steps to work:
1. The external authentication must be set up properly to correctly
authenticate users
2. Icinga Web 2 must be configured to use external authentication
1. The external authentication must be set up correctly to always
authenticate the users.
2. Icingaweb must be configured to use the external authentication.
### Preparing the External Authentication Provider
This step depends heavily on the used webserver and authentication mechanism you
want to use. It is not possible to cover all possibillities and you should
probably read the documentation for your webserver to get detailed instructions
on how to set up authentication properly.
### Prepare the External Authentication Provider
This step depends heavily on the used webserver and authentication
mechanism you want to use. It is not possible to cover all possibillities
and you should probably read the documentation for your webserver for
detailed instructions on how to set up authentication properly.
In general, you need to make sure that:
- All routes require authentication
- Only permitted users are allowed to authenticate
In general you need to make sure that:
- All routes require authentication
- Only permitted users are allowed to authenticate
#### Example Configuration for Apache and HTTPDigestAuthentication
The following example will show how to enable external authentication in Apache using
*HTTP Digest Authentication*.
The following example will show how to enable external authentication in Apache
using *HTTP Digest Authentication*.
##### Create users
##### Creating users
To create users for a digest authentication we can use the tool *htdigest*.
We choose *.icingawebdigest* as a name for the created file, containing
the user credentials.
To create users for digest authentication you can use the tool *htdigest*. In
this example **.icingawebdigest** is the name of the file containing the user
credentials.
This command will create a new file with the user *jdoe*. *htdigest*
will prompt you for your password, after it has been executed. If you
want to add more users to the file you need to ommit the *-c* parameter
in all further commands to avoInid the file to be overwritten.
This command creates a new file with the user *jdoe*. *htdigest* will prompt
you for a password. If you want to add more users to the file you need to omit
the *-c* parameter in all following commands to not to overwrite the file.
````
sudo htdigest -c /etc/icingaweb2/.icingawebdigest "Icinga Web 2" jdoe
````
sudo htdigest -c /etc/httpd/conf.d/.icingawebdigest "Icingaweb 2" jdoe
##### Configuring the Webserver
The webserver should require authentication for all public Icinga Web 2 files.
##### Set up authentication
````
<Directory "/usr/share/icingaweb2/public">
AuthType digest
AuthName "Icinga Web 2"
AuthDigestProvider file
AuthUserFile /etc/icingaweb2/.icingawebdigest
Require valid-user
</Directory>
````
The webserver should require authentication for all public icingaweb files.
### Preparing Icinga Web 2
Once external authentication is set up correctly you need to configure Icinga
Web 2. In case you already completed the setup wizard it is likely that you are
now finished.
<Directory "/var/www/html/icingaweb">
AuthType digest
AuthName "Icingaweb 2"
AuthDigestProvider file
AuthUserFile /etc/httpd/conf.d/.icingawebdigest
Require valid-user
</Directory>
To get Icinga Web 2 to use external authentication the file
**config/authentication.ini** is required. Just add the following section
called "autologin", or any name of your choice, and save your changes:
### Prepare Icingaweb
````
[autologin]
backend = external
````
When the external authentication is set up correctly, we need
to configure IcingaWeb to use it as an authentication source. The
configuration key *authenticationMode* in the section *global* defines
if the authentication should be handled internally or externally. Since
we want to delegate the authentication to the Webserver we choose
"external" as the new value:
[global]
; ...
authenticationMode = "external"
; ...
Congratulations! You are now logged in when visiting Icinga Web 2.

View File

@ -6,7 +6,7 @@ system and distribution you are running. But it is also possible to install Icin
## <a id="installation-requirements"></a> Installing Requirements
* A web server, e.g. Apache or nginx
* PHP >= 5.3.0
* PHP >= 5.3.0 w/ gettext and OpenSSL support
* MySQL or PostgreSQL PHP libraries when using a database for authentication or storing user preferences into a database
* LDAP PHP library when using Active Directory or LDAP for authentication
* Icinga 1.x w/ Livestatus or IDO, Icinga 2 w/ Livestatus or IDO feature enabled
@ -26,7 +26,7 @@ repository either via git or http protocol using the following URLs:
* http://git.icinga.org/icingaweb2.git
There is also a browsable version available at
[gi.icinga.org](https://git.icinga.org/?p=icingaweb2.git;a=summary "Icinga Web 2 Git Repository").
[git.icinga.org](https://git.icinga.org/?p=icingaweb2.git;a=summary "Icinga Web 2 Git Repository").
This version also offers snapshots for easy download which you can use if you do not have git present on your system.
````
@ -38,25 +38,73 @@ git clone git://git.icinga.org/icingaweb2.git
Choose a target directory and move Icinga Web 2 there.
````
mv icingaweb2 /usr/share/icingaweb
mv icingaweb2 /usr/share/icingaweb2
````
**Step 3: Configuring the Web Server**
Use `icingacli` to generate web server configuration for either Apache or nginx.
*Apache*
Apache:
````
./bin/icingacli setup config webserver apache --document-root /usr/share/icingaweb/public
./bin/icingacli setup config webserver apache --document-root /usr/share/icingaweb2/public
````
*nginx*
nginx:
````
./bin/icingacli setup config webserver nginx --document-root /usr/share/icingaweb/public
./bin/icingacli setup config webserver nginx --document-root /usr/share/icingaweb2/public
````
**Step 4: Web Setup**
**Step 4: Preparing Web Setup**
Visit Icinga Web 2 in your browser and complete installation using the web setup.
Because both web and CLI must have access to configuration and logs, permissions will be managed using a special
system group. The web server user and CLI user have to be added to this system group.
Add the system group `icingaweb2` in the first place.
Fedora, RHEL, CentOS, SLES and OpenSUSE:
````
groupadd -r icingaweb2
````
Debian and Ubuntu:
````
addgroup --system icingaweb2
````
Add your web server's user to the system group `icingaweb2`:
Fedora, RHEL and CentOS:
````
usermod -a -G icingaweb2 apache
````
SLES and OpenSUSE:
````
usermod -G icingaweb2 wwwrun
````
Debian and Ubuntu:
````
usermod -a -G icingaweb2 wwwrun
````
Use `icingacli` to create the configuration directory which defaults to **/etc/icingaweb2**:
````
./bin/icingacli setup config directory
````
When using the web setup you are required to authenticate using a token. In order to generate a token use the
`icingacli`:
````
./bin/icingacli setup token create
````
In case you do not remember the token you can show it using the `icingacli`:
````
./bin/icingacli setup token show
````
**Step 5: Web Setup**
Visit Icinga Web 2 in your browser and complete installation using the web setup: /icingaweb2/setup

52
doc/vagrant.md Normal file
View File

@ -0,0 +1,52 @@
# Vagrant
## Requirements
* Vagrant &gt;= version 1.5
* VirtualBox or Parallels
> **Note:** The deployment of the virtual machine is tested against Vagrant starting with version 1.5.
> 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,
MySQL and PostgreSQL backends as well as the LDAP authentication. All you
have to do is install Vagrant and run:
````
vagrant up
````
> **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.
After you should be able to browse [localhost:8080/icingaweb2](http://localhost:8080/icingaweb2).
## Log into Icinga Web 2
Both LDAP and a MySQL are configured as authentication backend. Please use one of the following login credentials:
> LDAP:
>> **Username**: `jdoe`
>> **Password**: `password`
>MySQL:
>> **Username**: `icingaadmin`
>> **Password**: `icinga`
## Testing the Source Code
All software required to run tests is installed in the virtual machine.
In order to run all tests you have to execute the following command:
````
vagrant ssh -c "icingacli test php unit"
````

View File

@ -1,237 +1,271 @@
#/**
# * 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 1.beta2
%define revision 1
%define configdir %{_sysconfdir}/%{name}
%define sharedir %{_datadir}/%{name}
%define prefixdir %{_datadir}/%{name}
%define usermodparam -a -G
%define logdir %{_localstatedir}/log/%{name}
%define docdir %{sharedir}/doc
%if "%{_vendor}" == "suse"
%define phpname php5
%define phpzendname php5-ZendFramework
%define apache2modphpname apache2-mod_php5
%endif
# SLE 11 = 1110
%if 0%{?suse_version} == 1110
%define phpname php53
%define apache2modphpname apache2-mod_php53
%define usermodparam -A
%endif
%if "%{_vendor}" == "redhat"
%define phpname php
%define phpzendname php-ZendFramework
%endif
# el5 requires newer php53 rather than php (5.1)
%if 0%{?el5} || 0%{?rhel} == 5 || "%{?dist}" == ".el5"
%define phpname php53
%endif
%if "%{_vendor}" == "suse"
%define apacheconfdir %{_sysconfdir}/apache2/conf.d
%define apacheuser wwwrun
%define apachegroup www
%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 %{_localstatedir}/run/icinga2/cmd/icinga.cmd
%define livestatussocket %{_localstatedir}/run/icinga2/cmd/livestatus
%endif
Summary: Open Source host, service and network monitoring Web UI
Name: icingaweb2
Version: 0.0.1
Version: 2.0.0
Release: %{revision}%{?dist}
License: GPLv2
Summary: Icinga Web 2
Group: Applications/System
URL: http://www.icinga.org
License: GPL
URL: https://icinga.org
Source0: https://github.com/Icinga/%{name}/archive/v%{version}.tar.gz
BuildArch: noarch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
Packager: Icinga Team <info@icinga.org>
%if "%{_vendor}" == "suse"
AutoReqProv: Off
%if 0%{?fedora} || 0%{?rhel}
%define wwwconfigdir %{_sysconfdir}/httpd/conf.d
%define wwwuser apache
%if 0%{?rhel} == 5
%define php php53
%define php_cli php53-cli
%else
%define php php
%define php_cli php-cli
%endif
%if 0%{?rhel} == 6
%define zend php-ZendFramework
%else
%define zend %{name}-vendor-Zend
%endif
%endif
Source: icingaweb2-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: %{phpname} >= 5.3.0
BuildRequires: %{phpname}-devel >= 5.3.0
BuildRequires: %{phpname}-ldap
BuildRequires: %{phpname}-pdo
BuildRequires: %{phpzendname}
%if "%{_vendor}" != "suse"
BuildRequires: %{phpzendname}-Db-Adapter-Pdo
BuildRequires: %{phpzendname}-Db-Adapter-Pdo-Mysql
BuildRequires: %{phpzendname}-Db-Adapter-Pdo-Pgsql
%if 0%{?suse_version}
%define wwwconfigdir %{_sysconfdir}/apache2/conf.d
%define wwwuser wwwrun
%define zend php5-ZendFramework
%if 0%{?suse_version} == 1110
%define php php53
Requires: apache2-mod_php53
%else
%define php php5
Requires: apache2-mod_php5
%endif
%endif
%if "%{_vendor}" == "redhat"
%endif
%if "%{_vendor}" == "suse"
Requires: %{phpname}-devel >= 5.3.0
BuildRequires: %{phpname}-json
BuildRequires: %{phpname}-sockets
BuildRequires: %{phpname}-dom
%endif
Requires: %{phpname} >= 5.3.0
Requires: %{phpzendname}
Requires: %{phpname}-ldap
Requires: %{phpname}-pdo
%if "%{_vendor}" == "redhat"
Requires: %{phpname}-common
Requires: %{phpzendname}-Db-Adapter-Pdo
Requires: %{phpzendname}-Db-Adapter-Pdo-Mysql
Requires: php-pear
%endif
%if "%{_vendor}" == "suse"
Requires: %{phpname}-pear
Requires: %{phpname}-dom
Requires: %{phpname}-tokenizer
Requires: %{phpname}-gettext
Requires: %{phpname}-ctype
Requires: %{phpname}-json
Requires: %{apache2modphpname}
%endif
Requires: php-Icinga
Requires(pre): shadow-utils
Requires: %{name}-common = %{version}-%{release}
Requires: php-Icinga = %{version}-%{release}
Requires: %{name}-vendor-dompdf
Requires: %{name}-vendor-HTMLPurifier
Requires: %{name}-vendor-JShrink
Requires: %{name}-vendor-lessphp
Requires: %{name}-vendor-Parsedown
Requires: %{zend}
%description
Icinga Web 2 for Icinga 2 or Icinga 1.x using multiple backends
for example DB IDO.
Icinga Web 2
%package -n icingacli
Summary: Icinga CLI
Group: Applications/System
Requires: %{name} = %{version}-%{release}
Requires: php-Icinga
%description -n icingacli
Icinga CLI using php-Icinga Icinga Web 2 backend.
%define basedir %{_datadir}/%{name}
%define bindir %{_bindir}
%define configdir %{_sysconfdir}/%{name}
%define logdir %{_localstatedir}/log/%{name}
%define phpdir %{_datadir}/php
%define icingawebgroup icingaweb2
%define docsdir %{_datadir}/doc/%{name}
%package common
Summary: Common files for Icinga Web 2 and the Icinga CLI
Group: Applications/System
%description common
Common files for Icinga Web 2 and the Icinga CLI
%package -n php-Icinga
Summary: Icinga Web 2 PHP Libraries
Group: Applications/System
Requires: %{name} = %{version}-%{release}
Requires: %{phpname} >= 5.3.0
Requires: %{phpzendname}
Summary: Icinga Web 2 PHP library
Group: Development/Libraries
Requires: %{php} >= 5.3.0
Requires: %{php}-gd %{php}-intl
%{?fedora:Requires: php-pecl-imagick}
%{?rhel:Requires: php-pecl-imagick}
%{?suse_version:Requires: %{php}-gettext %{php}-openssl php5-imagick}
%description -n php-Icinga
Icinga Web 2 PHP Libraries required by the web frontend and cli tool.
Icinga Web 2 PHP library
%package -n icingacli
Summary: Icinga CLI
Group: Applications/System
Requires: %{name}-common = %{version}-%{release}
Requires: php-Icinga = %{version}-%{release}
%{?fedora:Requires: %{php_cli} >= 5.3.0 bash-completion}
%{?rhel:Requires: %{php_cli} >= 5.3.0 bash-completion}
%{?suse_version:Requires: %{php} >= 5.3.0}
%description -n icingacli
Icinga CLI
%package vendor-dompdf
Version: 0.6.1
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library dompdf
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-dompdf
Icinga Web 2 vendor library dompdf
%package vendor-HTMLPurifier
Version: 4.6.0
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library HTMLPurifier
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-HTMLPurifier
Icinga Web 2 vendor library HTMLPurifier
%package vendor-JShrink
Version: 1.0.1
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library JShrink
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-JShrink
Icinga Web 2 vendor library JShrink
%package vendor-lessphp
Version: 0.4.0
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library lessphp
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-lessphp
Icinga Web 2 vendor library lessphp
%package vendor-Parsedown
Version: 1.0.0
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library Parsedown
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-Parsedown
Icinga Web 2 vendor library Parsedown
%package vendor-Zend
Version: 1.12.9
Release: 1%{?dist}
Summary: Icinga Web 2 vendor library Zend Framework
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description vendor-Zend
Icinga Web 2 vendor library Zend
%prep
#VERSION=0.0.1; git archive --format=tar --prefix=icingaweb2-$VERSION/ HEAD | gzip >icingaweb2-$VERSION.tar.gz
%setup -q -n %{name}-%{version}
%setup -q
%build
%install
[ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot}
# prepare configuration for sub packages
# install rhel apache config
install -D -m0644 packages/files/apache/icingaweb.conf %{buildroot}/%{apacheconfdir}/icingaweb.conf
# install public, library, modules
%{__mkdir} -p %{buildroot}/%{sharedir}
%{__mkdir} -p %{buildroot}/%{logdir}
%{__mkdir} -p %{buildroot}/%{docdir}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/enabledModules
# make sure to install local icingacli for setup wizard token generation & webserver config
%{__cp} -r application doc library modules public bin %{buildroot}/%{sharedir}/
# enable the monitoring module by default
ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/%{name}/enabledModules/monitoring
## config
# symlink icingacli
mkdir -p %{buildroot}/usr/bin
ln -sf %{sharedir}/bin/icingacli %{buildroot}/usr/bin/icingacli
rm -rf %{buildroot}
mkdir -p %{buildroot}/{%{basedir}/{modules,library,public},%{bindir},%{configdir}/modules/setup,%{logdir},%{phpdir},%{wwwconfigdir},%{_sysconfdir}/bash_completion.d,%{docsdir}}
cp -prv application doc %{buildroot}/%{basedir}
cp -pv etc/bash_completion.d/icingacli %{buildroot}/%{_sysconfdir}/bash_completion.d/icingacli
cp -prv modules/{monitoring,setup} %{buildroot}/%{basedir}/modules
cp -prv library/Icinga %{buildroot}/%{phpdir}
cp -prv library/vendor %{buildroot}/%{basedir}/library
cp -prv public/{css,img,js,error_norewrite.html} %{buildroot}/%{basedir}/public
cp -pv packages/files/apache/icingaweb2.conf %{buildroot}/%{wwwconfigdir}/icingaweb2.conf
cp -pv packages/files/bin/icingacli %{buildroot}/%{bindir}
cp -pv packages/files/public/index.php %{buildroot}/%{basedir}/public
cp -prv etc/schema %{buildroot}/%{docsdir}
cp -prv packages/files/config/modules/setup %{buildroot}/%{configdir}/modules/
%pre
# Add apacheuser in the icingacmd group
# If the group exists, add the apacheuser in the icingacmd group.
# It is not neccessary that icinga2-web is installed on the same system as
# icinga and only on systems with icinga installed the icingacmd
# group exists. In all other cases the user used for ssh access has
# to be added to the icingacmd group on the remote icinga server.
getent group icingacmd > /dev/null
if [ $? -eq 0 ]; then
%{_sbindir}/usermod %{usermodparam} icingacmd %{apacheuser}
fi
%preun
%post
getent group icingacmd >/dev/null || groupadd -r icingacmd
%if 0%{?suse_version}
usermod -G icingacmd,%{icingawebgroup} %{wwwuser}
%else
usermod -a -G icingacmd,%{icingawebgroup} %{wwwuser}
%endif
exit 0
%clean
[ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot}
rm -rf %{buildroot}
%files
# main dirs
%defattr(-,root,root)
%doc etc/schema doc packages/RPM.md
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/public
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/modules
# configs
%{basedir}/application/controllers
%{basedir}/application/fonts
%{basedir}/application/forms
%{basedir}/application/layouts
%{basedir}/application/views
%{basedir}/doc
%{basedir}/modules
%{basedir}/public
%{wwwconfigdir}/icingaweb2.conf
%attr(2775,root,%{icingawebgroup}) %dir %{logdir}
%{docsdir}
%docdir %{docsdir}
%attr(2770,root,%{icingawebgroup}) %config(noreplace) %dir %{configdir}/modules/setup
%attr(0660,root,%{icingawebgroup}) %config(noreplace) %{configdir}/modules/setup/config.ini
%pre common
getent group %{icingawebgroup} >/dev/null || groupadd -r %{icingawebgroup}
exit 0
%files common
%defattr(-,root,root)
%config(noreplace) %attr(-,root,root) %{apacheconfdir}/icingaweb.conf
%config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir}
# logs
%attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir}
# shipped docs
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/doc
%{basedir}/application/locale
%dir %{basedir}/modules
%attr(2770,root,%{icingawebgroup}) %config(noreplace) %dir %{configdir}
%attr(2770,root,%{icingawebgroup}) %config(noreplace) %dir %{configdir}/modules
%files -n php-Icinga
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/application
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/library
%defattr(-,root,root)
%{phpdir}/Icinga
%files -n icingacli
%attr(0755,root,root) /usr/bin/icingacli
%attr(0755,root,root) %{sharedir}/bin/icingacli
%attr(0755,root,root) %{sharedir}/bin/license_writer.py
%defattr(-,root,root)
%{basedir}/application/clicommands
%{_sysconfdir}/bash_completion.d/icingacli
%attr(0755,root,root) %{bindir}/icingacli
%changelog
%files vendor-dompdf
%defattr(-,root,root)
%{basedir}/library/vendor/dompdf
%files vendor-HTMLPurifier
%defattr(-,root,root)
%{basedir}/library/vendor/HTMLPurifier
%files vendor-JShrink
%defattr(-,root,root)
%{basedir}/library/vendor/JShrink
%files vendor-lessphp
%defattr(-,root,root)
%{basedir}/library/vendor/lessphp
%files vendor-Parsedown
%defattr(-,root,root)
%{basedir}/library/vendor/Parsedown
%files vendor-Zend
%defattr(-,root,root)
%{basedir}/library/vendor/Zend

View File

@ -113,6 +113,13 @@ abstract class ApplicationBootstrap
*/
protected $isWeb = false;
/**
* Whether Icinga Web 2 requires setup
*
* @type bool
*/
protected $requiresSetup = false;
/**
* Constructor
*
@ -133,7 +140,7 @@ abstract class ApplicationBootstrap
if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
$configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
} else {
$configDir = '/etc/icingaweb';
$configDir = '/etc/icingaweb2';
}
}
$canonical = realpath($configDir);
@ -333,7 +340,7 @@ abstract class ApplicationBootstrap
/**
* Setup Icinga auto loader
*
* @return self
* @return $this
*/
public function setupAutoloader()
{
@ -366,7 +373,7 @@ abstract class ApplicationBootstrap
/**
* Setup module manager
*
* @return self
* @return $this
*/
protected function setupModuleManager()
{
@ -378,25 +385,10 @@ abstract class ApplicationBootstrap
return $this;
}
/**
* Load all core modules
*
* @return self
*/
protected function loadCoreModules()
{
try {
$this->moduleManager->loadCoreModules();
} catch (NotReadableError $e) {
Logger::error(new IcingaException('Cannot load core modules. An exception was thrown:', $e));
}
return $this;
}
/**
* Load all enabled modules
*
* @return self
* @return $this
*/
protected function loadEnabledModules()
{
@ -408,10 +400,47 @@ abstract class ApplicationBootstrap
return $this;
}
/**
* Load the setup module if Icinga Web 2 requires setup or the setup token exists
*
* @return $this
*/
protected function loadSetupModuleIfNecessary()
{
if (! @file_exists($this->config->resolvePath('authentication.ini'))) {
$this->requiresSetup = true;
$this->moduleManager->loadModule('setup');
} elseif ($this->setupTokenExists()) {
// Load setup module but do not require setup
$this->moduleManager->loadModule('setup');
}
return $this;
}
/**
* Get whether Icinga Web 2 requires setup
*
* @return bool
*/
public function requiresSetup()
{
return $this->requiresSetup;
}
/**
* Get whether the setup token exists
*
* @return bool
*/
public function setupTokenExists()
{
return @file_exists($this->config->resolvePath('setup.token'));
}
/**
* Setup default logging
*
* @return self
* @return $this
*/
protected function setupLogging()
{
@ -428,7 +457,7 @@ abstract class ApplicationBootstrap
/**
* Load Configuration
*
* @return self
* @return $this
*/
protected function loadConfig()
{
@ -447,7 +476,7 @@ abstract class ApplicationBootstrap
/**
* Error handling configuration
*
* @return self
* @return $this
*/
protected function setupErrorHandling()
{
@ -473,7 +502,7 @@ abstract class ApplicationBootstrap
/**
* Set up logger
*
* @return self
* @return $this
*/
protected function setupLogger()
{
@ -490,7 +519,7 @@ abstract class ApplicationBootstrap
/**
* Set up the resource factory
*
* @return self
* @return $this
*/
protected function setupResourceFactory()
{

View File

@ -44,7 +44,7 @@ class Cli extends ApplicationBootstrap
->setupLogger()
->setupResourceFactory()
->setupModuleManager()
->loadCoreModules();
->loadSetupModuleIfNecessary();
}
protected function setupLogging()

View File

@ -279,7 +279,7 @@ class Config implements Countable, Iterator
*
* @param string $file The file to parse
*
* @throws NotReadableError When the file does not exist or cannot be read
* @throws NotReadableError When the file cannot be read
*/
public static function fromIni($file)
{
@ -292,7 +292,7 @@ class Config implements Countable, Iterator
$config = new static(new ConfigObject(parse_ini_file($filepath, true)));
$config->setConfigFile($filepath);
return $config;
} else {
} elseif (@file_exists($filepath)) {
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
}
@ -322,7 +322,7 @@ class Config implements Countable, Iterator
*/
public static function app($configname = 'config', $fromDisk = false)
{
if (!isset(self::$app[$configname]) || $fromDisk) {
if (! isset(self::$app[$configname]) || $fromDisk) {
self::$app[$configname] = static::fromIni(static::resolvePath($configname . '.ini'));
}
@ -341,12 +341,12 @@ class Config implements Countable, Iterator
*/
public static function module($modulename, $configname = 'config', $fromDisk = false)
{
if (!isset(self::$modules[$modulename])) {
if (! isset(self::$modules[$modulename])) {
self::$modules[$modulename] = array();
}
$moduleConfigs = self::$modules[$modulename];
if (!isset($moduleConfigs[$configname]) || $fromDisk) {
if (! isset($moduleConfigs[$configname]) || $fromDisk) {
$moduleConfigs[$configname] = static::fromIni(
static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
);

View File

@ -6,10 +6,8 @@ namespace Icinga\Application;
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
use Icinga\Exception\ProgrammingError;
/**
* Use this if you want to make use of Icinga funtionality in other web projects
* Use this if you want to make use of Icinga functionality in other web projects
*
* Usage example:
* <code>

View File

@ -68,18 +68,6 @@ class Manager
*/
private $modulePaths = array();
/**
* The core modules
*
* Core modules do not need to be enabled to load and cannot be disabled
* by the user. This must not be writable programmatically!
*
* @var array
*/
private $coreModules = array(
'setup'
);
/**
* Create a new instance of the module manager
*
@ -170,21 +158,7 @@ class Manager
}
/**
* Try to set all core modules in loaded state
*
* @return self
* @see Manager::loadModule()
*/
public function loadCoreModules()
{
foreach ($this->coreModules as $name) {
$this->loadModule($name);
}
return $this;
}
/**
* Try to set all enabled modules in loaded state
* Try to set all enabled modules in loaded sate
*
* @return self
* @see Manager::loadModule()
@ -239,8 +213,6 @@ class Manager
'Cannot enable module "%s". Module is not installed.',
$name
);
} elseif (in_array($name, $this->coreModules)) {
return $this;
}
clearstatcache(true);
@ -458,7 +430,7 @@ class Manager
}
$installed = $this->listInstalledModules();
foreach (array_diff($installed, $this->coreModules) as $name) {
foreach ($installed as $name) {
$info[$name] = (object) array(
'name' => $name,
'path' => $this->installedBaseDirs[$name],

View File

@ -759,15 +759,15 @@ class Module
protected function registerAutoloader()
{
$moduleName = ucfirst($this->getName());
$moduleLibraryDir = $this->getLibDir(). '/'. $moduleName;
if (is_dir($this->getBaseDir()) && is_dir($this->getLibDir()) && is_dir($moduleLibraryDir)) {
if (is_dir($moduleLibraryDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName, $moduleLibraryDir);
if (is_dir($this->getFormDir())) {
$this->app->getLoader()->registerNamespace(
'Icinga\\Module\\' . $moduleName. '\\Forms',
$this->getFormDir()
);
}
}
$moduleFormDir = $this->getFormDir();
if (is_dir($moduleFormDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName. '\\Forms', $moduleFormDir);
}
return $this;

View File

@ -104,7 +104,7 @@ class Web extends ApplicationBootstrap
->setupZendMvc()
->setupFormNamespace()
->setupModuleManager()
->loadCoreModules()
->loadSetupModuleIfNecessary()
->loadEnabledModules()
->setupRoute()
->setupPagination();

View File

@ -87,9 +87,16 @@ class DbUserBackend extends UserBackend
*/
protected function getPasswordHash($username)
{
$stmt = $this->conn->getDbAdapter()->prepare(
'SELECT password_hash FROM icingaweb_user WHERE name = :name AND active = 1'
);
if ($this->conn->getDbType() === 'pgsql') {
// Since PostgreSQL version 9.0 the default value for bytea_output is 'hex' instead of 'escape'
$stmt = $this->conn->getDbAdapter()->prepare(
'SELECT ENCODE(password_hash, \'escape\') FROM icingaweb_user WHERE name = :name AND active = 1'
);
} else {
$stmt = $this->conn->getDbAdapter()->prepare(
'SELECT password_hash FROM icingaweb_user WHERE name = :name AND active = 1'
);
}
$stmt->execute(array(':name' => $username));
$stmt->bindColumn(1, $lob, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);

View File

@ -11,7 +11,7 @@ use Icinga\User;
/**
* Test login with external authentication mechanism, e.g. Apache
*/
class AutoLoginBackend extends UserBackend
class ExternalBackend extends UserBackend
{
/**
* Regexp expression to strip values from a username
@ -21,7 +21,7 @@ class AutoLoginBackend extends UserBackend
private $stripUsernameRegexp;
/**
* Create new autologin backend
* Create new authentication backend of type "external"
*
* @param ConfigObject $config
*/
@ -33,7 +33,7 @@ class AutoLoginBackend extends UserBackend
/**
* Count the available users
*
* Autologin backends will always return 1
* Authenticaton backends of type "external" will always return 1
*
* @return int
*/

View File

@ -213,7 +213,7 @@ class LdapUserBackend extends UserBackend
*/
public function count()
{
return $this->conn->count($this->selectUsers());
return $this->selectUsers()->count();
}
/**

View File

@ -63,7 +63,7 @@ class Manager
);
$config = new Config();
}
if ($config->hasSection('preferences')) {
if ($config->get('preferences', 'store', 'ini') !== 'none') {
$preferencesConfig = $config->getSection('preferences');
try {
$preferencesStore = PreferencesStore::create(
@ -165,6 +165,7 @@ class Manager
*/
public function hasPermission($permission)
{
return true;
if (! $this->isAuthenticated()) {
return false;
}

View File

@ -5,7 +5,7 @@
namespace Icinga\Authentication;
use Countable;
use Icinga\Authentication\Backend\AutoLoginBackend;
use Icinga\Authentication\Backend\ExternalBackend;
use Icinga\Authentication\Backend\DbUserBackend;
use Icinga\Authentication\Backend\LdapUserBackend;
use Icinga\Data\ConfigObject;
@ -69,8 +69,8 @@ abstract class UserBackend implements Countable
);
}
$backendType = strtolower($backendType);
if ($backendType === 'autologin') {
$backend = new AutoLoginBackend($backendConfig);
if ($backendType === 'external') {
$backend = new ExternalBackend($backendConfig);
$backend->setName($name);
return $backend;
}

View File

@ -78,24 +78,23 @@ class Axis implements Drawable
private $yUnit = null;
/**
* If the displayed labels should be aligned horizontally or diagonally
* The minimum amount of units each step must take up
*
* @var int
*/
private $labelRotationStyle = self::LABEL_ROTATE_DIAGONAL;
public $minUnitsPerStep = 80;
/**
* Set the label rotation style for the horizontal axis
* The minimum amount of units each tick must take up
*
* <ul>
* <li><b>LABEL_ROTATE_HORIZONTAL</b>: Labels will be displayed horizontally </li>
* <li><b>LABEL_ROTATE_DIAGONAL</b>: Labels will be rotated by 45° </li>
* </ul>
*
* @param $style The rotation mode
* @var int
*/
public function setHorizontalLabelRotationStyle($style)
{
$this->labelRotationStyle = $style;
}
public $minUnitsPerTick = 15;
/**
* If the displayed labels should be aligned horizontally or diagonally
*/
protected $labelRotationStyle = self::LABEL_ROTATE_HORIZONTAL;
/**
* Inform the axis about an added dataset
@ -160,58 +159,74 @@ class Axis implements Drawable
*/
private function renderHorizontalAxis(RenderContext $ctx, DOMElement $group)
{
$steps = $this->ticksPerX($this->xUnit->getTicks(), $ctx->getNrOfUnitsX(), $this->minUnitsPerStep);
$ticks = $this->ticksPerX($this->xUnit->getTicks(), $ctx->getNrOfUnitsX(), $this->minUnitsPerTick);
// Steps should always be ticks
if ($ticks !== $steps) {
$steps = $ticks * 5;
}
// Check whether there is enough room for regular labels
$labelRotationStyle = $this->labelRotationStyle;
if ($this->labelsOversized($this->xUnit, 6)) {
$labelRotationStyle = self::LABEL_ROTATE_DIAGONAL;
}
/*
$line = new Line(0, 100, 100, 100);
$line->setStrokeWidth(2);
$group->appendChild($line->toSvg($ctx));
*/
// contains the approximate end position of the last label
$lastLabelEnd = -1;
$shift = 0;
$i = 0;
foreach ($this->xUnit as $label => $pos) {
if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) {
// If the last label would overlap this label we shift the y axis a bit
if ($lastLabelEnd > $pos) {
$shift = ($shift + 5) % 10;
} else {
$shift = 0;
if ($i % $ticks === 0) {
/*
$tick = new Line($pos, 100, $pos, 101);
$group->appendChild($tick->toSvg($ctx));
*/
}
if ($i % $steps === 0) {
if ($labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) {
// If the last label would overlap this label we shift the y axis a bit
if ($lastLabelEnd > $pos) {
$shift = ($shift + 5) % 10;
} else {
$shift = 0;
}
}
$labelField = new Text($pos + 0.5, ($this->xLabel ? 107 : 105) + $shift, $label);
if ($labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) {
$labelField->setAlignment(Text::ALIGN_MIDDLE)
->setFontSize('2.5em');
} else {
$labelField->setFontSize('2.5em');
}
if ($labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) {
$labelField = new Rotator($labelField, 45);
}
$labelField = $labelField->toSvg($ctx);
$group->appendChild($labelField);
if ($this->drawYGrid) {
$bgLine = new Line($pos, 0, $pos, 100);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#BFBFBF');
$group->appendChild($bgLine->toSvg($ctx));
}
$lastLabelEnd = $pos + strlen($label) * 1.2;
}
$tick = new Line($pos, 100, $pos, 102);
$group->appendChild($tick->toSvg($ctx));
$labelField = new Text($pos + 0.5, ($this->xLabel ? 107 : 105) + $shift, $label);
if ($this->labelRotationStyle === self::LABEL_ROTATE_HORIZONTAL) {
$labelField->setAlignment(Text::ALIGN_MIDDLE)
->setFontSize('1.8em');
} else {
$labelField->setFontSize('2.5em');
}
if ($this->labelRotationStyle === self::LABEL_ROTATE_DIAGONAL) {
$labelField = new Rotator($labelField, 45);
}
$labelField = $labelField->toSvg($ctx);
$group->appendChild($labelField);
if ($this->drawYGrid) {
$bgLine = new Line($pos, 0, $pos, 100);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#232');
$group->appendChild($bgLine->toSvg($ctx));
}
$lastLabelEnd = $pos + strlen($label) * 1.2;
}
// render the label for this axis
if ($this->xLabel) {
$axisLabel = new Text(50, 104, $this->xLabel);
$axisLabel->setFontSize('2em')
->setFontWeight('bold')
->setAlignment(Text::ALIGN_MIDDLE);
$group->appendChild($axisLabel->toSvg($ctx));
$i++;
}
}
@ -223,34 +238,59 @@ class Axis implements Drawable
*/
private function renderVerticalAxis(RenderContext $ctx, DOMElement $group)
{
$steps = $this->ticksPerX($this->yUnit->getTicks(), $ctx->getNrOfUnitsY(), $this->minUnitsPerStep);
$ticks = $this->ticksPerX($this->yUnit->getTicks(), $ctx->getNrOfUnitsY(), $this->minUnitsPerTick);
// Steps should always be ticks
if ($ticks !== $steps) {
$steps = $ticks * 5;
}
/*
$line = new Line(0, 0, 0, 100);
$line->setStrokeWidth(2);
$group->appendChild($line->toSvg($ctx));
*/
$i = 0;
foreach ($this->yUnit as $label => $pos) {
$pos = 100 - $pos;
$tick = new Line(0, $pos, -1, $pos);
$group->appendChild($tick->toSvg($ctx));
$labelField = new Text(-0.5, $pos+0.5, $label);
$labelField->setFontSize('1.8em')
->setAlignment(Text::ALIGN_END);
$group->appendChild($labelField->toSvg($ctx));
if ($this->drawXGrid) {
$bgLine = new Line(0, $pos, 100, $pos);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#343');
$group->appendChild($bgLine->toSvg($ctx));
if ($i % $ticks === 0) {
// draw a tick
//$tick = new Line(0, $pos, -1, $pos);
//$group->appendChild($tick->toSvg($ctx));
}
if ($i % $steps === 0) {
// draw a step
$labelField = new Text(-0.5, $pos + 0.5, $label);
$labelField->setFontSize('2.5em')
->setAlignment(Text::ALIGN_END);
$group->appendChild($labelField->toSvg($ctx));
if ($this->drawXGrid) {
$bgLine = new Line(0, $pos, 100, $pos);
$bgLine->setStrokeWidth(0.5)
->setStrokeColor('#BFBFBF');
$group->appendChild($bgLine->toSvg($ctx));
}
}
$i++;
}
if ($this->yLabel) {
$axisLabel = new Text(-8, 50, $this->yLabel);
if ($this->yLabel || $this->xLabel) {
if ($this->yLabel && $this->xLabel) {
$txt = $this->yLabel . ' / ' . $this->xLabel;
} else if ($this->xLabel) {
$txt = $this->xLabel;
} else {
$txt = $this->yLabel;
}
$axisLabel = new Text(50, -3, $txt);
$axisLabel->setFontSize('2em')
->setFontWeight('bold')
->setAlignment(Text::ALIGN_MIDDLE);
$axisLabel = new Rotator($axisLabel, 90);
$group->appendChild($axisLabel->toSvg($ctx));
}
@ -416,4 +456,32 @@ class Axis implements Drawable
$this->renderVerticalAxis($ctx, $group);
return $group;
}
protected function ticksPerX($ticks, $units, $min)
{
$per = 1;
while ($per * $units / $ticks < $min) {
$per++;
}
return $per;
}
/**
* Returns whether at least one label of the given Axis
* is bigger than the given maxLength
*
* @param AxisUnit $axis The axis that contains the labels that will be checked
*
* @return boolean Whether at least one label is bigger than maxLength
*/
private function labelsOversized(AxisUnit $axis, $maxLength = 5)
{
$oversized = false;
foreach ($axis as $label => $pos) {
if (strlen($label) > $maxLength) {
$oversized = true;
}
}
return $oversized;
}
}

View File

@ -28,7 +28,7 @@ class BarGraph extends Styleable implements Drawable
*
* @var int
*/
private $barWidth = 4;
private $barWidth = 3;
/**
* The dataset to use for this bar graph
@ -122,6 +122,14 @@ class BarGraph extends Styleable implements Drawable
$doc = $ctx->getDocument();
$group = $doc->createElement('g');
$idx = 0;
if (count($this->dataSet) > 15) {
$this->barWidth = 2;
}
if (count($this->dataSet) > 25) {
$this->barWidth = 1;
}
foreach ($this->dataSet as $x => $point) {
// add white background bar, to prevent other bars from altering transparency effects
$bar = $this->drawSingleBar($point, $idx++, 'white', $this->strokeWidth, $idx)->toSvg($ctx);

View File

@ -45,6 +45,13 @@ class LineGraph extends Styleable implements Drawable
*/
public $strokeWidth = 5;
/**
* The size of the displayed dots
*
* @var int
*/
public $dotWith = 0;
/**
* Create a new LineGraph displaying the given dataset
*
@ -138,8 +145,8 @@ class LineGraph extends Styleable implements Drawable
$group = $path->toSvg($ctx);
if ($this->showDataPoints === true) {
foreach ($this->dataset as $point) {
$dot = new Circle($point[0], $point[1], $this->strokeWidth*5);
$dot->setFill('black');
$dot = new Circle($point[0], $point[1], $this->dotWith);
$dot->setFill($this->strokeColor);
$group->appendChild($dot->toSvg($ctx));
}

View File

@ -66,13 +66,14 @@ class Legend implements Drawable
$outer->getLayout()->setPadding(2, 2, 2, 2);
$nrOfColumns = 4;
$leftstep = 100 / $nrOfColumns;
$topstep = 10 / $nrOfColumns + 2;
$top = 0;
$left = 0;
$lastLabelEndPos = -1;
foreach ($this->dataset as $color => $text) {
$leftstep = 100 / $nrOfColumns + strlen($text);
// Make sure labels don't overlap each other
while ($lastLabelEndPos >= $left) {
$left += $leftstep;

View File

@ -61,7 +61,7 @@ class Circle extends Styleable implements Drawable
$circle = $ctx->getDocument()->createElement('circle');
$circle->setAttribute('cx', Format::formatSVGNumber($coords[0]));
$circle->setAttribute('cy', Format::formatSVGNumber($coords[1]));
$circle->setAttribute('r', 5);
$circle->setAttribute('r', $this->radius);
$circle->setAttribute('style', $this->getStyle());
$this->applyAttributes($circle);
return $circle;

View File

@ -9,13 +9,14 @@ use Iterator;
/**
* Base class for Axis Units
*
* An AxisUnit takes a set of values and places them on a given range
*
* Concrete subclasses must implement the iterator interface, with
* getCurrent returning the axis relative position and getValue the label
* that will be displayed
*/
interface AxisUnit extends Iterator
{
/**
* Add a dataset to this AxisUnit, required for dynamic min and max vlaues
*
@ -46,4 +47,11 @@ interface AxisUnit extends Iterator
* @param int $max The new maximum value
*/
public function setMax($max);
/**
* Get the amount of ticks of this axis
*
* @return int
*/
public function getTicks();
}

View File

@ -9,7 +9,6 @@ namespace Icinga\Chart\Unit;
*/
class LinearUnit implements AxisUnit
{
/**
* The minimum value to display
*
@ -43,7 +42,7 @@ class LinearUnit implements AxisUnit
*
* @var int
*/
private $nrOfTicks = 10;
protected $nrOfTicks = 10;
/**
* The currently displayed tick
@ -95,45 +94,13 @@ class LinearUnit implements AxisUnit
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;
}
if ($this->max === $this->min) {
$this->max = $this->min + 10;
}
return $steps[$step] * $factor;
$this->nrOfTicks = $this->max - $this->min;
return $this;
}
/**
@ -149,7 +116,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->nrOfTicks;
}
}
@ -211,7 +178,6 @@ class LinearUnit implements AxisUnit
if ($max !== null) {
$this->max = $max;
$this->staticMax = true;
$this->updateMaxValue();
}
}
@ -225,7 +191,6 @@ class LinearUnit implements AxisUnit
if ($min !== null) {
$this->min = $min;
$this->staticMin = true;
$this->updateMaxValue();
}
}
@ -248,4 +213,14 @@ class LinearUnit implements AxisUnit
{
return $this->max;
}
/**
* Get the amount of ticks necessary to display this AxisUnit
*
* @return int
*/
public function getTicks()
{
return $this->nrOfTicks;
}
}

View File

@ -0,0 +1,264 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Chart\Unit;
/**
* Logarithmic tick distribution over the axis
*
* This class does not use the actual logarithm, but a slightly altered version called the
* Log-Modulo transformation. This is necessary, since a regular logarithmic scale is not able to display negative
* values and zero-points. See <a href="http://blogs.sas.com/content/iml/2014/07/14/log-transformation-of-pos-neg>
* this article </a> for a more detailed description.
*/
class LogarithmicUnit implements AxisUnit
{
/**
* @var int
*/
protected $base;
/**
* @var
*/
protected $currentTick;
/**
* @var
*/
protected $minExp;
/**
* @var
*/
protected $maxExp;
/**
* True when the minimum value is static and isn't affected by the data set
*
* @var bool
*/
protected $staticMin = false;
/**
* True when the maximum value is static and isn't affected by the data set
*
* @var bool
*/
protected $staticMax = false;
/**
* Create and initialize this AxisUnit
*
* @param int $nrOfTicks The number of ticks to use
*/
public function __construct($base = 10)
{;
$this->base = $base;
$this->minExp = PHP_INT_MAX;
$this->maxExp = ~PHP_INT_MAX;
}
/**
* Add a dataset and calculate the minimum and maximum value for this AxisUnit
*
* @param array $dataset The dataset to add
* @param int $idx The idx (0 for x, 1 for y)
*
* @return self Fluent interface
*/
public function addValues(array $dataset, $idx = 0)
{
$datapoints = array();
foreach ($dataset['data'] as $points) {
$datapoints[] = $points[$idx];
}
if (empty($datapoints)) {
return $this;
}
sort($datapoints);
if (!$this->staticMax) {
$this->maxExp = max($this->maxExp, $this->logCeil($datapoints[count($datapoints) - 1]));
}
if (!$this->staticMin) {
$this->minExp = min($this->minExp, $this->logFloor($datapoints[0]));
}
$this->currentTick = 0;
return $this;
}
/**
* Transform the absolute value to an axis relative value
*
* @param int $value The absolute coordinate from the data set
* @return float|int The axis relative coordinate (between 0 and 100)
*/
public function transform($value)
{
if ($value < $this->pow($this->minExp)) {
return 0;
} elseif ($value > $this->pow($this->maxExp)) {
return 100;
} else {
return 100 * ($this->log($value) - $this->minExp) / $this->getTicks();
}
}
/**
* Return the position of the current tick
*
* @return int
*/
public function current()
{
return $this->currentTick * (100 / $this->getTicks());
}
/**
* Calculate the next tick and tick value
*/
public function next()
{
++ $this->currentTick;
}
/**
* Return the label for the current tick
*
* @return string The label for the current tick
*/
public function key()
{
$currentBase = $this->currentTick + $this->minExp;
if (abs($currentBase) > 4) {
return $this->base . 'E' . $currentBase;
}
return (string) intval($this->pow($currentBase));
}
/**
* True when we're at a valid tick (iterator interface)
*
* @return bool
*/
public function valid()
{
return $this->currentTick >= 0 && $this->currentTick < $this->getTicks();
}
/**
* Reset the current tick and label value
*/
public function rewind()
{
$this->currentTick = 0;
}
/**
* Perform a log-modulo transformation
*
* @param $value The value to transform
*
* @return double The transformed value
*/
protected function log($value)
{
$sign = $value > 0 ? 1 : -1;
return $sign * log1p($sign * $value) / log($this->base);
}
/**
* Calculate the biggest exponent necessary to display the given data point
*
* @param $value
*
* @return float
*/
protected function logCeil($value)
{
return ceil($this->log($value)) + 1;
}
/**
* Calculate the smallest exponent necessary to display the given data point
*
* @param $value
*
* @return float
*/
protected function logFloor($value)
{
return floor($this->log($value));
}
/**
* Inverse function to the log-modulo transformation
*
* @param $value
*
* @return double
*/
protected function pow($value)
{
if ($value == 0) {
return 0;
}
$sign = $value > 0 ? 1 : -1;
return $sign * (pow($this->base, $sign * $value));
}
/**
* Set the axis minimum value to a fixed value
*
* @param int $min The new minimum value
*/
public function setMin($min)
{
$this->minExp = $this->logFloor($min);
$this->staticMin = true;
}
/**
* Set the axis maximum value to a fixed value
*
* @param int $max The new maximum value
*/
public function setMax($max)
{
$this->maxExp = $this->logCeil($max);
$this->staticMax = true;
}
/**
* Return the current minimum value of the axis
*
* @return int The minimum set for this axis
*/
public function getMin()
{
return $this->pow($this->minExp);
}
/**
* Return the current maximum value of the axis
*
* @return int The maximum set for this axis
*/
public function getMax()
{
return $this->pow($this->maxExp);
}
/**
* Get the amount of ticks necessary to display this AxisUnit
*
* @return int
*/
public function getTicks()
{
return $this->maxExp - $this->minExp;
}
}

View File

@ -118,4 +118,14 @@ class StaticAxis implements AxisUnit
{
return reset($this->items);
}
/**
* Get the amount of ticks of this axis
*
* @return int
*/
public function getTicks()
{
return count($this->items);
}
}

View File

@ -16,6 +16,10 @@ abstract class Command
{
protected $app;
protected $docs;
/**
* @type Params
*/
protected $params;
protected $screen;
protected $isVerbose;
@ -124,7 +128,7 @@ abstract class Command
public function fail($msg)
{
throw new IcingaException('%s', $msg);
throw new IcingaException($msg);
}
public function getDefaultActionName()

View File

@ -8,6 +8,11 @@ use Icinga\Data\Filter\Filter;
/**
* Interface for filtering a result set
*
* @deprecated(EL): addFilter and applyFilter do the same in all usages.
* addFilter could be replaced w/ getFilter()->add(). We must no require classes implementing this interface to
* implement redundant methods over and over again. This interface must be moved to the namespace Icinga\Data\Filter.
* It lacks documentation.
*/
interface Filterable
{

View File

@ -0,0 +1,71 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\File;
use FilterIterator;
use Iterator;
/**
* Iterator over files having a specific file extension
*
* Usage example:
* <code>
* <?php
*
* namespace Icinga\Example;
*
* use RecursiveDirectoryIterator;
* use RecursiveIteratorIterator;
* use Icinga\File\FileExtensionFilterIterator;
*
* $markdownFiles = new FileExtensionFilterIterator(
* new RecursiveIteratorIterator(
* new RecursiveDirectoryIterator(__DIR__),
* RecursiveIteratorIterator::SELF_FIRST
* ),
* 'md'
* );
* </code>
*/
class FileExtensionFilterIterator extends FilterIterator
{
/**
* The extension to filter for
*
* @type string
*/
protected $extension;
/**
* Create a new FileExtensionFilterIterator
*
* @param Iterator $iterator Apply filter to this iterator
* @param string $extension The file extension to filter for. The file extension may not contain the leading dot
*/
public function __construct(Iterator $iterator, $extension)
{
$this->extension = '.' . ltrim(strtolower((string) $extension), '.');
parent::__construct($iterator);
}
/**
* Accept files which match the file extension to filter for
*
* @return bool Whether the current element of the iterator is acceptable
* through this filter
*/
public function accept()
{
$current = $this->current();
/* @var $current \SplFileInfo */
if (! $current->isFile()) {
return false;
}
// SplFileInfo::getExtension() is only available since PHP 5 >= 5.3.6
$filename = $current->getFilename();
$sfx = substr($filename, -strlen($this->extension));
return $sfx === false ? false : strtolower($sfx) === $this->extension;
}
}

View File

@ -27,7 +27,7 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
*
* @var int
*/
public static $fileMode = 0664;
public static $fileMode = 0660;
/**
* Create a new INI writer
@ -90,11 +90,9 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
if ($setMode) {
$mode = isset($this->options['filemode']) ? $this->options['filemode'] : static::$fileMode;
$old = umask(0); // Make sure that the mode we're going to set doesn't get mangled
if (is_int($mode) && false === @chmod($filePath, $mode)) {
throw new Zend_Config_Exception(sprintf('Failed to set file mode "%o" on file "%s"', $mode, $filePath));
}
umask($old);
}
}
@ -234,7 +232,7 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
/**
* Getter for filename
*
*
* @return string
*/
public function getFilename()

View File

@ -0,0 +1,49 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}
namespace Icinga\File;
use FilterIterator;
/**
* Iterator over non-empty files
*
* Usage example:
* <code>
* <?php
*
* namespace Icinga\Example;
*
* use RecursiveDirectoryIterator;
* use RecursiveIteratorIterator;
* use Icinga\File\NonEmptyFilterIterator;
*
* $nonEmptyFiles = new NonEmptyFileIterator(
* new RecursiveIteratorIterator(
* new RecursiveDirectoryIterator(__DIR__),
* RecursiveIteratorIterator::SELF_FIRST
* )
* );
* </code>
*/
class NonEmptyFileIterator extends FilterIterator
{
/**
* Accept non-empty files
*
* @return bool Whether the current element of the iterator is acceptable
* through this filter
*/
public function accept()
{
$current = $this->current();
/** @type $current \SplFileInfo */
if (! $current->isFile()
|| $current->getSize() === 0
) {
return false;
}
return true;
}
}

View File

@ -4,6 +4,7 @@
namespace Icinga\Protocol\Ldap;
use Exception;
use Icinga\Protocol\Ldap\Exception as LdapException;
use Icinga\Application\Platform;
use Icinga\Application\Config;
@ -97,6 +98,9 @@ class Connection
protected $namingContexts;
protected $discoverySuccess = false;
protected $lastResult;
protected $pageCookie;
/**
* Constructor
*
@ -238,7 +242,8 @@ class Connection
*/
public function fetchRow($query, $fields = array())
{
// TODO: This is ugly, make it better!
$query = clone $query;
$query->limit(1);
$results = $this->fetchAll($query, $fields);
return array_shift($results);
}
@ -250,44 +255,56 @@ class Connection
*/
public function count(Query $query)
{
$results = $this->runQuery($query, '+');
if (! $results) {
return 0;
$this->connect();
$this->bind();
$count = 0;
$results = $this->runQuery($query);
while (! empty($results)) {
$count += ldap_count_entries($this->ds, $results);
$results = $this->runQuery($query);
}
return ldap_count_entries($this->ds, $results);
return $count;
}
public function fetchAll($query, $fields = array())
public function fetchAll(Query $query, $fields = array())
{
$offset = null;
$limit = null;
$this->connect();
$this->bind();
$offset = $limit = null;
if ($query->hasLimit()) {
$offset = $query->getOffset();
$limit = $query->getLimit();
$limit = $query->getLimit();
}
$count = 0;
$entries = array();
$results = $this->runQuery($query, $fields);
if (! $results) {
return array();
}
$entry = ldap_first_entry($this->ds, $results);
$count = 0;
while ($entry) {
if (($offset === null || $offset <= $count)
&& ($limit === null || ($offset + $limit) >= $count)
) {
$attrs = ldap_get_attributes($this->ds, $entry);
$entries[ldap_get_dn($this->ds, $entry)]
= $this->cleanupAttributes($attrs);
while (! empty($results)) {
$entry = ldap_first_entry($this->ds, $results);
while ($entry) {
$count++;
if (
($offset === null || $offset <= $count)
&& ($limit === null || $limit > count($entries))
) {
$entries[ldap_get_dn($this->ds, $entry)] = $this->cleanupAttributes(
ldap_get_attributes($this->ds, $entry)
);
}
$entry = ldap_next_entry($this->ds, $entry);
}
$count++;
$entry = ldap_next_entry($this->ds, $entry);
$results = $this->runQuery($query, $fields);
}
ldap_free_result($results);
return $entries;
}
public function cleanupAttributes(& $attrs)
protected function cleanupAttributes($attrs)
{
$clean = (object) array();
for ($i = 0; $i < $attrs['count']; $i++) {
@ -303,26 +320,55 @@ class Connection
return $clean;
}
protected function runQuery($query, $fields)
protected function runQuery(Query $query, $fields = array())
{
$this->connect();
$this->bind();
if ($query instanceof Query) {
$fields = $query->listFields();
if ($query->getUsePagedResults() && version_compare(PHP_VERSION, '5.4.0') >= 0) {
if ($this->pageCookie === null) {
$this->pageCookie = '';
} else {
try {
ldap_control_paged_result_response($this->ds, $this->lastResult, $this->pageCookie);
} catch (Exception $e) {
$this->pageCookie = '';
Logger::debug(
'Unable to request paged LDAP results. Does the server allow paged search requests? (%s)',
$e->getMessage()
);
}
ldap_free_result($this->lastResult);
if (! $this->pageCookie) {
$this->pageCookie = $this->lastResult = null;
// Abandon the paged search request so that subsequent requests succeed
ldap_control_paged_result($this->ds, 0);
return false;
}
}
// Does not matter whether we'll use a valid page size here,
// as the server applies its hard limit in case its too high
ldap_control_paged_result(
$this->ds,
$query->hasLimit() ? $query->getLimit() : 500,
true,
$this->pageCookie
);
} elseif ($this->lastResult !== null) {
ldap_free_result($this->lastResult);
$this->lastResult = null;
return false;
}
// WARNING:
// We do not support pagination right now, and there is no chance to
// do so for PHP < 5.4. Warnings about "Sizelimit exceeded" will
// therefore not be hidden right now.
$base = $query->hasBase() ? $query->getBase() : $this->root_dn;
$results = @ldap_search(
$this->ds,
$base,
$query->create(),
$fields,
empty($fields) ? $query->listFields() : $fields,
0, // Attributes and values
0 // No limit - at least where possible
$query->hasLimit() ? $query->getOffset() + $query->getLimit() : 0 // No limit - at least where possible
);
if ($results === false) {
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
return false;
@ -336,12 +382,12 @@ class Connection
)
);
}
$list = array();
if ($query instanceof Query) {
foreach ($query->getSortColumns() as $col) {
ldap_sort($this->ds, $results, $col[0]);
}
foreach ($query->getSortColumns() as $col) {
ldap_sort($this->ds, $results, $col[0]);
}
$this->lastResult = $results;
return $results;
}

View File

@ -33,6 +33,7 @@ class Query
protected $sort_columns = array();
protected $count;
protected $base;
protected $usePagedResults = true;
/**
* Constructor
@ -61,6 +62,17 @@ class Query
return $this->base;
}
public function setUsePagedResults($state = true)
{
$this->usePagedResults = (bool) $state;
return $this;
}
public function getUsePagedResults()
{
return $this->usePagedResults;
}
/**
* Count result set, ignoring limits
*

View File

@ -197,8 +197,8 @@ class User
*/
public function setPermissions(array $permissions)
{
natcasesort($permissions);
if (! empty($permissions)) {
natcasesort($permissions);
$this->permissions = array_combine($permissions, $permissions);
}
return $this;

View File

@ -26,7 +26,7 @@ use Icinga\Data\Db\DbConnection;
* // Create a INI store
* $store = PreferencesStore::create(
* new ConfigObject(
* 'type' => 'ini',
* 'store' => 'ini',
* 'config_path' => '/path/to/preferences'
* ),
* $user // Instance of \Icinga\User
@ -117,13 +117,7 @@ abstract class PreferencesStore
*/
public static function create(ConfigObject $config, User $user)
{
if (($type = $config->type) === null) {
throw new ConfigurationError(
'Preferences configuration is missing the type directive'
);
}
$type = ucfirst(strtolower($type));
$type = ucfirst(strtolower($config->get('store', 'ini')));
$storeClass = 'Icinga\\User\\Preferences\\Store\\' . $type . 'Store';
if (!class_exists($storeClass)) {
throw new ConfigurationError(

View File

@ -36,4 +36,22 @@ class String
{
return str_replace(' ', '', ucwords(str_replace($separator, ' ', strtolower($name))));
}
/**
* Add ellipsis when a string is longer than max length
*
* @param string $string
* @param int $maxLength
* @param string $ellipsis
*
* @return string
*/
public static function ellipsis($string, $maxLength, $ellipsis = '...')
{
if (strlen($string) > $maxLength) {
return substr($string, 0, $maxLength - strlen($ellipsis)) . $ellipsis;
}
return $string;
}
}

View File

@ -5,7 +5,7 @@
namespace Icinga\Web\Controller;
use Exception;
use Icinga\Authentication\Manager as AuthManager;
use Icinga\Authentication\Manager;
use Icinga\Application\Benchmark;
use Icinga\Application\Config;
use Icinga\Exception\IcingaException;
@ -47,6 +47,11 @@ class ActionController extends Zend_Controller_Action
private $xhrLayout = 'inline';
/**
* Authentication manager
*
* @type \Icinga\Authentication\Manager|null
*/
private $auth;
protected $params;
@ -101,6 +106,49 @@ class ActionController extends Zend_Controller_Action
{
}
/**
* Get the authentication manager
*
* @return Manager
*/
public function Auth()
{
if ($this->auth === null) {
$this->auth = Manager::getInstance();
}
return $this->auth;
}
/**
* Whether the current user has the given permission
*
* @param string $permission Name of the permission
*
* @return bool
*/
public function hasPermission($permission)
{
return $this->Auth()->hasPermission($permission);
}
/**
* Throw an exception if user lacks the given permission
*
* @param string $name Permission name
* @throws Exception
*/
public function assertPermission($name)
{
if (! $this->Auth()->hasPermission($name)) {
// TODO: Shall this be an Auth Exception? Or a 404?
throw new IcingaException(
'Auth error, no permission for "%s"',
$name
);
}
}
public function Config($file = null)
{
if ($file === null) {
@ -110,14 +158,6 @@ class ActionController extends Zend_Controller_Action
}
}
public function Auth()
{
if ($this->auth === null) {
$this->auth = AuthManager::getInstance();
}
return $this->auth;
}
public function Window()
{
if ($this->window === null) {
@ -146,6 +186,22 @@ class ActionController extends Zend_Controller_Action
return $this;
}
/**
* Respond with HTTP 405 if the current request's method is not one of the given methods
*
* @param string $httpMethod Unlimited number of allowed HTTP methods
*
* @throws \Zend_Controller_Action_Exception If the request method is not one of the given methods
*/
public function assertHttpMethod($httpMethod)
{
$httpMethods = array_flip(array_map('strtoupper', func_get_args()));
if (! isset($httpMethods[$this->getRequest()->getMethod()])) {
$this->getResponse()->setHeader('Allow', implode(', ', array_keys($httpMethods)));
throw new \Zend_Controller_Action_Exception($this->translate('Method Not Allowed'), 405);
}
}
/**
* Return restriction information for an eventually authenticated user
*
@ -157,34 +213,6 @@ class ActionController extends Zend_Controller_Action
return $this->Auth()->getRestrictions($name);
}
/**
* Whether the user currently authenticated has the given permission
*
* @param string $name Permission name
* @return bool
*/
public function hasPermission($name)
{
return $this->Auth()->hasPermission($name);
}
/**
* Throws an exception if user lacks the given permission
*
* @param string $name Permission name
* @throws Exception
*/
public function assertPermission($name)
{
if (! $this->Auth()->hasPermission($name)) {
// TODO: Shall this be an Auth Exception? Or a 404?
throw new IcingaException(
'Auth error, no permission for "%s"',
$name
);
}
}
/**
* Check whether the controller requires a login. That is when the controller requires authentication and the
* user is currently not authenticated
@ -270,7 +298,7 @@ class ActionController extends Zend_Controller_Action
/**
* Redirect to the login path
*
* @param string $afterLogin The action to call when the login was successful. Defaults to '/index/welcome'
* @param Url $afterLogin The action to call when the login was successful. Defaults to '/index/welcome'
*
* @throws \Exception
*/

View File

@ -512,7 +512,7 @@ class Form extends Zend_Form
$el = parent::createElement($type, $name, $options);
if (($description = $el->getDescription()) !== null && ($label = $el->getDecorator('label')) !== null) {
if (($description = $el->getDescription()) !== null && ($label = $el->getDecorator('label')) !== false) {
$label->setOptions(array(
'title' => $description,
'class' => 'has-feedback'
@ -805,6 +805,24 @@ class Form extends Zend_Form
return array();
}
/**
* Get the translation domain for this form
*
* The returned translation domain is either determined based on this form's qualified name or it is the default
* 'icinga' domain
*
* @return string
*/
protected function getTranslationDomain()
{
$parts = explode('\\', get_called_class());
if ($parts[1] === 'Module') {
// Assume format Icinga\Module\ModuleName\Forms\...
return strtolower($parts[2]);
}
return 'icinga';
}
/**
* Translate a string
*
@ -815,7 +833,7 @@ class Form extends Zend_Form
*/
protected function translate($text, $context = null)
{
return Translator::translate($text, $this->request->getModuleName(), $context);
return Translator::translate($text, $this->getTranslationDomain(), $context);
}
/**
@ -834,7 +852,7 @@ class Form extends Zend_Form
$textSingular,
$textPlural,
$number,
$this->request->getModuleName(),
$this->getTranslationDomain(),
$context
);
}

View File

@ -43,12 +43,6 @@ class LessCompiler
{
require_once 'lessphp/lessc.inc.php';
$this->lessc = new lessc();
$this->lessc->setVariables(
array(
'baseurl' => '\'' . Zend_Controller_Front::getInstance()->getBaseUrl(). '\''
)
);
}
public function compress()

View File

@ -248,7 +248,8 @@ class Menu implements RecursiveIterator
$section->add(t('Logout'), array(
'url' => 'authentication/logout',
'priority' => 700
'priority' => 700,
'renderer' => 'ForeignMenuItemRenderer'
));
}
}
@ -366,7 +367,7 @@ class Menu implements RecursiveIterator
/**
* Return the url of this menu
*
* @return string
* @return Url
*/
public function getUrl()
{

View File

@ -0,0 +1,24 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Menu;
use Icinga\Web\Menu;
use Icinga\Web\Url;
/**
* A menu item with a link that surpasses the regular navigation link behavior
*/
class ForeignMenuItemRenderer implements MenuItemRenderer {
public function render(Menu $menu)
{
return sprintf(
'<a href="%s" target="_self">%s%s<span></span></a>',
$menu->getUrl() ?: '#',
$menu->getIcon() ? '<img src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
htmlspecialchars($menu->getTitle())
);
}
}

5
library/Icinga/Web/Session/PhpSession.php Normal file → Executable file
View File

@ -78,8 +78,9 @@ class PhpSession extends Session
}
}
if (!is_writable(session_save_path())) {
throw new ConfigurationError('Can\'t save session');
$sessionSavePath = session_save_path();
if (session_module_name() === 'files' && !is_writable($sessionSavePath)) {
throw new ConfigurationError("Can't save session, path '$sessionSavePath' is not writable.");
}
if ($this->exists()) {

View File

@ -96,6 +96,7 @@ class StyleSheet
$cache->send($cacheFile);
return;
}
$less = new LessCompiler();
foreach ($lessFiles as $file) {
$less->addFile($file);

View File

@ -4,10 +4,11 @@
namespace Icinga\Web;
use Closure;
use Zend_View_Abstract;
use Icinga\Authentication\Manager;
use Icinga\Exception\ProgrammingError;
use Icinga\Util\Translator;
use Zend_View_Abstract;
use Closure;
/**
* Icinga view
@ -36,6 +37,13 @@ class View extends Zend_View_Abstract
*/
private $helperFunctions = array();
/**
* Authentication manager
*
* @type \Icinga\Authentication\Manager|null
*/
private $auth;
/**
* Create a new view object
*
@ -154,6 +162,31 @@ class View extends Zend_View_Abstract
}
}
/**
* Get the authentication manager
*
* @return Manager
*/
public function Auth()
{
if ($this->auth === null) {
$this->auth = Manager::getInstance();
}
return $this->auth;
}
/**
* Whether the current user has the given permission
*
* @param string $permission Name of the permission
*
* @return bool
*/
public function hasPermission($permission)
{
return $this->Auth()->hasPermission($permission);
}
/**
* Use to include the view script in a scope that only allows public
* members.

View File

@ -0,0 +1,8 @@
<?php
namespace Icinga\Web\View;
use Icinga\Util\String;
$this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis = '...') {
return String::ellipsis($string, $maxLength, $ellipsis);
});

View File

@ -5,6 +5,7 @@
namespace Icinga\Web\Widget\Chart;
use Icinga\Chart\PieChart;
use Icinga\Module\Monitoring\Plugin\PerfdataSet;
use Icinga\Web\Widget\AbstractWidget;
use Icinga\Web\Url;
use Icinga\Util\Format;
@ -28,36 +29,23 @@ class InlinePie extends AbstractWidget
const NUMBER_FORMAT_RATIO = 'ratio';
/**
* The template string used for rendering this widget
* The template string used for rendering this widget
*
* @var string
*/
private $template =<<<'EOD'
<span
class="sparkline"
sparkTitle="{title}"
sparkWidth="{width}"
sparkHeight="{height}"
sparkBorderWidth="{borderWidth}"
sparkBorderColor="{borderColor}"
sparkTooltipChartTitle="{title}"
style="{style}"
labels="{labels}"
formatted="{formatted}"
hideEmptyLabel={hideEmptyLabel}
values="{data}"
tooltipFormat="{tooltipFormat}"
sparkSliceColors="[{colors}]"
sparkType="pie"></span>
<noscript>
<img class="inlinepie"
title="{title}" src="{url}" style="position: relative; top: 10px; width: {width}px; height: {height}px; {style}"
data-icinga-colors="{colors}" data-icinga-values="{data}"
/>
</noscript>
<span sparkType="pie" class="sparkline {class}" {title} {size} sparkSliceColors="[{colors}]" values="{data}">
</span>
{noscript}
EOD;
private $noscript =<<<'EOD'
<noscript>
<img class="inlinepie {class}" {title} src="{url}" data-icinga-colors="{colors}" data-icinga-values="{data}"/>
</noscript>
EOD;
/**
* @var Url
*/
@ -68,35 +56,7 @@ EOD;
*
* @var array
*/
private $colors = array('#44bb77', '#ffaa44', '#ff5566', '#ddccdd');
/**
* The width of the rendered chart
*
* @var int The value in px
*/
private $width = 16;
/**
* The height of the rendered chart
*
* @var int The value in px
*/
private $height = 16;
/**
* PieChart border width
*
* @var float
*/
private $borderWidth = 1;
/**
* The color of the border
*
* @var string
*/
private $borderColor = '#fff';
private $colors = array('#049BAF', '#ffaa44', '#ff5566', '#ddccdd');
/**
* The title of the chart
@ -106,11 +66,9 @@ EOD;
private $title;
/**
* The style for the HtmlElement
*
* @var string
* @var
*/
private $style = '';
private $size;
/**
* The data displayed by the pie-chart
@ -120,45 +78,9 @@ EOD;
private $data;
/**
* The labels to display for each data set
*
* @var array
* @var
*/
private $labels = array();
/**
* If the tooltip for the "empty" area should be hidden
*
* @var bool
*/
private $hideEmptyLabel = false;
/**
* The format string used to display tooltips
*
* @var string
*/
private $tooltipFormat = '<b>{{title}}</b></br> {{label}}: {{formatted}} ({{percent}}%)';
/**
* The number format used to render numeric values in tooltips
*
* @var array
*/
private $format = self::NUMBER_FORMAT_NONE;
/**
* Set if the tooltip for the empty area should be hidden
*
* @param bool $hide Whether to hide the empty area
*
* @return $this
*/
public function setHideEmptyLabel($hide = true)
{
$this->hideEmptyLabel = $hide;
return $this;
}
private $class = '';
/**
* Set the data to be displayed.
@ -175,24 +97,36 @@ EOD;
}
/**
* The labels to be displayed in the pie-chart
* Set the size of the inline pie
*
* @param mixed $label The label of the displayed value, or null for no labels
* @param int $size Sets both, the height and width
*
* @return $this
* @return $this
*/
public function setLabel($label)
public function setSize($size = null)
{
if (is_array($label)) {
$this->url->setParam('labels', implode(',', array_keys($label)));
} elseif ($label != null) {
$labelArr = array($label, $label, $label, '');
$this->url->setParam('labels', implode(',', $labelArr));
$label = $labelArr;
} else {
$this->url->removeKey('labels');
}
$this->labels = $label;
$this->size = $size;
return $this;
}
/**
* Do not display the NoScript fallback html
*/
public function disableNoScript()
{
$this->noscript = '';
}
/**
* Set the class to define the
*
* @param $class
*
* @return $this
*/
public function setSparklineClass($class)
{
$this->class = $class;
return $this;
}
@ -214,105 +148,6 @@ EOD;
return $this;
}
/**
* Set the used number format
*
* @param $format string 'bytes' or 'time'
*
* @return $this
*/
public function setNumberFormat($format)
{
$this->format = $format;
return $this;
}
/**
* A format string used to render the content of the piechart tooltips
*
* Placeholders using curly braces '{FOO}' are replace with their specific values. The format
* String may contain HTML-Markup. The available replaceable values are:
* <ul>
* <li><b>label</b>: The description for the current value </li>
* <li><b>formatted</b>: A string representing the formatted value </li>
* <li><b>value</b>: The raw (non-formatted) value used to render the piechart </li>
* <li><b>percent</b>: The percentage of the current value </li>
* </ul>
* Note: Changes will only affect JavaScript sparklines and not the SVG charts used for fallback
*
* @param $format
*
* @return $this
*/
public function setTooltipFormat($format)
{
$this->tooltipFormat = $format;
return $this;
}
/**
* Set the height
*
* @param $height
*
* @return $this
*/
public function setHeight($height)
{
$this->height = $height;
return $this;
}
/**
* Set the border width of the pie chart
*
* @param float $width Width in px
*
* @return $this
*/
public function setBorderWidth($width)
{
$this->borderWidth = $width;
return $this;
}
/**
* Set the color of the pie chart border
*
* @param string $col The color string
*
* @return $this
*/
public function setBorderColor($col)
{
$this->borderColor = $col;
}
/**
* Set the width
*
* @param $width
*
* @return $this
*/
public function setWidth($width)
{
$this->width = $width;
return $this;
}
/**
* Set the styling of the created HtmlElement
*
* @param string $style
*
* @return $this
*/
public function setStyle($style)
{
$this->style = $style;
}
/**
* Set the title of the displayed Data
*
@ -322,7 +157,7 @@ EOD;
*/
public function setTitle($title)
{
$this->title = $title;
$this->title = 'title="' . htmlspecialchars($title) . '"';
return $this;
}
@ -335,13 +170,10 @@ EOD;
*/
public function __construct(array $data, $title, $colors = null)
{
$this->title = $title;
$this->setTitle($title);
$this->url = Url::fromPath('svg/chart.php');
if (array_key_exists('data', $data)) {
$this->data = $data['data'];
if (array_key_exists('labels', $data)) {
$this->labels = $data['labels'];
}
if (array_key_exists('colors', $data)) {
$this->colors = $data['colors'];
}
@ -354,21 +186,6 @@ EOD;
$this->setColors($this->colors);
}
}
/**
* Create a serialization containing the current label array
*
* @return string A serialized array of labels
*/
private function createLabelString ()
{
$labels = $this->labels;
foreach ($labels as $key => $label) {
$labels[$key] = str_replace('|', '', $label);
}
return isset($this->labels) && is_array($this->labels) ? implode('|', $this->labels) : '';
}
/**
* Renders this widget via the given view and returns the
* HTML as a string
@ -382,11 +199,11 @@ EOD;
$pie->alignTopLeft();
$pie->disableLegend();
$pie->drawPie(array(
'data' => $this->data, 'colors' => $this->colors, 'labels' => $this->labels
'data' => $this->data, 'colors' => $this->colors
));
try {
$png = $pie->toPng($this->width, $this->height);
$png = $pie->toPng($this->size, $this->size);
return '<img class="inlinepie" src="data:image/png;base64,' . base64_encode($png) . '" />';
} catch (IcingaException $_) {
return '';
@ -394,17 +211,17 @@ EOD;
}
$template = $this->template;
// TODO: Check whether we are XHR and don't send
$template = str_replace('{noscript}', $this->noscript, $template);
$template = str_replace('{url}', $this->url, $template);
$template = str_replace('{class}', $this->class, $template);
// style
$template = str_replace('{width}', $this->width, $template);
$template = str_replace('{height}', $this->height, $template);
$template = str_replace('{title}', htmlspecialchars($this->title), $template);
$template = str_replace('{style}', $this->style, $template);
$template = str_replace('{size}',
isset($this->size) ? 'sparkWidth="' . $this->size . '" sparkHeight="' . $this->size . '" ' : '', $template);
$template = str_replace('{title}', $this->title, $template);
$template = str_replace('{colors}', implode(',', $this->colors), $template);
$template = str_replace('{borderWidth}', $this->borderWidth, $template);
$template = str_replace('{borderColor}', $this->borderColor, $template);
$template = str_replace('{hideEmptyLabel}', $this->hideEmptyLabel ? 'true' : 'false', $template);
// Locale-ignorant string cast. Please. Do. NOT. Remove. This. Again.
// Problem is that implode respects locales when casting floats. This means
@ -414,38 +231,7 @@ EOD;
$data[] = sprintf('%F', $dat);
}
// values
$formatted = array();
foreach ($this->data as $key => $value) {
$formatted[$key] = $this->formatValue($value);
}
$template = str_replace('{data}', htmlspecialchars(implode(',', $data)), $template);
$template = str_replace('{formatted}', htmlspecialchars(implode('|', $formatted)), $template);
$template = str_replace('{labels}', htmlspecialchars($this->createLabelString()), $template);
$template = str_replace('{tooltipFormat}', $this->tooltipFormat, $template);
return $template;
}
/**
* Format the given value depending on the current value of numberFormat
*
* @param float $value The value to format
*
* @return string The formatted value
*/
private function formatValue($value)
{
if ($this->format === self::NUMBER_FORMAT_NONE) {
return (string)$value;
} elseif ($this->format === self::NUMBER_FORMAT_BYTES) {
return Format::bytes($value);
} elseif ($this->format === self::NUMBER_FORMAT_TIME) {
return Format::duration($value);
} elseif ($this->format === self::NUMBER_FORMAT_RATIO) {
return $value;
} else {
Logger::warning('Unknown format string "' . $this->format . '" for InlinePie, value not formatted.');
return $value;
}
}
}

View File

@ -218,6 +218,25 @@ EOT;
return $this;
}
/**
* Remove a tab
*
* @param string $name
*
* @return self
*/
public function remove($name)
{
if ($this->has($name)) {
unset($this->tabs[$name]);
if (($dropdownIndex = array_search($name, $this->dropdownTabs)) !== false) {
array_splice($this->dropdownTabs, $dropdownIndex, 2);
}
}
return $this;
}
/**
* Add a tab to the dropdown on the right side of the tab-bar.
*

View File

@ -39,6 +39,13 @@ class Wizard
*/
const BTN_PREV = 'btn_prev';
/**
* This wizard's parent
*
* @var Wizard
*/
protected $parent;
/**
* The name of the wizard's current page
*
@ -71,19 +78,55 @@ class Wizard
}
/**
* Return this wizard's parent or null in case it has none
*
* @return Wizard|null
*/
public function getParent()
{
return $this->parent;
}
/**
* Set this wizard's parent
*
* @param Wizard $wizard The parent wizard
*
* @return self
*/
public function setParent(Wizard $wizard)
{
$this->parent = $wizard;
return $this;
}
/**
* Return the pages being part of this wizard
*
* In case this is a nested wizard a flattened array of all contained pages is returned.
*
* @return array
*/
public function getPages()
{
return $this->pages;
$pages = array();
foreach ($this->pages as $page) {
if ($page instanceof self) {
$pages = array_merge($pages, $page->getPages());
} else {
$pages[] = $page;
}
}
return $pages;
}
/**
* Return the page with the given name
*
* Note that it's also possible to retrieve a nested wizard's page by using this method.
*
* @param string $name The name of the page to return
*
* @return null|Form The page or null in case there is no page with the given name
@ -98,22 +141,31 @@ class Wizard
}
/**
* Add a new page to this wizard
* Add a new page or wizard to this wizard
*
* @param Form $page The page to add to the wizard
* @param Form|Wizard $page The page or wizard to add to the wizard
*
* @return self
*/
public function addPage(Form $page)
public function addPage($page)
{
if (! $page instanceof Form && ! $page instanceof self) {
throw InvalidArgumentException(
'The $page argument must be an instance of Icinga\Web\Form '
. 'or Icinga\Web\Wizard but is of type: ' . get_class($page)
);
} elseif ($page instanceof self) {
$page->setParent($this);
}
$this->pages[] = $page;
return $this;
}
/**
* Add multiple pages to this wizard
* Add multiple pages or wizards to this wizard
*
* @param array $pages The pages to add to the wizard
* @param array $pages The pages or wizards to add to the wizard
*
* @return self
*/
@ -148,6 +200,10 @@ class Wizard
*/
public function getCurrentPage()
{
if ($this->parent) {
return $this->parent->getCurrentPage();
}
if ($this->currentPage === null) {
$this->assertHasPages();
$pages = $this->getPages();
@ -202,6 +258,10 @@ class Wizard
{
$page = $this->getCurrentPage();
if (($wizard = $this->findWizard($page)) !== null) {
return $wizard->handleRequest($request);
}
if ($request === null) {
$request = $page->getRequest();
}
@ -238,6 +298,39 @@ class Wizard
return $request;
}
/**
* Return the wizard for the given page or null if its not part of a wizard
*
* @param Form $page The page to return its wizard for
*
* @return Wizard|null
*/
protected function findWizard(Form $page)
{
foreach ($this->getWizards() as $wizard) {
if ($wizard->getPage($page->getName()) === $page) {
return $wizard;
}
}
}
/**
* Return this wizard's child wizards
*
* @return array
*/
protected function getWizards()
{
$wizards = array();
foreach ($this->pages as $pageOrWizard) {
if ($pageOrWizard instanceof self) {
$wizards[] = $pageOrWizard;
}
}
return $wizards;
}
/**
* Return the request data based on given form's request method
*
@ -264,6 +357,10 @@ class Wizard
*/
protected function getRequestedPage(array $requestData)
{
if ($this->parent) {
return $this->parent->getRequestedPage($requestData);
}
if (isset($requestData[static::BTN_NEXT])) {
return $requestData[static::BTN_NEXT];
} elseif (isset($requestData[static::BTN_PREV])) {
@ -280,6 +377,10 @@ class Wizard
*/
protected function getDirection(Request $request = null)
{
if ($this->parent) {
return $this->parent->getDirection($request);
}
$currentPage = $this->getCurrentPage();
if ($request === null) {
@ -299,7 +400,7 @@ class Wizard
/**
* Return the new page to set as current page
*
* Permission is checked by verifying that the requested page's previous page has page data available.
* Permission is checked by verifying that the requested page or its previous page has page data available.
* The requested page is automatically permitted without any checks if the origin page is its previous
* page or one that occurs later in order.
*
@ -312,11 +413,15 @@ class Wizard
*/
protected function getNewPage($requestedPage, Form $originPage)
{
if ($this->parent) {
return $this->parent->getNewPage($requestedPage, $originPage);
}
if (($page = $this->getPage($requestedPage)) !== null) {
$permitted = true;
$pages = $this->getPages();
if (($index = array_search($page, $pages, true)) > 0) {
if (! $this->hasPageData($requestedPage) && ($index = array_search($page, $pages, true)) > 0) {
$previousPage = $pages[$index - 1];
if ($originPage === null || ($previousPage->getName() !== $originPage->getName()
&& array_search($originPage, $pages, true) < $index))
@ -335,6 +440,36 @@ class Wizard
);
}
/**
* Return the next or previous page based on the given one
*
* @param Form $page The page to skip
*
* @return Form
*/
protected function skipPage(Form $page)
{
if ($this->parent) {
return $this->parent->skipPage($page);
}
if ($this->hasPageData($page->getName())) {
$pageData = & $this->getPageData();
unset($pageData[$page->getName()]);
}
$pages = $this->getPages();
if ($this->getDirection() === static::FORWARD) {
$nextPage = $pages[array_search($page, $pages, true) + 1];
$newPage = $this->getNewPage($nextPage->getName(), $page);
} else { // $this->getDirection() === static::BACKWARD
$previousPage = $pages[array_search($page, $pages, true) - 1];
$newPage = $this->getNewPage($previousPage->getName(), $page);
}
return $newPage;
}
/**
* Return whether the given page is this wizard's last page
*
@ -344,10 +479,27 @@ class Wizard
*/
protected function isLastPage(Form $page)
{
if ($this->parent) {
return $this->parent->isLastPage($page);
}
$pages = $this->getPages();
return $page->getName() === end($pages)->getName();
}
/**
* Return whether all of this wizard's pages were visited by the user
*
* The base implementation just verifies that the very last page has page data available.
*
* @return bool
*/
public function isComplete()
{
$pages = $this->getPages();
return $this->hasPageData($pages[count($pages) - 1]->getName());
}
/**
* Set whether this wizard has been completed
*
@ -421,6 +573,10 @@ class Wizard
*/
public function getSession()
{
if ($this->parent) {
return $this->parent->getSession();
}
return Session::getSession()->getNamespace(get_class($this));
}

View File

@ -4,3 +4,4 @@ DESTINATION=.
wget -O ${FILENAME}.tar.gz https://github.com/erusev/parsedown/archive/${RELEASE}.tar.gz
tar xfz ${FILENAME}.tar.gz -C $DESTINATION/ --strip-components 1 $FILENAME/Parsedown.php $FILENAME/LICENSE.txt
chmod 644 $DESTINATION/Parsedown.php
mv LICENSE.txt LICENSE

View File

@ -1,6 +1,7 @@
curl https://codeload.github.com/dompdf/dompdf/tar.gz/v0.6.1 -o dompdf-0.6.1.tar.gz
tar xzf dompdf-0.6.1.tar.gz --strip-components 1 dompdf-0.6.1/{include/*.php,lib,dompdf*.php,LICENSE.LGPL}
rm dompdf-0.6.1.tar.gz
mv LICENSE.LGPL LICENSE
curl https://codeload.github.com/PhenX/php-font-lib/tar.gz/0.3.1 -o php-font-lib-0.3.1.tar.gz
mkdir lib/php-font-lib/classes

View File

@ -8,12 +8,36 @@ use Icinga\Module\Doc\DocController;
class Doc_IcingawebController extends DocController
{
/**
* Get the path to Icinga Web 2's documentation
*
* @return string
*
* @throws Zend_Controller_Action_Exception If Icinga Web 2's documentation is not available
*/
protected function getPath()
{
$path = Icinga::app()->getBaseDir('doc');
if (is_dir($path)) {
return $path;
}
if (($path = $this->Config()->get('documentation', 'icingaweb2')) !== null) {
if (is_dir($path)) {
return $path;
}
}
throw new Zend_Controller_Action_Exception(
$this->translate('Documentation for Icinga Web 2 is not available'),
404
);
}
/**
* View the toc of Icinga Web 2's documentation
*/
public function tocAction()
{
return $this->renderToc(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter');
$this->renderToc($this->getPath(), 'Icinga Web 2', 'doc/icingaweb/chapter');
}
/**
@ -26,12 +50,12 @@ class Doc_IcingawebController extends DocController
$chapterId = $this->getParam('chapterId');
if ($chapterId === null) {
throw new Zend_Controller_Action_Exception(
$this->translate('Missing parameter \'chapterId\''),
sprintf($this->translate('Missing parameter \'%s\''), 'chapterId'),
404
);
}
return $this->renderChapter(
Icinga::app()->getApplicationDir('/../doc'),
$this->renderChapter(
$this->getPath(),
$chapterId,
'doc/icingaweb/toc',
'doc/icingaweb/chapter'
@ -43,6 +67,6 @@ class Doc_IcingawebController extends DocController
*/
public function pdfAction()
{
return $this->renderPdf(Icinga::app()->getApplicationDir('/../doc'), 'Icinga Web 2', 'doc/icingaweb/chapter');
$this->renderPdf($this->getPath(), 'Icinga Web 2', 'doc/icingaweb/chapter');
}
}

View File

@ -9,6 +9,40 @@ use Icinga\Module\Doc\Exception\DocException;
class Doc_ModuleController extends DocController
{
/**
* Get the path to a module documentation
*
* @param string $module The name of the module
* @param string $default The default path
* @param bool $suppressErrors Whether to not throw an exception if the module documentation is not
* available
*
* @return string|null Path to the documentation or null if the module documentation is not
* available and errors are suppressed
*
* @throws Zend_Controller_Action_Exception If the module documentation is not available and errors are not
* suppressed
*/
protected function getPath($module, $default, $suppressErrors = false)
{
if (is_dir($default)) {
return $default;
}
if (($path = $this->Config()->get('documentation', 'modules')) !== null) {
$path = str_replace('{module}', $module, $path);
if (is_dir($path)) {
return $path;
}
}
if ($suppressErrors) {
return null;
}
throw new Zend_Controller_Action_Exception(
sprintf($this->translate('Documentation for module \'%s\' is not available'), $module),
404
);
}
/**
* List modules which are enabled and having the 'doc' directory
*/
@ -16,10 +50,10 @@ class Doc_ModuleController extends DocController
{
$moduleManager = Icinga::app()->getModuleManager();
$modules = array();
foreach (Icinga::app()->getModuleManager()->listEnabledModules() as $enabledModule) {
$docDir = $moduleManager->getModuleDir($enabledModule, '/doc');
if (is_dir($docDir)) {
$modules[] = $enabledModule;
foreach (Icinga::app()->getModuleManager()->listEnabledModules() as $module) {
$path = $this->getPath($module, $moduleManager->getModuleDir($module, '/doc'), true);
if ($path !== null) {
$modules[] = $module;
}
}
$this->view->modules = $modules;
@ -37,7 +71,7 @@ class Doc_ModuleController extends DocController
{
if (empty($moduleName)) {
throw new Zend_Controller_Action_Exception(
$this->translate('Missing parameter \'moduleName\''),
sprintf($this->translate('Missing parameter \'%s\''), 'moduleName'),
404
);
}
@ -63,16 +97,15 @@ class Doc_ModuleController extends DocController
*/
public function tocAction()
{
$moduleName = $this->getParam('moduleName');
$this->assertModuleEnabled($moduleName);
$this->view->moduleName = $moduleName;
$moduleManager = Icinga::app()->getModuleManager();
$module = $this->getParam('moduleName');
$this->assertModuleEnabled($module);
$this->view->moduleName = $module;
try {
return $this->renderToc(
$moduleManager->getModuleDir($moduleName, '/doc'),
$moduleName,
$this->renderToc(
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
$module,
'doc/module/chapter',
array('moduleName' => $moduleName)
array('moduleName' => $module)
);
} catch (DocException $e) {
throw new Zend_Controller_Action_Exception($e->getMessage(), 404);
@ -88,24 +121,23 @@ class Doc_ModuleController extends DocController
*/
public function chapterAction()
{
$moduleName = $this->getParam('moduleName');
$this->assertModuleEnabled($moduleName);
$module = $this->getParam('moduleName');
$this->assertModuleEnabled($module);
$chapterId = $this->getParam('chapterId');
if ($chapterId === null) {
throw new Zend_Controller_Action_Exception(
$this->translate('Missing parameter \'chapterId\''),
sprintf($this->translate('Missing parameter \'%s\''), 'chapterId'),
404
);
}
$this->view->moduleName = $moduleName;
$moduleManager = Icinga::app()->getModuleManager();
$this->view->moduleName = $module;
try {
return $this->renderChapter(
$moduleManager->getModuleDir($moduleName, '/doc'),
$this->renderChapter(
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
$chapterId,
$this->_helper->url->url(array('moduleName' => $moduleName), 'doc/module/toc'),
$this->_helper->url->url(array('moduleName' => $module), 'doc/module/toc'),
'doc/module/chapter',
array('moduleName' => $moduleName)
array('moduleName' => $module)
);
} catch (DocException $e) {
throw new Zend_Controller_Action_Exception($e->getMessage(), 404);
@ -119,14 +151,13 @@ class Doc_ModuleController extends DocController
*/
public function pdfAction()
{
$moduleName = $this->getParam('moduleName');
$this->assertModuleEnabled($moduleName);
$moduleManager = Icinga::app()->getModuleManager();
return $this->renderPdf(
$moduleManager->getModuleDir($moduleName, '/doc'),
$moduleName,
$module = $this->getParam('moduleName');
$this->assertModuleEnabled($module);
$this->renderPdf(
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
$module,
'doc/module/chapter',
array('moduleName' => $moduleName)
array('moduleName' => $module)
);
}
}

View File

@ -2,7 +2,7 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/* @var $this \Icinga\Application\Modules\Module */
/** @type $this \Icinga\Application\Modules\Module */
$section = $this->menuSection($this->translate('Documentation'), array(
'title' => 'Documentation',

View File

@ -0,0 +1,67 @@
# <a id="module-documentation"></a> Writing Module Documentation
![Markdown](/img/doc/doc/markdown.png)
Icinga Web 2 is capable of viewing your module's documentation, if the documentation is written in
[Markdown](http://en.wikipedia.org/wiki/Markdown). Please refer to
[Markdown Syntax Documentation](http://daringfireball.net/projects/markdown/syntax) for Markdown's formatting syntax.
## <a id="location"></a> Where to Put Module Documentation?
By default, your module's Markdown documentation files must be placed in the `doc` directory beneath your module's root
directory, e.g.:
example-module/doc
## <a id="chapters"></a> Chapters
Each Markdown documentation file represents a chapter of your module's documentation. The first found heading inside
each file is the chapter's title. The order of chapters is based on the case insensitive "Natural Order" of your files'
names. <dfn>Natural Order</dfn> means that the file names are ordered in the way which seems natural to humans.
It is best practice to prefix Markdown documentation file names with numbers to ensure that they appear in the correct
order, e.g.:
1-about.md
2-installation.md
3-configuration.md
## <a id="toc"></a> Table Of Contents
The table of contents for your module's documentation is auto-generated based on all found headings inside each
Markdown documentation file.
## <a id="linking"></a> Linking Between Headings
For linking between headings, place an anchor where you want to link to, e.g.:
# <a id="heading"></a> Heading
Please note that anchors have to be unique across all your Markdown documentation files.
Now you can reference the anchor either in the same or **in another** Markdown documentation file, e.g.:
This is a link to [Heading](#heading).
Other tools support linking between headings by giving the filename plus the anchor to link to, e.g.:
This is a link to [About/Heading](1-about.md#heading.md)
This syntax is also supported in Icinga Web 2.
## <a id="images"></a> Including Images
Images must placed in the `img` directory beneath your module's `public` directory, e.g.:
example-module/public/img/doc
Module images can be accessed using the following URL:
{baseURL}/img/{moduleName}/{file} e.g. icingaweb/img/example-module/doc/example.png
Markdown's image syntax is very similar to Markdown's link syntax, but prefixed with an exclamation mark, e.g.:
![Alt text](http://path/to/img.png "Optional Title")
URLs to images inside your Markdown documentation files must be specified without the base URL, e.g.:
![Example](/img/example-module/doc/example.png)

Some files were not shown because too many files have changed in this diff Show More