Merge branch 'master' into feature/translation-plural-8092

This commit is contained in:
Eric Lippmann 2014-12-30 10:22:44 +01:00
commit 973a56c984
80 changed files with 1173 additions and 1041 deletions

View File

@ -1,7 +1,7 @@
---
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

View File

@ -12,4 +12,6 @@ node default {
file { '/etc/profile.d/env.sh':
source => 'puppet:////vagrant/.puppet/files/etc/profile.d/env.sh'
}
@user { vagrant: ensure => present }
User <| title == vagrant |> { groups +> icingaweb }
}

View File

@ -1,3 +1,4 @@
# TODO(el): This module is not reuseable because it relies on vagrant paths
class icingacli {
file { '/usr/local/bin/icingacli':
ensure => link,

2
Vagrantfile vendored
View File

@ -2,7 +2,7 @@
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
VAGRANT_REQUIRED_VERSION = "1.2.0"
VAGRANT_REQUIRED_VERSION = "1.5.0"
# Require 1.2.x at least
if ! defined? Vagrant.require_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\AutoLoginBackend;
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,7 +34,8 @@ 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'));
}
@ -139,7 +141,7 @@ class AuthenticationController extends ActionController
$this->view->errorInfo = $e->getMessage();
}
$this->view->configMissing = is_dir(Config::$configDir) === false;
$this->view->requiresSetup = Icinga::app()->requiresSetup();
}
/**

View File

@ -53,8 +53,10 @@ class AutologinBackendForm extends Form
'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' => '/\@[^$]+$/',
'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'
),
'validators' => array(
new Zend_Validate_Callback(function ($value) {
return @preg_match($value, '') !== false;

View File

@ -67,7 +67,7 @@ class LoggingConfigForm extends Form
'required' => true,
'label' => t('Application Prefix'),
'description' => t('The name of the application by which to prefix syslog messages.'),
'value' => 'icingaweb',
'value' => 'icingaweb2',
'validators' => array(
array(
'Regex',
@ -106,7 +106,7 @@ class LoggingConfigForm extends Form
'required' => true,
'label' => t('File path'),
'description' => t('The full path to the log file to write messages to.'),
'value' => $this->getDefaultLogDir(),
'value' => '/var/log/icingaweb2/icingaweb2.log',
'validators' => array(new WritablePathValidator())
)
);
@ -114,14 +114,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

@ -44,7 +44,7 @@ class LivestatusResourceForm extends Form
'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')
'value' => '/var/run/icinga2/cmd/livestatus'
)
);

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
);
}
}
}
@ -90,13 +103,13 @@ class RoleForm extends ConfigForm
)
)
));
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']
)
);
}
@ -129,6 +142,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;
}
@ -230,6 +252,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

@ -15,10 +15,10 @@
<?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): ?>
<?php if ($requiresSetup): ?>
<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 '
'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.'
),

View File

@ -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

@ -2,21 +2,23 @@
**Choosing the Authentication Method**
With Icinga Web 2 you can authenticate against Active Directory, LDAP, a MySQL or PostgreSQL database or delegate
authentication to the web server. Authentication methods can be chained to set up fallback authentication methods
With Icinga Web 2 you can authenticate against Active Directory, LDAP, a MySQL or a PostgreSQL database or delegate
authentication to the web server.
Authentication methods can be chained to set up fallback authentication methods
or if users are spread over multiple places.
## Configuration
## <a id="authentication-configuration"></a> Configuration
Authentication methods are configured in the INI file **config/authentication.ini**.
Each section in the authentication configuration represents a single authentication method.
The order of entries in the authentication configuration determines the order of the authentication methods.
If the current authentication method errors or the current authentication method does not know the account being
If the current authentication method errors or if the current authentication method does not know the account being
authenticated, the next authentication method will be used.
## External Authentication
### <a id="authentication-configuration-external-authentication"></a> External Authentication
For delegating authentication to the web server simply add `autologin` to your authentication configuration:
@ -27,13 +29,13 @@ backend = autologin
If your web server is not configured for authentication though the `autologin` section has no effect.
## Active Directory or LDAP Authentication
### <a id="authentication-configuration-ad-or-ldap-authentication"></a> Active Directory or LDAP Authentication
If you want to authenticate against Active Directory or LDAP, you have to define a
[LDAP resource](#resources-configuration-ldap) first which will be referenced as data source for the Active Directory
[LDAP resource](#resources-configuration-ldap) which will be referenced as data source for the Active Directory
or LDAP configuration method.
### LDAP
#### <a id="authentication-configuration-ldap-authentication"></a> LDAP
Directive | Description
------------------------|------------
@ -52,7 +54,7 @@ user_class = inetOrgPerson
user_name_attribute = uid
```
### Active Directory
#### <a id="authentication-configuration-ad-authentication"></a> Active Directory
Directive | Description
------------------------|------------
@ -67,10 +69,10 @@ backend = ad
resource = my_ad
```
## Database Authentication
### <a id="authentication-configuration-db-authentication"></a> Database Authentication
If you want to authenticate against a MySQL or PostgreSQL database, you have to define a
[database resource](#resources-configuration-database) first which will be referenced as data source for the database
If you want to authenticate against a MySQL or a PostgreSQL database, you have to define a
[database resource](#resources-configuration-database) which will be referenced as data source for the database
authentication method.
Directive | Description
@ -83,13 +85,31 @@ Directive | Description
```
[auth_ad]
backend = ad
resource = my_db
resource = icingaweb-mysql
```
#### <a id="authentication-configuration-db-setup"></a> Database Setup
For authenticating against a database, you have to import one of the following database schemas:
* **etc/schema/preferences.mysql.sql** (for **MySQL** database)
* **etc/schema/preferences.pgsql.sql** (for **PostgreSQL** databases)
After that you have to define the [database resource](#resources-configuration-database).
**Manually Creating Users**
Icinga Web 2 uses the MD5 based BSD password algorithm. For generating a password hash, please use the following
command:
````
openssl passwd -1 "password"
````
> Note: The switch to `openssl passwd` is the **number one** (`-1`) for using the MD5 based BSD password algorithm.
Insert the user into the database using the generated password hash:
````
INSERT INTO icingaweb_user (name, active, password_hash) VALUES ('icingaadmin', 1, 'hash from openssl');
````

View File

@ -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.
````

View File

@ -1,101 +1,53 @@
# Preferences
# <a id="preferences"></a> Preferences
Preferences are user based configuration for Icinga Web 2. For example max page
items, languages or date time settings can controlled by users.
Preferences are settings a user can set for his account only, for example his language and time zone.
# Architecture
**Choosing Where to Store Preferences**
Preferences are initially loaded from a provider (ini files or database) and
stored into session at login time. After this step preferences are only
persisted to the configured backend, but never reloaded from them.
Preferences can be stored either in INI files or in a MySQL or in a PostgreSQL database. By default, Icinga Web 2 stores
preferences in INI files beneath Icinga Web 2's configuration directory.
# Configuration
## <a id="preferences-configuration"></a> Configuration
Preferences can be configured in config.ini in **preferences** section, default
settings are this:
Where to store preferences is defined in the INI file **config/config.ini** in the *preferences* section.
[preferences]
type=ini
### <a id="preferences-configuration-ini"></a> Store Preferences in INI Files
The ini provider uses the directory **config/preferences** to create one ini
file per user and persists the data into a single file. If you want to drop your
preferences just drop the file from disk and you'll start with a new profile.
If preferences are stored in INI Files, Icinga Web 2 automatically creates one file per user using the username as
file name for storing preferences. A INI file is created once a user saves changed preferences the first time.
The files are located beneath the `preferences` directory beneath Icinga Web 2's configuration directory.
## Database Provider
For storing preferences in INI files you have to add the following section to the INI file **config/config.ini**:
To be more flexible in distributed setups you can store preferences in a
database (pgsql or mysql), a typical configuration looks like the following
example:
```
[preferences]
type = ini
````
[preferences]
type=db
resource=icingaweb-pgsql
### <a id="preferences-configuration-db"></a> Store Preferences in a Database
## Null Provider
In order to be more flexible in distributed setups you can store preferences in a MySQL or in a PostgreSQL database.
For storing preferences in a database, you have to define a [database resource](#resources-configuration-database)
which will be referenced as resource for the preferences storage.
The Null Provider discards all preferences and is mainly used as a fallback when no provider could be
created (due to permission errors, database outtakes, etc.).
Directive | Description
------------------------|------------
**type** | `db`
**resource** | The name of the database resource defined in [resources.ini](resources).
[preferences]
type=null
**Example:**
If your preferences aren't stored it's best to take a look into the logfiles - errors during the preference setup
are displayed as warnings here.
```
[preferences]
type = db
resource = icingaweb-mysql
```
### Settings
#### <a id="preferences-configuration-db-setup"></a> Database Setup
* **resource**: A reference to a database declared in *resources.ini*. Please read the chapter about
resources for a detailed description about how to set up resources.
For storing preferences in a database, you have to import one of the following database schemas:
### Preparation
* **etc/schema/preferences.mysql.sql** (for **MySQL** database)
* **etc/schema/preferences.pgsql.sql** (for **PostgreSQL** databases)
To use this feature you need a running database environment. After creating a
database and a writable user you need to import the initial table file:
* etc/schema/preferences.mysql.sql (for mysql database)
* etc/schema/preferemces.pgsql.sql (for postgres databases)
#### Example for mysql
# mysql -u root -p
mysql> create database icingaweb;
mysql> GRANT SELECT,INSERT,UPDATE,DELETE ON icingaweb.* TO \
'icingaweb'@'localhost' IDENTIFIED BY 'icingaweb';
mysql> exit
# mysql -u root -p icingaweb < /path/to/icingaweb/etc/schema/preferences.mysql.sql
After following these steps above you can configure your preferences provider.
## Coding API
You can set, update or remove preferences using the Preference data object
which is bound to the user. Here are some simple examples how to work with
that:
$preferences = $user->getPreferences();
// Get language with en_US as fallback
$preferences->get('app.language', 'en_US');
$preferences->set('app.language', 'de_DE');
$preferences->remove('app.language');
// Using transactional mode
$preferences->startTransaction();
$preferences->set('test.pref1', 'pref1');
$preferences->set('test.pref2', 'pref2');
$preferences->remove('test.pref3');
$preferemces->commit(); // Stores 3 changes in one operation
More information can be found in the api docs.
## Namespaces and behaviour
If you are using this API please obey the following rules:
* Use dotted notation for preferences
* Namespaces starting with one context identifier
* **app** as global identified (e.g. app.language)
* **mymodule** for your module
* **monitoring** for the monitoring module
* Use preferences wisely (set only when needed and write small settings)
* Use only simple data types, e.g. strings or numbers
* If you need complex types you have to do it your self (e.g. serialization)
After that you have to define the [database resource](#resources-configuration-database).

View File

@ -1,5 +0,0 @@
Icinga Web 2
@link https://www.icinga.org/icingaweb2/
@copyright Copyright (c) 2013-%(YEAR)s Icinga Development Team (https://www.icinga.org)
@license http://www.gnu.org/licenses/gpl-2.0.txt, or any later version

View File

@ -1,237 +1,235 @@
#/**
# * 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
%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
Release: %{revision}%{?dist}
License: GPLv2
Version: 2.0.0
Release: 1.beta2%{?dist}
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
%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
%define basedir %{_datadir}/%{name}
%define bindir %{_bindir}
%define configdir %{_sysconfdir}/%{name}
%define logdir %{_localstatedir}/log/%{name}
%define phpdir %{_datadir}/php
%define icingawebgroup icingaweb2
%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 library
Group: Development/Libraries
Requires: %{php} >= 5.3.0
%description -n php-Icinga
Icinga Web 2 PHP library
%package -n icingacli
Summary: Icinga CLI
Group: Applications/System
Requires: %{name} = %{version}-%{release}
Requires: php-Icinga
Requires: %{name}-common = %{version}-%{release}
Requires: php-Icinga = %{version}-%{release}
Requires: %{php_cli} >= 5.3.0
%description -n icingacli
Icinga CLI using php-Icinga Icinga Web 2 backend.
%package -n php-Icinga
Summary: Icinga Web 2 PHP Libraries
Group: Applications/System
Requires: %{name} = %{version}-%{release}
Requires: %{phpname} >= 5.3.0
Requires: %{phpzendname}
Icinga CLI
%description -n php-Icinga
Icinga Web 2 PHP Libraries required by the web frontend and cli tool.
%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},%{logdir},%{phpdir},%{wwwconfigdir}}
cp -prv application doc var %{buildroot}/%{basedir}
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
%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
usermod -a -G icingacmd,%{icingawebgroup} %{wwwuser}
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}
%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) %{configdir}
%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
%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,44 @@ abstract class ApplicationBootstrap
return $this;
}
/**
* Load the setup module if Icinga Web 2 requires setup
*
* @return $this
*/
protected function loadSetupModuleIfNecessary()
{
if (! @file_exists($this->config->resolvePath('config.ini'))) {
$this->requiresSetup = true;
$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 +454,7 @@ abstract class ApplicationBootstrap
/**
* Load Configuration
*
* @return self
* @return $this
*/
protected function loadConfig()
{
@ -447,7 +473,7 @@ abstract class ApplicationBootstrap
/**
* Error handling configuration
*
* @return self
* @return $this
*/
protected function setupErrorHandling()
{
@ -473,7 +499,7 @@ abstract class ApplicationBootstrap
/**
* Set up logger
*
* @return self
* @return $this
*/
protected function setupLogger()
{
@ -490,7 +516,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

@ -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

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

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

@ -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

@ -23,16 +23,17 @@ class String
}
/**
* Uppercase the first character of each word in a string assuming and removing the underscore as word separator
* Uppercase the first character of each word in a string
*
* Converts 'first_name' to 'firstName' for example.
*
* @param string $name
* @param string $separator Word separator
*
* @return string
*/
public static function cname($name)
public static function cname($name, $separator = '_')
{
return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name))));
return str_replace(' ', '', ucwords(str_replace($separator, ' ', strtolower($name))));
}
}

View File

@ -4,7 +4,6 @@
namespace Icinga\Util;
use Exception;
use Icinga\Exception\IcingaException;
/**
@ -34,8 +33,8 @@ class Translator
*
* Falls back to the default domain in case the string cannot be translated using the given domain
*
* @param string $text The string to translate
* @param string $domain The primary domain to use
* @param string $text The string to translate
* @param string $domain The primary domain to use
* @param string|null $context Optional parameter for context based translation
*
* @return string The translated string
@ -64,7 +63,7 @@ class Translator
*
* @param string $textSingular The string in singular form to translate
* @param string $textPlural The string in plural form to translate
* @param integer $number The number to get the plural or singular string
* @param integer $number The amount to determine from whether to return singular or plural
* @param string $domain The primary domain to use
* @param string|null $context Optional parameter for context based translation
*

View File

@ -270,7 +270,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

@ -9,6 +9,7 @@ use Zend_Config;
use Zend_Form;
use Zend_View_Interface;
use Icinga\Application\Icinga;
use Icinga\Util\Translator;
use Icinga\Web\Form\Decorator\NoScriptApply;
use Icinga\Web\Form\Element\CsrfCounterMeasure;
@ -147,7 +148,7 @@ class Form extends Zend_Form
/**
* Set a callback that is called instead of this form's onSuccess method
*
* It is called using the following signature: (Request $request, Form $form).
* It is called using the following signature: (Form $this).
*
* @param callable $onSuccess Callback
*
@ -804,6 +805,58 @@ 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
*
* @param string $text The string to translate
* @param string|null $context Optional parameter for context based translation
*
* @return string The translated string
*/
protected function translate($text, $context = null)
{
return Translator::translate($text, $this->getTranslationDomain(), $context);
}
/**
* Translate a plural string
*
* @param string $textSingular The string in singular form to translate
* @param string $textPlural The string in plural form to translate
* @param integer $number The amount to determine from whether to return singular or plural
* @param string|null $context Optional parameter for context based translation
*
* @return string The translated string
*/
protected function translatePlural($textSingular, $textPlural, $number, $context = null)
{
return Translator::translatePlural(
$textSingular,
$textPlural,
$number,
$this->getTranslationDomain(),
$context
);
}
/**
* Render this form
*

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

@ -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

@ -4,6 +4,7 @@
namespace Icinga\Web\Widget\Tabextension;
use Icinga\Application\Platform;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tab;
use Icinga\Web\Widget\Tabs;
@ -28,35 +29,6 @@ class OutputFormat implements Tabextension
*/
const TYPE_CSV = 'csv';
/**
* An array containing the tab definitions for all supported types
*
* Using array_keys on this array or isset allows to check whether a
* requested type is supported
*
* @var array
*/
private $supportedTypes = array(
self::TYPE_PDF => array(
'name' => 'pdf',
'title' => 'PDF',
'icon' => 'file-pdf',
'urlParams' => array('format' => 'pdf'),
),
self::TYPE_CSV => array(
'name' => 'csv',
'title' => 'CSV',
'icon' => 'file-excel',
'urlParams' => array('format' => 'csv')
),
self::TYPE_JSON => array(
'name' => 'json',
'title' => 'JSON',
'icon' => 'img/icons/json.png',
'urlParams' => array('format' => 'json')
)
);
/**
* An array of tabs to be added to the dropdown area
*
@ -74,7 +46,7 @@ class OutputFormat implements Tabextension
*/
public function __construct(array $disabled = array())
{
foreach ($this->supportedTypes as $type => $tabConfig) {
foreach ($this->getSupportedTypes() as $type => $tabConfig) {
if (!in_array($type, $disabled)) {
$tabConfig['url'] = Url::fromRequest();
$tabConfig['tagParams'] = array(
@ -98,4 +70,44 @@ class OutputFormat implements Tabextension
$tabs->addAsDropdown($tab->getName(), $tab);
}
}
/**
* Return an array containing the tab definitions for all supported types
*
* Using array_keys on this array or isset allows to check whether a
* requested type is supported
*
* @return array
*/
public function getSupportedTypes()
{
$supportedTypes = array();
if (Platform::extensionLoaded('gd')) {
$supportedTypes[self::TYPE_PDF] = array(
'name' => 'pdf',
'title' => 'PDF',
'icon' => 'file-pdf',
'urlParams' => array('format' => 'pdf'),
);
}
$supportedTypes[self::TYPE_CSV] = array(
'name' => 'csv',
'title' => 'CSV',
'icon' => 'file-excel',
'urlParams' => array('format' => 'csv')
);
if (Platform::extensionLoaded('json')) {
$supportedTypes[self::TYPE_JSON] = array(
'name' => 'json',
'title' => 'JSON',
'icon' => 'img/icons/json.png',
'urlParams' => array('format' => 'json')
);
}
return $supportedTypes;
}
}

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,59 @@
# <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
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).
## <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)

View File

@ -28,7 +28,7 @@ class DocController extends ModuleActionController
$urlParams
);
$this->view->title = $chapterId;
return $this->render('chapter', null, true);
$this->render('chapter', null, true);
}
/**
@ -46,7 +46,7 @@ class DocController extends ModuleActionController
$name = ucfirst($name);
$this->view->docName = $name;
$this->view->title = sprintf($this->translate('%s Documentation'), $name);
return $this->render('toc', null, true);
$this->render('toc', null, true);
}
/**
@ -71,6 +71,6 @@ class DocController extends ModuleActionController
);
$this->view->docName = $name;
$this->_request->setParam('format', 'pdf');
return $this->render('pdf', null, true);
$this->render('pdf', null, true);
}
}

View File

@ -9,6 +9,8 @@ use Countable;
use IteratorAggregate;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use Icinga\File\NonEmptyFileIterator;
use Icinga\File\FileExtensionFilterIterator;
/**
* Iterator over non-empty Markdown files ordered by the case insensitive "natural order" of file names
@ -29,12 +31,14 @@ class DocIterator implements Countable, IteratorAggregate
*/
public function __construct($path)
{
$it = new RecursiveIteratorIterator(
$it = new FileExtensionFilterIterator(
new NonEmptyFileIterator(
new MarkdownFileIterator(
new RecursiveDirectoryIterator($path)
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::SELF_FIRST
)
)
),
'md'
);
// Unfortunately we have no chance to sort the iterator
$fileInfo = iterator_to_array($it);

View File

@ -31,7 +31,7 @@ class DocParser
/**
* Create a new documentation parser for the given path
*
* @param string $path Path to the documentation
* @param string $path Path to the documentation
*
* @throws DocException If the documentation directory does not exist
* @throws NotReadableError If the documentation directory is not readable

View File

@ -1,31 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Module\Doc;
use RecursiveFilterIterator;
/**
* Recursive iterator over Markdown files
*/
class MarkdownFileIterator extends RecursiveFilterIterator
{
/**
* Accept files with '.md' suffix
*
* @return bool Whether the current element of the iterator is acceptable
* through this filter
*/
public function accept()
{
$current = $this->getInnerIterator()->current();
/* @var $current \SplFileInfo */
if (! $current->isFile()) {
return false;
}
$filename = $current->getFilename();
$sfx = substr($filename, -3);
return $sfx === false ? false : strtolower($sfx) === '.md';
}
}

View File

@ -1,31 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}
namespace Icinga\Module\Doc;
use RecursiveFilterIterator;
/**
* Recursive iterator over non-empty files
*/
class NonEmptyFileIterator extends RecursiveFilterIterator
{
/**
* Accept non-empty files
*
* @return bool Whether the current element of the iterator is acceptable
* through this filter
*/
public function accept()
{
$current = $this->getInnerIterator()->current();
/* @var $current \SplFileInfo */
if (! $current->isFile()
|| $current->getSize() === 0
) {
return false;
}
return true;
}
}

View File

@ -1,4 +1,4 @@
Module: doc
Version: 2.0.0~alpha4
Version: 2.0.0
Description: Documentation module
Extracts, shows and exports documentation for Icinga Web 2 and it's modules.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -47,4 +47,3 @@ $this->addRoute('doc/module/chapter', $docModuleChapter);
$this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter);
$this->addRoute('doc/module/toc', $docModuleToc);
$this->addRoute('doc/module/pdf', $docModulePdf);

View File

@ -72,8 +72,10 @@ class Monitoring_HostsController extends Controller
'host_obsessing'*/
));
$unhandledObjects = array();
$unhandledFilterExpressions = array();
$acknowledgedObjects = array();
$objectsInDowntime = array();
$downtimeFilterExpressions = array();
$hostStates = array(
Host::getStateText(Host::STATE_UP) => 0,
Host::getStateText(Host::STATE_DOWN) => 0,
@ -81,15 +83,17 @@ class Monitoring_HostsController extends Controller
Host::getStateText(Host::STATE_PENDING) => 0,
);
foreach ($this->hostList as $host) {
/** @var Service $host */
/** @var Host $host */
if ((bool) $host->problem === true && (bool) $host->handled === false) {
$unhandledObjects[] = $host;
$unhandledFilterExpressions[] = Filter::where('host', $host->getName());
}
if ((bool) $host->acknowledged === true) {
$acknowledgedObjects[] = $host;
}
if ((bool) $host->in_downtime === true) {
$objectsInDowntime[] = $host;
$downtimeFilterExpressions[] = Filter::where('downtime_host', $host->getName());
}
++$hostStates[$host::getStateText($host->state)];
}
@ -108,16 +112,15 @@ class Monitoring_HostsController extends Controller
$this->view->hostStates = $hostStates;
$this->view->objects = $this->hostList;
$this->view->unhandledObjects = $unhandledObjects;
$this->view->acknowledgeUnhandledLink = Url::fromRequest()
->setPath('monitoring/hosts/acknowledge-problem')
->addParams(array('host_problem' => 1, 'host_handled' => 0));
$this->view->downtimeUnhandledLink = Url::fromRequest()
->setPath('monitoring/hosts/schedule-downtime')
->addParams(array('host_problem' => 1, 'host_handled' => 0));
$unhandledFilterQueryString = Filter::matchAny($unhandledFilterExpressions)->toQueryString();
$this->view->acknowledgeUnhandledLink = Url::fromPath('monitoring/hosts/acknowledge-problem')
->setQueryString($unhandledFilterQueryString);
$this->view->downtimeUnhandledLink = Url::fromPath('monitoring/hosts/schedule-downtime')
->setQueryString($unhandledFilterQueryString);
$this->view->acknowledgedObjects = $acknowledgedObjects;
$this->view->objectsInDowntime = $objectsInDowntime;
$this->view->inDowntimeLink = Url::fromRequest()
->setPath('monitoring/list/downtimes');
$this->view->inDowntimeLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString(Filter::matchAny($downtimeFilterExpressions)->toQueryString());
$this->view->havingCommentsLink = Url::fromRequest()
->setPath('monitoring/list/comments');
$this->view->hostStatesPieChart = $this->createPieChart(
@ -131,9 +134,6 @@ class Monitoring_HostsController extends Controller
{
$chart = new InlinePie(array_values($states), $title, $colors);
return $chart
->setLabel(array_map('strtoupper', array_keys($states)))
->setHeight(100)
->setWidth(100)
->setTitle($title);
}

View File

@ -221,7 +221,7 @@ class Monitoring_MultiController extends Controller
private function createPie($states, $colors, $title)
{
$chart = new InlinePie(array_values($states), $title, $colors);
$chart->setLabel(array_keys($states))->setHeight(100)->setWidth(100);
$chart->setLabel(array_keys($states))->setSize(100);
$chart->setTitle($title);
return $chart;
}

View File

@ -74,8 +74,10 @@ class Monitoring_ServicesController extends Controller
'service_obsessing'*/
));
$unhandledObjects = array();
$unhandledFilterExpressions = array();
$acknowledgedObjects = array();
$objectsInDowntime = array();
$downtimeFilterExpressions = array();
$serviceStates = array(
Service::getStateText(Service::STATE_OK) => 0,
Service::getStateText(Service::STATE_WARNING) => 0,
@ -94,12 +96,20 @@ class Monitoring_ServicesController extends Controller
/** @var Service $service */
if ((bool) $service->problem === true && (bool) $service->handled === false) {
$unhandledObjects[] = $service;
$unhandledFilterExpressions[] = Filter::matchAll(
Filter::where('host', $service->getHost()->getName()),
Filter::where('service', $service->getName())
);
}
if ((bool) $service->acknowledged === true) {
$acknowledgedObjects[] = $service;
}
if ((bool) $service->in_downtime === true) {
$objectsInDowntime[] = $service;
$downtimeFilterExpressions[] = Filter::matchAll(
Filter::where('downtime_host', $service->getHost()->getName()),
Filter::where('downtime_service', $service->getName())
);
}
++$serviceStates[$service::getStateText($service->state)];
if (! isset($knownHostStates[$service->getHost()->getName()])) {
@ -125,16 +135,15 @@ class Monitoring_ServicesController extends Controller
$this->view->serviceStates = $serviceStates;
$this->view->objects = $this->serviceList;
$this->view->unhandledObjects = $unhandledObjects;
$this->view->acknowledgeUnhandledLink = Url::fromRequest()
->setPath('monitoring/services/acknowledge-problem')
->addParams(array('service_problem' => 1, 'service_handled' => 0));
$this->view->downtimeUnhandledLink = Url::fromRequest()
->setPath('monitoring/services/schedule-downtime')
->addParams(array('service_problem' => 1, 'service_handled' => 0));
$unhandledFilterQueryString = Filter::matchAny($unhandledFilterExpressions)->toQueryString();
$this->view->acknowledgeUnhandledLink = Url::fromPath('monitoring/services/acknowledge-problem')
->setQueryString($unhandledFilterQueryString);
$this->view->downtimeUnhandledLink = Url::fromPath('monitoring/services/schedule-downtime')
->setQueryString($unhandledFilterQueryString);
$this->view->acknowledgedObjects = $acknowledgedObjects;
$this->view->objectsInDowntime = $objectsInDowntime;
$this->view->inDowntimeLink = Url::fromRequest()
->setPath('monitoring/list/downtimes');
$this->view->inDowntimeLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString(Filter::matchAny($downtimeFilterExpressions)->toQueryString());
$this->view->havingCommentsLink = Url::fromRequest()
->setPath('monitoring/list/comments');
$this->view->serviceStatesPieChart = $this->createPieChart(
@ -153,10 +162,10 @@ class Monitoring_ServicesController extends Controller
{
$chart = new InlinePie(array_values($states), $title, $colors);
return $chart
->setLabel(array_map('strtoupper', array_keys($states)))
->setHeight(100)
->setWidth(100)
->setTitle($title);
// ->setLabel(array_map('strtoupper', array_keys($states)))
->setSize(50)
->setTitle($title)
->setSparklineClass('sparkline-multi');
}
/**

View File

@ -73,9 +73,8 @@ class Monitoring_ShowController extends Controller
public function historyAction()
{
$this->getTabs()->activate('history');
//$this->view->object->populate();
$this->view->object->fetchEventHistory();
$this->view->history = $this->view->object->eventhistory->paginate($this->params->get('limit', 50));
$this->view->history = $this->view->object->eventhistory->getQuery()->paginate($this->params->get('limit', 50));
$this->handleFormatRequest($this->view->object->eventhistory);
$this->fetchHostStats();
}

View File

@ -104,7 +104,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
array('HtmlTag', array('tag' => 'div'))
)
)
);

View File

@ -130,7 +130,7 @@ class ScheduleServiceDowntimeCommandForm extends ObjectsCommandForm
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
array('HtmlTag', array('tag' => 'div'))
)
)
);
@ -169,7 +169,7 @@ class ScheduleServiceDowntimeCommandForm extends ObjectsCommandForm
),
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')),
array('HtmlTag', array('tag' => 'div')),
array(
'Description',
array('tag' => 'span', 'class' => 'description', 'placement' => 'prepend')

View File

@ -5,10 +5,10 @@
namespace Icinga\Module\Monitoring\Forms\Config;
use InvalidArgumentException;
use Icinga\Web\Notification;
use Icinga\Forms\ConfigForm;
use Icinga\Application\Config;
use Icinga\Exception\ConfigurationError;
use Icinga\Forms\ConfigForm;
use Icinga\Web\Notification;
/**
* Form class for creating/modifying monitoring backends
@ -18,7 +18,7 @@ class BackendConfigForm extends ConfigForm
/**
* The available monitoring backend resources split by type
*
* @var array
* @type array
*/
protected $resources;
@ -28,15 +28,15 @@ class BackendConfigForm extends ConfigForm
public function init()
{
$this->setName('form_config_monitoring_backends');
$this->setSubmitLabel(mt('monitoring', 'Save Changes'));
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Set the resource configuration to use
*
* @param Config $resources The resource configuration
* @param Config $resourceConfig The resource configuration
*
* @return self
* @return $this
*
* @throws ConfigurationError In case there are no valid monitoring backend resources
*/
@ -50,7 +50,7 @@ class BackendConfigForm extends ConfigForm
}
if (empty($resources)) {
throw new ConfigurationError(mt('monitoring', 'Could not find any valid monitoring backend resources'));
throw new ConfigurationError($this->translate('Could not find any valid monitoring backend resources'));
}
$this->resources = $resources;
@ -64,7 +64,7 @@ class BackendConfigForm extends ConfigForm
*
* @param array $values The values to extend the configuration with
*
* @return self
* @return $this
*
* @throws InvalidArgumentException In case the backend does already exist
*/
@ -72,9 +72,9 @@ class BackendConfigForm extends ConfigForm
{
$name = isset($values['name']) ? $values['name'] : '';
if (! $name) {
throw new InvalidArgumentException(mt('monitoring', 'Monitoring backend name missing'));
throw new InvalidArgumentException($this->translate('Monitoring backend name missing'));
} elseif ($this->config->hasSection($name)) {
throw new InvalidArgumentException(mt('monitoring', 'Monitoring backend already exists'));
throw new InvalidArgumentException($this->translate('Monitoring backend already exists'));
}
unset($values['name']);
@ -95,11 +95,11 @@ class BackendConfigForm extends ConfigForm
public function edit($name, array $values)
{
if (! $name) {
throw new InvalidArgumentException(mt('monitoring', 'Old monitoring backend name missing'));
throw new InvalidArgumentException($this->translate('Old monitoring backend name missing'));
} elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) {
throw new InvalidArgumentException(mt('monitoring', 'New monitoring backend name missing'));
throw new InvalidArgumentException($this->translate('New monitoring backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(mt('monitoring', 'Unknown monitoring backend provided'));
throw new InvalidArgumentException($this->translate('Unknown monitoring backend provided'));
}
unset($values['name']);
@ -119,9 +119,9 @@ class BackendConfigForm extends ConfigForm
public function remove($name)
{
if (! $name) {
throw new InvalidArgumentException(mt('monitoring', 'Monitoring backend name missing'));
throw new InvalidArgumentException($this->translate('Monitoring backend name missing'));
} elseif (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(mt('monitoring', 'Unknown monitoring backend provided'));
throw new InvalidArgumentException($this->translate('Unknown monitoring backend provided'));
}
$backendConfig = $this->config->getSection($name);
@ -131,23 +131,21 @@ class BackendConfigForm extends ConfigForm
/**
* Add or edit a monitoring backend and save the configuration
*
* @see Form::onSuccess()
*/
public function onSuccess()
{
$monitoringBackend = $this->request->getQuery('backend');
try {
if ($monitoringBackend === null) { // create new backend
if ($monitoringBackend === null) { // Create new backend
$this->add($this->getValues());
$message = mt('monitoring', 'Monitoring backend "%s" has been successfully created');
} else { // edit existing backend
$message = $this->translate('Monitoring backend "%s" has been successfully created');
} else { // Edit existing backend
$this->edit($monitoringBackend, $this->getValues());
$message = mt('monitoring', 'Monitoring backend "%s" has been successfully changed');
$message = $this->translate('Monitoring backend "%s" has been successfully changed');
}
} catch (InvalidArgumentException $e) {
Notification::error($e->getMessage());
return;
return null;
}
if ($this->save()) {
@ -160,18 +158,16 @@ class BackendConfigForm extends ConfigForm
/**
* Populate the form in case a monitoring backend is being edited
*
* @see Form::onRequest()
*
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
* @throws ConfigurationError In case the backend name is missing in the request or is invalid
*/
public function onRequest()
{
$monitoringBackend = $this->request->getQuery('backend');
if ($monitoringBackend !== null) {
if ($monitoringBackend === '') {
throw new ConfigurationError(mt('monitoring', 'Monitoring backend name missing'));
throw new ConfigurationError($this->translate('Monitoring backend name missing'));
} elseif (! $this->config->hasSection($monitoringBackend)) {
throw new ConfigurationError(mt('monitoring', 'Unknown monitoring backend provided'));
throw new ConfigurationError($this->translate('Unknown monitoring backend provided'));
}
$backendConfig = $this->config->getSection($monitoringBackend)->toArray();
@ -181,7 +177,8 @@ class BackendConfigForm extends ConfigForm
}
/**
* @see Form::createElements()
* (non-PHPDoc)
* @see Form::createElements() For the method documentation.
*/
public function createElements(array $formData)
{
@ -200,7 +197,7 @@ class BackendConfigForm extends ConfigForm
'disabled',
array(
'required' => true,
'label' => mt('monitoring', 'Disable This Backend')
'label' => $this->translate('Disable This Backend')
)
);
$this->addElement(
@ -208,8 +205,8 @@ class BackendConfigForm extends ConfigForm
'name',
array(
'required' => true,
'label' => mt('monitoring', 'Backend Name'),
'description' => mt('monitoring', 'The identifier of this backend')
'label' => $this->translate('Backend Name'),
'description' => $this->translate('The identifier of this backend')
)
);
$this->addElement(
@ -218,8 +215,8 @@ class BackendConfigForm extends ConfigForm
array(
'required' => true,
'autosubmit' => true,
'label' => mt('monitoring', 'Backend Type'),
'description' => mt('monitoring', 'The data source used for retrieving monitoring information'),
'label' => $this->translate('Backend Type'),
'description' => $this->translate('The data source used for retrieving monitoring information'),
'multiOptions' => $resourceTypes,
'value' => $resourceType
)
@ -230,8 +227,8 @@ class BackendConfigForm extends ConfigForm
'resource',
array(
'required' => true,
'label' => mt('monitoring', 'Resource'),
'description' => mt('monitoring', 'The resource to use'),
'label' => $this->translate('Resource'),
'description' => $this->translate('The resource to use'),
'multiOptions' => $this->resources[$resourceType],
'autosubmit' => true
)
@ -253,8 +250,8 @@ class BackendConfigForm extends ConfigForm
array(
'value' => sprintf(
'<a href="%s" data-base-target="_main">%s</a>',
$this->getView()->href('/icingaweb/config/editresource', array('resource' => $resourceName)),
mt('monitoring', 'Show resource configuration')
$this->getView()->url('config/editresource', array('resource' => $resourceName)),
$this->translate('Show resource configuration')
),
'escape' => false
)

View File

@ -9,33 +9,36 @@ use Icinga\Module\Monitoring\Plugin\PerfdataSet;
class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract
{
public function perfdata($perfdataStr, $compact = false)
/**
* Display the given perfdata string to the user
*
* @param $perfdataStr The perfdata string
* @param bool $compact Whether to display the perfdata in compact mode
* @param $color The color indicating the perfdata state
*
* @return string
*/
public function perfdata($perfdataStr, $compact = false, $color = Perfdata::PERFDATA_DEFAULT)
{
$pset = PerfdataSet::fromString($perfdataStr)->asArray();
$onlyPieChartData = array_filter($pset, function ($e) { return $e->getPercentage() > 0; });
if ($compact) {
$onlyPieChartData = array_slice($onlyPieChartData, 0, 5);
} else {
$nonPieChartData = array_filter($pset, function ($e) { return $e->getPercentage() == 0; });
}
$pieChartData = PerfdataSet::fromString($perfdataStr)->asArray();
$result = '';
$table = array();
foreach ($onlyPieChartData as $perfdata) {
$pieChart = $this->createInlinePie($perfdata);
if ($compact) {
$result .= $pieChart->render();
} else {
if (! $perfdata->isPercentage()) {
// TODO: Should we trust sprintf-style placeholders in perfdata titles?
$pieChart->setTooltipFormat('{{label}}: {{formatted}} ({{percent}}%)');
foreach ($pieChartData as $perfdata) {
if ($perfdata->isVisualizable()) {
$pieChart = $perfdata->asInlinePie($color);
if ($compact) {
$result .= $pieChart->render();
} else {
$table[] = '<tr><th>' . $pieChart->render()
. htmlspecialchars($perfdata->getLabel())
. '</th><td> '
. htmlspecialchars($this->formatPerfdataValue($perfdata)) .
' </td></tr>';
}
// $pieChart->setStyle('margin: 0.2em 0.5em 0.2em 0.5em;');
$table[] = '<tr><th>' . $pieChart->render()
. htmlspecialchars($perfdata->getLabel())
. '</th><td> '
. htmlspecialchars($this->formatPerfdataValue($perfdata)) .
' </td></tr>';
} else {
$table[] = (string)$perfdata;
}
}
@ -43,32 +46,10 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract
return $result;
} else {
$pieCharts = empty($table) ? '' : '<table class="perfdata">' . implode("\n", $table) . '</table>';
return $pieCharts . "\n" . implode("<br>\n", $nonPieChartData);
return $pieCharts;
}
}
protected function calculatePieChartData(Perfdata $perfdata)
{
$rawValue = $perfdata->getValue();
$minValue = $perfdata->getMinimumValue() !== null ? $perfdata->getMinimumValue() : 0;
$maxValue = $perfdata->getMaximumValue();
$usedValue = ($rawValue - $minValue);
$unusedValue = ($maxValue - $minValue) - $usedValue;
$gray = $unusedValue;
$green = $orange = $red = 0;
// TODO(#6122): Add proper treshold parsing.
if ($perfdata->getCriticalThreshold() && $perfdata->getValue() > $perfdata->getCriticalThreshold()) {
$red = $usedValue;
} elseif ($perfdata->getWarningThreshold() && $perfdata->getValue() > $perfdata->getWarningThreshold()) {
$orange = $usedValue;
} else {
$green = $usedValue;
}
return array($green, $orange, $red, $gray);
}
protected function formatPerfdataValue(Perfdata $perfdata)
{
if ($perfdata->isBytes()) {
@ -82,24 +63,4 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract
return $perfdata->getValue();
}
protected function createInlinePie(Perfdata $perfdata)
{
$pieChart = new InlinePie($this->calculatePieChartData($perfdata), $perfdata->getLabel());
$pieChart->setLabel(htmlspecialchars($perfdata->getLabel()));
$pieChart->setHideEmptyLabel();
//$pieChart->setHeight(32)->setWidth(32);
if ($perfdata->isBytes()) {
$pieChart->setTooltipFormat('{{label}}: {{formatted}} ({{percent}}%)');
$pieChart->setNumberFormat(InlinePie::NUMBER_FORMAT_BYTES);
} else if ($perfdata->isSeconds()) {
$pieChart->setTooltipFormat('{{label}}: {{formatted}} ({{percent}}%)');
$pieChart->setNumberFormat(InlinePie::NUMBER_FORMAT_TIME);
} else {
$pieChart->setTooltipFormat('{{label}}: {{formatted}}%');
$pieChart->setNumberFormat(InlinePie::NUMBER_FORMAT_RATIO);
$pieChart->setHideEmptyLabel();
}
return $pieChart;
}
}

View File

@ -53,7 +53,7 @@ use Icinga\Module\Monitoring\Object\Service;
</td>
<td>
<?php if (isset($downtime->service)): ?>
<a href="<?= $this->href('monitoring/service/show', array(
<?= $this->icon('service'); ?> <a href="<?= $this->href('monitoring/service/show', array(
'host' => $downtime->host,
'service' => $downtime->service
)); ?>">
@ -63,7 +63,7 @@ use Icinga\Module\Monitoring\Object\Service;
<?= $this->translate('on'); ?> <?= $downtime->host; ?>
</small>
<?php else: ?>
<a href="<?= $this->href('monitoring/host/show', array(
<?= $this->icon('host'); ?> <a href="<?= $this->href('monitoring/host/show', array(
'host' => $downtime->host
)); ?>">
<?= $downtime->host; ?>
@ -76,7 +76,9 @@ use Icinga\Module\Monitoring\Object\Service;
<?php if ($downtime->is_flexible): ?>
<?php if ($downtime->is_in_effect): ?>
<?= sprintf(
$this->translate('This flexible downtime was started on %s at %s and lasts for %s until %s at %s.'),
isset($downtime->service)
? $this->translate('This flexible service downtime was started on %s at %s and lasts for %s until %s at %s.')
: $this->translate('This flexible host downtime was started on %s at %s and lasts for %s until %s at %s.'),
date('d.m.y', $downtime->start),
date('H:i', $downtime->start),
$this->format()->duration($downtime->duration),
@ -85,7 +87,9 @@ use Icinga\Module\Monitoring\Object\Service;
); ?>
<?php else: ?>
<?= sprintf(
$this->translate('This flexible downtime has been scheduled to start between %s - %s and to last for %s.'),
isset($downtime->service)
? $this->translate('This flexible service downtime has been scheduled to start between %s - %s and to last for %s.')
: $this->translate('This flexible host downtime has been scheduled to start between %s - %s and to last for %s.'),
date('d.m.y H:i', $downtime->scheduled_start),
date('d.m.y H:i', $downtime->scheduled_end),
$this->format()->duration($downtime->duration)
@ -94,7 +98,9 @@ use Icinga\Module\Monitoring\Object\Service;
<?php else: ?>
<?php if ($downtime->is_in_effect): ?>
<?= sprintf(
$this->translate('This fixed downtime was started on %s at %s and expires on %s at %s.'),
isset($downtime->service)
? $this->translate('This fixed service downtime was started on %s at %s and expires on %s at %s.')
: $this->translate('This fixed host downtime was started on %s at %s and expires on %s at %s.'),
date('d.m.y', $downtime->start),
date('H:i', $downtime->start),
date('d.m.y', $downtime->end),
@ -102,7 +108,9 @@ use Icinga\Module\Monitoring\Object\Service;
); ?>
<?php else: ?>
<?= sprintf(
$this->translate('This fixed downtime has been scheduled to start on %s at %s and to end on %s at %s.'),
isset($downtime->service)
? $this->translate('This fixed service downtime has been scheduled to start on %s at %s and to end on %s at %s.')
: $this->translate('This fixed host downtime has been scheduled to start on %s at %s and to end on %s at %s.'),
date('d.m.y', $downtime->scheduled_start),
date('H:i', $downtime->scheduled_start),
date('d.m.y', $downtime->scheduled_end),

View File

@ -79,7 +79,7 @@
</td>
<?php endif ?>
<td class="groupname">
<a href="<?= $this->href('monitoring/list/services', array('hostgroup' => $h->hostgroup)); ?>">
<a href="<?= $this->href('monitoring/list/hosts', array('hostgroup' => $h->hostgroup)); ?>">
<?= $h->hostgroup; ?>
</a>
</td>

View File

@ -108,10 +108,9 @@ if ($hosts->count() === 0) {
$host->host_unhandled_services),
'monitoring/show/services',
array(
'host' => $host->host_name,
'service_problem' => 1,
'service_acknowledged' => 0,
'service_in_downtime' => 0
'host' => $host->host_name,
'service_problem' => 1,
'service_handled' => 0
),
array('style' => 'font-weight: normal')
) ?>)</span>

View File

@ -1,6 +1,7 @@
<?php
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Module\Monitoring\Plugin\Perfdata;
$helper = $this->getHelper('MonitoringState');

View File

@ -4,7 +4,7 @@ if (empty($object->hostgroups)) return;
$list = array();
foreach ($object->hostgroups as $name => $alias) {
$list[] = $this->qlink($alias, 'monitoring/list/services', array(
$list[] = $this->qlink($alias, 'monitoring/list/hosts', array(
'hostgroup' => $name
));
}

View File

@ -2,7 +2,7 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/** @var $this \Icinga\Application\Modules\Module */
/** @type $this \Icinga\Application\Modules\Module */
$this->providePermission(
'monitoring/command/*',

View File

@ -518,12 +518,14 @@ class StatusQuery extends IdoQuery
protected function joinServiceproblemsummary()
{
$sub = new Zend_Db_Expr('(SELECT'
. ' SUM(CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth) > 0 THEN 0 ELSE 1 END) AS unhandled_services_count,'
. ' SUM(CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth) > 0 THEN 1 ELSE 0 END) AS handled_services_count,'
. ' SUM(CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 0 ELSE 1 END) AS unhandled_services_count,'
. ' SUM(CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 1 ELSE 0 END) AS handled_services_count,'
. ' s.host_object_id FROM icinga_servicestatus ss'
. ' JOIN icinga_services s'
. ' ON s.service_object_id = ss.service_object_id'
. ' AND ss.current_state > 0'
. ' JOIN icinga_hoststatus hs'
. ' ON hs.host_object_id = s.host_object_id'
. ' GROUP BY s.host_object_id)');
$this->select->joinLeft(
array('sps' => $sub),

View File

@ -30,7 +30,7 @@ class IcingaCommandFileCommandRenderer implements IcingaCommandRendererInterface
*
* @return string
*/
public function escape($commandString)
protected function escape($commandString)
{
return str_replace(array("\r", "\n"), array('\r', '\n'), $commandString);
}
@ -52,7 +52,7 @@ class IcingaCommandFileCommandRenderer implements IcingaCommandRendererInterface
if ($now === null) {
$now = time();
}
return sprintf('[%u] %s', $now, $this->$renderMethod($command));
return sprintf('[%u] %s', $now, $this->escape($this->$renderMethod($command)));
}
public function renderAddComment(AddCommentCommand $command)

View File

@ -421,7 +421,7 @@ abstract class MonitoredObject
if ($this->type === self::TYPE_SERVICE) {
$eventHistory->where('service_description', $this->service_description);
}
$this->eventhistory = $eventHistory->getQuery();
$this->eventhistory = $eventHistory;
return $this;
}

View File

@ -5,9 +5,15 @@
namespace Icinga\Module\Monitoring\Plugin;
use InvalidArgumentException;
use Icinga\Exception\ProgrammingError;
use Icinga\Web\Widget\Chart\InlinePie;
use Zend_Controller_Front;
class Perfdata
{
const PERFDATA_DEFAULT = 'green';
const PERFDATA_RED = 'red';
/**
* The performance data value being parsed
*
@ -159,6 +165,16 @@ class Perfdata
return $this->unit === 'c';
}
/**
* Returns whether it is possible to display a visual representation
*
* @return bool True when the perfdata is visualizable
*/
public function isVisualizable()
{
return isset($this->minValue) && isset($this->maxValue) && isset($this->value);
}
/**
* Return this perfomance data's label
*/
@ -316,4 +332,50 @@ class Perfdata
}
}
}
protected function calculatePieChartData( $color)
{
$rawValue = $this->getValue();
$minValue = $this->getMinimumValue() !== null ? $this->getMinimumValue() : 0;
$maxValue = $this->getMaximumValue();
$usedValue = ($rawValue - $minValue);
$unusedValue = ($maxValue - $minValue) - $usedValue;
$gray = $unusedValue;
$green = $orange = $red = 0;
switch ($color) {
case self::PERFDATA_DEFAULT:
$green = $usedValue;
break;
case self::PERFDATA_RED:
$red = $usedValue;
break;
case self::PERFDATA_ORANGE:
$orange = $usedValue;
break;
}
// TODO(#6122): Add proper treshold parsing.
return array($green, $orange, $red, $gray);
}
public function asInlinePie($color)
{
if (! $this->isVisualizable()) {
throw new ProgrammingError('Cannot calculate piechart data for unvisualizable perfdata entry.');
}
$data = $this->calculatePieChartData($color);
$pieChart = new InlinePie($data, $this->getLabel() . ' ' . number_format($this->getPercentage(), 2) . '%');
$pieChart->setSparklineClass('sparkline-perfdata');
if (Zend_Controller_Front::getInstance()->getRequest()->isXmlHttpRequest()) {
$pieChart->disableNoScript();
}
return $pieChart;
}
}

View File

@ -223,8 +223,6 @@ abstract class MonitoredObjectController extends Controller
)
);
}
$tabs
->extend(new OutputFormat())
->extend(new DashboardAction());
$tabs->extend(new DashboardAction());
}
}

View File

@ -0,0 +1,63 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Tests\Icinga\Regression;
use Icinga\Test\BaseTestCase;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Renderer\IcingaCommandFileCommandRenderer;
/**
* A command that has a hardcoded parameter with newlines
*/
class Bug6088Command extends IcingaCommand
{
public function getParameterWithCarriageReturnAndLineFeed()
{
return "foo\r\nbar";
}
public function getBug()
{
return '6088';
}
}
/**
* A subclass of IcingaCommandFileCommandRenderer to utiliseIcingaCommandFileCommandRenderer
* to render an instance of Bug6088Command
*/
class Bug6088CommandFileCommandRenderer extends IcingaCommandFileCommandRenderer
{
public function renderBug6088(Bug6088Command $command)
{
return 'SOLVE_BUG;' . $command->getBug() . ';' . $command->getParameterWithCarriageReturnAndLineFeed();
}
}
/**
* Class Bug6088
*
* Multi-line comments don't work
*
* @see https://dev.icinga.org/issues/6088
*/
class Bug6088Test extends BaseTestCase
{
public function testWhetherCommandParametersWithMultipleLinesAreProperlyEscaped()
{
$command = new Bug6088Command();
$renderer = new Bug6088CommandFileCommandRenderer();
$commandString = $renderer->render($command);
$this->assertEquals(
'SOLVE_BUG;6088;foo\r\nbar',
substr($commandString, strpos($commandString, ' ') + 1),
'Command parameters with multiple lines are not properly escaped'
);
}
}

View File

@ -112,7 +112,7 @@ class AuthBackendPage extends Form
}
$this->addElements($backendForm->getElements());
$this->getElement('name')->setValue('icingaweb');
$this->getElement('name')->setValue('icingaweb2');
}
/**

View File

@ -51,9 +51,7 @@ $cliPath = realpath(Icinga::app()->getApplicationDir() . '/../bin/icingacli');
</div>
<p><?= mt('setup', 'In case the IcingaCLI is missing you can create the token manually:'); ?></p>
<div class="code">
<span>su <?= ($user = Platform::getPhpUser()) !== null ? $user : 'your_webserver_group'; ?> && mkdir -m 2770 <?= dirname($setupTokenPath); ?>;</span>
<span>head -c 12 /dev/urandom | base64 | tee <?= $setupTokenPath; ?>;</span>
<span>chmod 0660 <?= $setupTokenPath; ?>;</span>
<span>su <?= ($user = Platform::getPhpUser()) !== null ? $user : 'your_webserver_user'; ?> -c "mkdir -m 2770 <?= dirname($setupTokenPath); ?>; head -c 12 /dev/urandom | base64 | tee <?= $setupTokenPath; ?>; chmod 0660 <?= $setupTokenPath; ?>;";</span>
</div>
<p><?= sprintf(
mt('setup', 'Please see the %s for an extensive description on how to access and use this wizard.'),

View File

@ -38,10 +38,6 @@ class TokenValidator extends Zend_Validate_Abstract
mt('setup', 'Cannot validate token, file "%s" is empty. Please define a token.'),
$tokenPath
),
'TOKEN_FILE_PUBLIC' => sprintf(
mt('setup', 'Cannot validate token, file "%s" must only be accessible by the webserver\'s user.'),
$tokenPath
),
'TOKEN_INVALID' => mt('setup', 'Invalid token supplied.')
);
}
@ -56,12 +52,6 @@ class TokenValidator extends Zend_Validate_Abstract
*/
public function isValid($value, $context = null)
{
$tokenStats = @stat($this->tokenPath);
if (($tokenStats['mode'] & 4) === 4) {
$this->_error('TOKEN_FILE_PUBLIC');
return false;
}
try {
$file = new File($this->tokenPath);
$expectedToken = trim($file->fgets());

View File

@ -9,6 +9,7 @@ use Icinga\Web\Form;
use Icinga\Web\Wizard;
use Icinga\Web\Request;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Platform;
use Icinga\Module\Setup\Forms\ModulePage;
use Icinga\Module\Setup\Forms\WelcomePage;
@ -343,7 +344,7 @@ class WebWizard extends Wizard implements SetupWizard
);
}
$configDir = $this->getConfigDir();
$configDir = Icinga::app()->getConfigDir();
$setup->addStep(
new MakeDirStep(
array(
@ -351,7 +352,7 @@ class WebWizard extends Wizard implements SetupWizard
$configDir . '/preferences',
$configDir . '/enabledModules'
),
0775
2770
)
);
@ -461,8 +462,8 @@ class WebWizard extends Wizard implements SetupWizard
mt('setup', 'PHP Module: GD'),
mt(
'setup',
'In case you want icons being exported to PDF as'
. ' well, you\'ll need the GD extension for PHP.'
'In case you want views being exported to PDF,'
. ' you\'ll need the GD extension for PHP.'
),
Platform::extensionLoaded('gd'),
Platform::extensionLoaded('gd') ? mt('setup', 'The PHP module GD is available') : (
@ -528,12 +529,12 @@ class WebWizard extends Wizard implements SetupWizard
)
);
$configDir = $this->getConfigDir();
$configDir = Icinga::app()->getConfigDir();
$requirements->addMandatory(
mt('setup', 'Writable Config Directory'),
mt(
'setup',
'The Icinga Web 2 configuration directory defaults to "/etc/icingaweb", if' .
'The Icinga Web 2 configuration directory defaults to "/etc/icingaweb2", if' .
' not explicitly set in the environment variable "ICINGAWEB_CONFIGDIR".'
),
is_writable($configDir),
@ -551,21 +552,4 @@ class WebWizard extends Wizard implements SetupWizard
return $requirements;
}
/**
* Return the configuration directory of Icinga Web 2
*
* @return string
*/
protected function getConfigDir()
{
if (array_key_exists('ICINGAWEB_CONFIGDIR', $_SERVER)) {
$configDir = $_SERVER['ICINGAWEB_CONFIGDIR'];
} else {
$configDir = '/etc/icingaweb';
}
$canonical = realpath($configDir);
return $canonical ? $canonical : $configDir;
}
}

View File

@ -101,7 +101,7 @@ class GettextTranslationHelper
*/
public function __construct(ApplicationBootstrap $bootstrap, $locale)
{
$this->moduleMgr = $bootstrap->getModuleManager()->loadCoreModules()->loadEnabledModules();
$this->moduleMgr = $bootstrap->getModuleManager()->loadEnabledModules();
$this->appDir = $bootstrap->getApplicationDir();
$this->locale = $locale;
}

View File

@ -1,4 +1,4 @@
Alias /icingaweb "/usr/share/icingaweb2/public"
Alias /icingaweb2 "/usr/share/icingaweb2/public"
<Directory "/usr/share/icingaweb2/public">
Options SymLinksIfOwnerMatch
@ -23,7 +23,7 @@ Alias /icingaweb "/usr/share/icingaweb2/public"
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /icingaweb/
RewriteBase /icingaweb2/
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d

View File

@ -3,4 +3,4 @@
require_once '/usr/share/php/Icinga/Application/Cli.php';
Icinga\Application\Cli::start('/usr/share/icingaweb')->dispatch();
Icinga\Application\Cli::start('/usr/share/icingaweb2')->dispatch();

View File

@ -0,0 +1,3 @@
[documentation]
icingaweb2 = /usr/share/doc/icingaweb2/markdown
modules = /usr/share/doc/icingaweb2/modules/{module}/markdown

View File

@ -195,7 +195,7 @@ tr.state.handled td.state {
}
/* HOVER colors */
tr.state:hover, tr[href]:hover {
tr[href]:hover {
color: black;
background-color: #eee;
}

View File

@ -285,3 +285,10 @@ li li .badge {
.widgetFilter li.active {
background-color: #eee;
}
.sparkline {
width: 12px;
height: 12px;
position: relative;
top: 4px;
}

View File

@ -32,6 +32,15 @@
if ($outerMenu.size()) {
$outerMenu.addClass('active');
}
/*
Recreate the html content of the menu item to force the browser to update the layout, or else
the link would only be visible as active after another click or page reload in Gecko and WebKit.
fixes #7897
*/
$selectedMenu.html($selectedMenu.html());
} else {
// store menu state
var $menus = $('#menu li.active', el);

View File

@ -18,34 +18,33 @@
$('span.sparkline', el).each(function(i, element) {
// read custom options
var $spark = $(element);
var labels = $spark.attr('labels').split('|');
var formatted = $spark.attr('formatted').split('|');
var tooltipChartTitle = $spark.attr('sparkTooltipChartTitle') || '';
var format = $spark.attr('tooltipformat');
var hideEmpty = $spark.attr('hideEmptyLabel') === 'true';
$spark.sparkline(
'html',
{
var $spark = $(element);
var title = $spark.attr('title');
if ($spark.attr('labels')) {
$spark.removeAttr('original-title');
}
var options;
if ($spark.hasClass('sparkline-perfdata')) {
options = {
enableTagOptions: true,
tooltipFormatter: function (sparkline, options, fields) {
var out = format;
if (hideEmpty && fields.offset === 3) {
return '';
}
var replace = {
title: tooltipChartTitle,
label: labels[fields.offset] ? labels[fields.offset] : fields.offset,
formatted: formatted[fields.offset] ? formatted[fields.offset] : '',
value: fields.value,
percent: Math.round(fields.percent * 100) / 100
};
$.each(replace, function(key, value) {
out = out.replace('{{' + key + '}}', value);
});
return out;
}
});
width: 12,
height: 12,
title: title,
disableTooltips: true
};
$spark.sparkline('html', options);
} else if ($spark.hasClass('sparkline-multi')) {
options = {
width: 100,
height: 100,
title: title,
enableTagOptions: true
};
$spark.sparkline('html', options);
}
});
};