= $this->qlink(
diff --git a/application/views/scripts/group/list.phtml b/application/views/scripts/group/list.phtml
index cf0645633..84d75018c 100644
--- a/application/views/scripts/group/list.phtml
+++ b/application/views/scripts/group/list.phtml
@@ -5,16 +5,14 @@ use Icinga\Data\Reducible;
if (! $this->compact): ?>
- = $tabs; ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
-
-
- = $this->backendSelection; ?>
- = $this->filterEditor; ?>
+ = $this->sortBox ?>
+ = $this->backendSelection ?>
+ = $this->filterEditor ?>
@@ -45,8 +43,7 @@ if (! isset($backend)) {
hasResult()): ?>
= $this->translate('No user groups found matching the filter'); ?>
-
-
+
@@ -85,8 +82,9 @@ if (! isset($backend)) {
'group' => $group->group_name
),
array(
+ 'class' => 'action-link',
'title' => sprintf($this->translate('Remove user group %s'), $group->group_name),
- 'icon' => 'trash'
+ 'icon' => 'cancel'
)
); ?>
diff --git a/application/views/scripts/user/list.phtml b/application/views/scripts/user/list.phtml
index 182e26e87..7e045d8a9 100644
--- a/application/views/scripts/user/list.phtml
+++ b/application/views/scripts/user/list.phtml
@@ -5,16 +5,14 @@ use Icinga\Data\Reducible;
if (! $this->compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
-
-
- = $this->backendSelection ?>
- = $this->filterEditor ?>
+ = $this->sortBox ?>
+ = $this->backendSelection ?>
+ = $this->filterEditor ?>
diff --git a/doc/about.md b/doc/01-About.md
similarity index 100%
rename from doc/about.md
rename to doc/01-About.md
diff --git a/doc/installation.md b/doc/02-Installation.md
similarity index 94%
rename from doc/installation.md
rename to doc/02-Installation.md
index 04ec7b842..9afe087e8 100644
--- a/doc/installation.md
+++ b/doc/02-Installation.md
@@ -4,7 +4,7 @@ The preferred way of installing Icinga Web 2 is to use the official package repo
system and distribution you are running. But it is also possible to install Icinga Web 2 directly from source.
In case you are upgrading from an older version of Icinga Web 2
-please make sure to read the [upgrading](installation.md#upgrading) section
+please make sure to read the [upgrading](02-Installation.md#upgrading) section
thoroughly.
## Installing Requirements
@@ -179,7 +179,7 @@ git clone git://git.icinga.org/icingaweb2.git
### Installing Requirements from Source
-You will need to install certain dependencies depending on your setup listed [here](installation.md#installing-requirements).
+You will need to install certain dependencies depending on your setup listed [here](02-Installation.md#installing-requirements).
The following example installs Apache2 as web server, MySQL as RDBMS and uses the PHP adapter for MySQL.
Adopt the package requirements to your needs (e.g. adding ldap for authentication) and distribution.
@@ -318,7 +318,7 @@ Puppet, Ansible, Chef, etc. modules.
> Read the documentation on the respective linked configuration sections before
> deploying the configuration manually.
>
-> If you are unsure about certain settings, use the [setup wizard](installation.md#web-setup-wizard-from-source) once
+> If you are unsure about certain settings, use the [setup wizard](02-Installation.md#web-setup-wizard-from-source) once
> and then collect the generated configuration as well as sql dumps.
#### Icinga Web 2 Manual Database Setup
@@ -336,7 +336,7 @@ mysql -p icingaweb2 < /usr/share/icingaweb2/etc/schema/mysql.schema.sql
```
-Then generate a new password hash as described in the [authentication docs](authentication.md#authentication-configuration-db-setup)
+Then generate a new password hash as described in the [authentication docs](05-Authentication.md#authentication-configuration-db-setup)
and use it to insert a new user called `icingaadmin` into the database.
```
@@ -349,7 +349,7 @@ quit
#### Icinga Web 2 Manual Configuration
-[resources.ini](resources.md#resources) providing the details for the Icinga Web 2 and
+[resources.ini](04-Resources.md#resources) providing the details for the Icinga Web 2 and
Icinga 2 IDO database configuration. Example for MySQL:
```
@@ -375,7 +375,7 @@ username = "icinga"
password = "icinga"
```
-[config.ini](configuration.md#configuration) defining general application settings.
+[config.ini](03-Configuration.md#configuration) defining general application settings.
```
vim /etc/icingaweb2/config.ini
@@ -391,7 +391,7 @@ type = "db"
resource = "icingaweb2"
```
-[authentication.ini](authentication.md#authentication) for e.g. using the previously created database.
+[authentication.ini](05-Authentication.md#authentication) for e.g. using the previously created database.
```
vim /etc/icingaweb2/authentication.ini
@@ -402,7 +402,7 @@ resource = "icingaweb2"
```
-[roles.ini](security.md#security) granting the previously added `icingaadmin` user all permissions.
+[roles.ini](06-Security.md#security) granting the previously added `icingaadmin` user all permissions.
```
vim /etc/icingaweb2/roles.ini
@@ -415,7 +415,7 @@ permissions = "*"
#### Icinga Web 2 Manual Configuration Monitoring Module
-[config.ini](../modules/monitoring/doc/configuration.md#configuration) defining additional security settings.
+**config.ini** defining additional security settings.
```
vim /etc/icingaweb2/modules/monitoring/config.ini
@@ -424,7 +424,7 @@ vim /etc/icingaweb2/modules/monitoring/config.ini
protected_customvars = "*pw*,*pass*,community"
```
-[backends.ini](../modules/monitoring/doc/configuration.md#configuration) referencing the Icinga 2 DB IDO resource.
+**backends.ini** referencing the Icinga 2 DB IDO resource.
```
vim /etc/icingaweb2/modules/monitoring/backends.ini
@@ -434,7 +434,7 @@ type = "ido"
resource = "icinga2"
```
-[commandtransports.ini](../modules/monitoring/doc/commandtransports.md#commandtransports) defining the Icinga 2 command pipe.
+**commandtransports.ini** defining the Icinga command pipe.
```
vim /etc/icingaweb2/modules/monitoring/commandtransports.ini
@@ -451,46 +451,20 @@ Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/ici
# Upgrading Icinga Web 2
-## Upgrading to Icinga Web 2 Beta 2
+## Upgrading to Icinga Web 2 2.3.0
-Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2,
-you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following
-content:
-```
-[administrators]
-users = "your_user_name, another_user_name"
-permissions = "*"
-```
+* Icinga Web 2 version 2.3.0 does not introduce any backward incompatible change.
-After please log out from Icinga Web 2 and log in again for having all permissions granted.
+## Upgrading to Icinga Web 2 2.2.0
-If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external`
-authentication backend to be able to log in again. The new name better reflects what’s going on. A similar change
-affects environments that opted for not storing preferences, your new backend is `none`.
+* The menu entry `Authorization` beneath `Config` has been renamed to `Authentication`. The role, user backend and user
+ group backend configuration which was previously found beneath `Authentication` has been moved to `Application`.
+
+## Upgrading to Icinga Web 2 2.1.x
-## Upgrading to Icinga Web 2 Beta 3
-
-Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your
-configuration files after upgrading to Icinga Web 2 Beta 3.
-
-## Upgrading to Icinga Web 2 Release Candidate 1
-
-The first release candidate of Icinga Web 2 introduces the following non-backward compatible changes:
-
-* The database schema has been adjusted and the tables `icingaweb_group` and
- `icingaweb_group_membership` were altered to ensure referential integrity.
- Please use the upgrade script located in **etc/schema/** to update your
- database schema
-
-* Users who are using PostgreSQL < v9.1 are required to upgrade their
- environment to v9.1+ as this is the new minimum required version
- for utilizing PostgreSQL as database backend
-
-* The restrictions `monitoring/hosts/filter` and `monitoring/services/filter`
- provided by the monitoring module were merged together. The new
- restriction is called `monitoring/filter/objects` and supports only a
- predefined subset of filter columns. Please see the module's security
- related documentation for more details.
+* Since Icinga Web 2 version 2.1.3 LDAP user group backends respect the configuration option `group_filter`.
+ Users who changed the configuration manually and used the option `filter` instead
+ have to change it back to `group_filter`.
## Upgrading to Icinga Web 2 2.0.0
@@ -514,13 +488,43 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
**<config-dir>/preferences/<username>/config.ini**.
The content of the file remains unchanged.
-## Upgrading to Icinga Web 2 2.1.x
+## Upgrading to Icinga Web 2 Release Candidate 1
-* Since Icinga Web 2 version 2.1.3 LDAP user group backends respect the configuration option `group_filter`.
- Users who changed the configuration manually and used the option `filter` instead
- have to change it back to `group_filter`.
+The first release candidate of Icinga Web 2 introduces the following non-backward compatible changes:
-## Upgrading to Icinga Web 2 2.2.0
+* The database schema has been adjusted and the tables `icingaweb_group` and
+ `icingaweb_group_membership` were altered to ensure referential integrity.
+ Please use the upgrade script located in **etc/schema/** to update your
+ database schema
-* The menu entry `Authorization` beneath `Config` has been renamed to `Authentication`. The role, user backend and user
- group backend configuration which was previously found beneath `Authentication` has been moved to `Application`.
+* Users who are using PostgreSQL < v9.1 are required to upgrade their
+ environment to v9.1+ as this is the new minimum required version
+ for utilizing PostgreSQL as database backend
+
+* The restrictions `monitoring/hosts/filter` and `monitoring/services/filter`
+ provided by the monitoring module were merged together. The new
+ restriction is called `monitoring/filter/objects` and supports only a
+ predefined subset of filter columns. Please see the module's security
+ related documentation for more details.
+
+## Upgrading to Icinga Web 2 Beta 3
+
+Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your
+configuration files after upgrading to Icinga Web 2 Beta 3.
+
+## Upgrading to Icinga Web 2 Beta 2
+
+Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2,
+you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following
+content:
+```
+[administrators]
+users = "your_user_name, another_user_name"
+permissions = "*"
+```
+
+After please log out from Icinga Web 2 and log in again for having all permissions granted.
+
+If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external`
+authentication backend to be able to log in again. The new name better reflects what’s going on. A similar change
+affects environments that opted for not storing preferences, your new backend is `none`.
diff --git a/doc/03-Configuration.md b/doc/03-Configuration.md
new file mode 100644
index 000000000..4df9ad2e6
--- /dev/null
+++ b/doc/03-Configuration.md
@@ -0,0 +1,15 @@
+# Configuration
+
+## Overview
+
+Apart from its web configuration capabilities, the local configuration is
+stored in `/etc/icingaweb2` by default (depending on your config setup).
+
+File/Directory | Description
+---------------------------------------------------------
+config.ini | General configuration (logging, preferences)
+[resources.ini](04-Ressources.md) | Global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database)
+roles.ini | User specific roles (e.g. `administrators`) and permissions
+[authentication.ini](05-Authentication.md) | Authentication backends (e.g. database)
+enabledModules | Contains symlinks to enabled modules
+modules | Directory for module specific configuration
diff --git a/doc/resources.md b/doc/04-Resources.md
similarity index 100%
rename from doc/resources.md
rename to doc/04-Resources.md
diff --git a/doc/authentication.md b/doc/05-Authentication.md
similarity index 59%
rename from doc/authentication.md
rename to doc/05-Authentication.md
index 21f5a8b9f..c30c593ff 100644
--- a/doc/authentication.md
+++ b/doc/05-Authentication.md
@@ -18,29 +18,60 @@ The order of entries in the authentication configuration determines the order of
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
+## External Authentication
For delegating authentication to the web server simply add `autologin` to your authentication configuration:
-````
+```
[autologin]
backend = external
-````
+```
-If your web server is not configured for authentication though the `autologin` section has no effect.
+If your web server is not configured for authentication though, the `autologin` section has no effect.
-### Active Directory or LDAP Authentication
+### Example Configuration for Apache and Basic Authentication
+
+The following example will show you how to enable external authentication in Apache
+using **Basic access authentication**.
+
+**Creating Users**
+
+To create users for **basic access authentication** you can use the tool `htpasswd`. In this example **.http-users** is
+the name of the file containing the user credentials.
+
+The following command creates a new file with the user **icingaadmin**. `htpasswd` will prompt you for a password.
+If you want to add more users to the file you have to omit the `-c` switch to not overwrite the file.
+
+```
+sudo htpasswd -c /etc/icingaweb2/.http-users icingaadmin
+```
+
+**Configuring the Web Server**
+
+Add the following configuration to the **<Directory> Directive** in the **icingaweb.conf** web server
+configuration file.
+
+```
+AuthType Basic
+AuthName "Icinga Web 2"
+AuthUserFile /etc/icingaweb2/.http-users
+Require valid-user
+```
+
+Restart your web server to apply the changes.
+
+## Active Directory or LDAP Authentication
If you want to authenticate against Active Directory or LDAP, you have to define a
-[LDAP resource](resources.md#resources-configuration-ldap) which will be referenced as data source for the Active Directory
-or LDAP configuration method.
+[LDAP resource](04-Resources.md#resources-configuration-ldap) which will be referenced as data source for the
+Active Directory or LDAP configuration method.
-#### LDAP
+### LDAP
Directive | Description
------------------------|------------
**backend** | `ldap`
-**resource** | The name of the LDAP resource defined in [resources.ini](resources.md#resources).
+**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources).
**user_class** | LDAP user class.
**user_name_attribute** | LDAP attribute which contains the username.
**filter** | LDAP search filter.
@@ -60,12 +91,12 @@ Note that in case the set *user_name_attribute* holds multiple values it is requ
values are unique. Additionally, a user will be logged in using the exact user id used to authenticate
with Icinga Web 2 (e.g. an alias) no matter what the primary user id might actually be.
-#### Active Directory
+### Active Directory
Directive | Description
------------------------|------------
**backend** | `msldap`
-**resource** | The name of the LDAP resource defined in [resources.ini](resources.md#resources).
+**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources).
**Example:**
@@ -75,16 +106,16 @@ backend = msldap
resource = my_ad
```
-### Database Authentication
+## Database Authentication
If you want to authenticate against a MySQL or a PostgreSQL database, you have to define a
-[database resource](resources.md#resources-configuration-database) which will be referenced as data source for the database
+[database resource](04-Resources.md#resources-configuration-database) which will be referenced as data source for the database
authentication method.
Directive | Description
------------------------|------------
**backend** | `db`
-**resource** | The name of the database resource defined in [resources.ini](resources.md#resources).
+**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources).
**Example:**
@@ -94,28 +125,28 @@ backend = db
resource = icingaweb-mysql
```
-#### Database Setup
+### 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.md#resources-configuration-database).
+After that you have to define the [database resource](04-Resources.md#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');
-````
+```
diff --git a/doc/security.md b/doc/06-Security.md
similarity index 88%
rename from doc/security.md
rename to doc/06-Security.md
index 3b8731a7d..65c908342 100644
--- a/doc/security.md
+++ b/doc/06-Security.md
@@ -21,9 +21,8 @@ things to which access can be managed: actions and objects.
### Actions
Actions are all the things an Icinga Web 2 user can do, like changing a certain configuration,
-changing permissions or sending a command to the Icinga instance through the
- Command Pipe
-in the monitoring module. All actions must be be **allowed explicitly** using permissions.
+changing permissions or sending a command to the Icinga instance through the Icinga command pipe.
+All actions must be be **allowed explicitly** using permissions.
A permission is a simple list of identifiers of actions a user is
allowed to do. Permissions are described in greater detail in the
@@ -47,7 +46,7 @@ using Active Directory, and a user **icingaadmin** that is authenticated using a
In the configuration, both can be referenced to by using their user names **icingaadmin** or **jdoe**.
Icinga Web 2 users and groups are not configured by a configuration file, but provided by
-an **authentication backend**. For extended information on setting up authentication backends and managing users, please read the chapter [Authentication](authentication.md#authentication).
+an **authentication backend**. For extended information on setting up authentication backends and managing users, please read the chapter [Authentication](05-Authentication.md#authentication).
@@ -59,7 +58,7 @@ an **authentication backend**. For extended information on setting up authentica
#### Managing Users
When using a [Database
-as authentication backend](authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration
+as authentication backend](05-Authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration
can be found at **Configuration > Authentication > Users **.
### Groups
@@ -70,12 +69,12 @@ A user can be member of multiple groups and will inherit all permissions and res
Like users, groups are identified solely by their **name** that is provided by
a **group backend**. For extended information on setting up group backends,
- please read the chapter [Authentication](authentication.md#authentication).
+ please read the chapter [Authentication](05-Authentication.md#authentication).
#### Managing Groups
-When using a [Database as an authentication backend](#authentication.md#authentication-configuration-db-authentication),
+When using a [Database as an authentication backend](05-Authentication.md#authentication-configuration-db-authentication),
it is possible to manage groups and group memberships directly in the frontend. This configuration
can be found at **Configuration > Authentication > Groups **.
@@ -157,18 +156,18 @@ through a group) all permissions are added together to get the users actual perm
### Global Permissions
-Name | Permits
---------------- ----|--------------------------------------------------------
-* | Allow everything, including module-specific permissions
-config/* | Allow all configuration actions
-config/modules | Allow enabling or disabling modules
-module/ | Allow access to module
+Name | Permits
+--------------------------|--------------------------------------------------------
+* | Allow everything, including module-specific permissions
+config/* | Allow all configuration actions
+config/modules | Allow enabling or disabling modules
+module/<moduleName> | Allow access to module <moduleName>
### Monitoring Module Permissions
The built-in monitoring module defines an additional set of permissions, that
-is described in detail in the [monitoring module documentation](/icingaweb2/doc/module/doc/chapter/monitoring-security#monitoring-security).
+is described in detail in the monitoring module documentation.
## Restrictions
@@ -187,7 +186,7 @@ mentioned in the section [Syntax](#syntax).
### Filter Expressions
Filters operate on columns. A complete list of all available filter columns on hosts and services can be found in
-the [monitoring module documentation](/icingaweb2/doc/module/doc/chapter/monitoring-security#monitoring-security-restrictions).
+the monitoring module documentation.
Any filter expression that is allowed in the filtered view, is also an allowed filter expression.
This means, that it is possible to define negations, wildcards, and even nested
diff --git a/doc/preferences.md b/doc/07-Preferences.md
similarity index 89%
rename from doc/preferences.md
rename to doc/07-Preferences.md
index ca26b66b0..e89a507bf 100644
--- a/doc/preferences.md
+++ b/doc/07-Preferences.md
@@ -27,13 +27,13 @@ type = ini
### Store Preferences in a Database
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.md#resources-configuration-database)
+For storing preferences in a database, you have to define a [database resource](04-Resources.md#resources-configuration-database)
which will be referenced as resource for the preferences storage.
Directive | Description
------------------------|------------
**type** | `db`
-**resource** | The name of the database resource defined in [resources.ini](resources.md#resources).
+**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources).
**Example:**
@@ -50,4 +50,4 @@ For storing preferences in a database, you have to import one of the following d
* **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.md#resources-configuration-database).
+After that you have to define the [database resource](04-Resources.md#resources-configuration-database).
diff --git a/doc/vagrant.md b/doc/99-Vagrant.md
similarity index 100%
rename from doc/vagrant.md
rename to doc/99-Vagrant.md
diff --git a/doc/configuration.md b/doc/configuration.md
deleted file mode 100644
index 90946be83..000000000
--- a/doc/configuration.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Configuration
-
-## Overview
-
-Apart from its web configuration capabilities, the local configuration is
-stored in `/etc/icingaweb2` by default (depending on your config setup).
-
- Location | File | Description
- ------------------------------|-----------------------|---------------------------
- . | config.ini | General configuration (logging, preferences)
- . | resources.ini | Global resources (Icinga Web 2 database for preferences and authentication, icinga ido database)
- . | roles.ini | User specific roles (e.g. `administrators`) and permissions
- . | [authentication.ini](authentication.md) | Authentication backends (e.g. database)
- enabledModules | Symlink | Contains symlinks to enabled modules from `/usr/share/icingaweb2/modules/*`. Defaults to [monitoring](modules/monitoring/doc/configuration.md) and `doc`.
- modules | Directory | Module specific configuration
diff --git a/doc/external_authentication.md b/doc/external_authentication.md
deleted file mode 100644
index 05225fb82..000000000
--- a/doc/external_authentication.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# External Authentication
-
-It is possible to utilize the authentication mechanism of the webserver instead
-of the internal authentication of Icinga Web 2 to authenticate users. This might
-be useful if you only have very few users and user management over **.htaccess**
-is not sufficient or if you are required to use some other authentication
-mechanism that is only available by utilizing the webserver.
-
-Icinga Web 2 will entrust the complete authentication process to the
-authentication provider of the webserver, if external authentication is used.
-So it is very important that the webserver's authentication is configured
-correctly as wrong configuration might lead to unauthorized access or a
-malfunction in the login-process.
-
-## Using External Authentication
-
-External authentication in Icinga Web 2 requires the following preparations:
-
-1. The external authentication must be set up properly to correctly
- authenticate users
-2. Icinga Web 2 must be configured to use external authentication
-
-### Preparing the External Authentication Provider
-
-This step depends heavily on the used webserver and authentication mechanism you
-want to use. It is not possible to cover all possibillities and you should
-probably read the documentation for your webserver to get detailed instructions
-on how to set up authentication properly.
-
-In general you need to make sure that:
-
-- All routes require authentication
-- Only permitted users are allowed to authenticate
-
-#### Example Configuration for Apache and HTTPDigestAuthentication
-
-The following example will show how to enable external authentication in Apache
-using *HTTP Digest Authentication*.
-
-##### Creating users
-
-To create users for digest authentication you can use the tool *htdigest*. In
-this example **.icingawebdigest** is the name of the file containing the user
-credentials.
-
-This command creates a new file with the user *jdoe*. *htdigest* will prompt
-you for a password. If you want to add more users to the file you need to omit
-the *-c* parameter in all following commands to not to overwrite the file.
-
-````
-sudo htdigest -c /etc/icingaweb2/.icingawebdigest "Icinga Web 2" jdoe
-````
-
-##### Configuring the Webserver
-
-The webserver should require authentication for all public Icinga Web 2 files.
-
-````
-
- AuthType digest
- AuthName "Icinga Web 2"
- AuthDigestProvider file
- AuthUserFile /etc/icingaweb2/.icingawebdigest
- Require valid-user
-
-````
-
-To get these changes to work, make sure to enable the module for
-HTTPDigestAuthentication and restart the webserver.
-
-### Preparing Icinga Web 2
-
-Once external authentication is set up correctly you need to configure Icinga
-Web 2. In case you already completed the setup wizard it is likely that you are
-now finished.
-
-To get Icinga Web 2 to use external authentication the file
-**config/authentication.ini** is required. Just add the following section
-called "autologin", or any name of your choice, and save your changes:
-
-````
-[autologin]
-backend = external
-````
-
-Congratulations! You are now logged in when visiting Icinga Web 2.
\ No newline at end of file
diff --git a/icingaweb2.spec b/icingaweb2.spec
index f284df23b..91862e0ee 100644
--- a/icingaweb2.spec
+++ b/icingaweb2.spec
@@ -190,7 +190,7 @@ cp -pv etc/bash_completion.d/icingacli %{buildroot}/%{_sysconfdir}/bash_completi
cp -prv modules/{monitoring,setup,doc,translation} %{buildroot}/%{basedir}/modules
cp -prv library/Icinga %{buildroot}/%{phpdir}
cp -prv library/vendor/{dompdf,HTMLPurifier*,JShrink,lessphp,Parsedown,Zend} %{buildroot}/%{basedir}/library/vendor
-cp -prv public/{css,img,js,error_norewrite.html} %{buildroot}/%{basedir}/public
+cp -prv public/{css,font,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
diff --git a/library/Icinga/Application/Hook/Ticket/TicketPattern.php b/library/Icinga/Application/Hook/Ticket/TicketPattern.php
new file mode 100644
index 000000000..8116ca6be
--- /dev/null
+++ b/library/Icinga/Application/Hook/Ticket/TicketPattern.php
@@ -0,0 +1,152 @@
+match[$offset]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetGet($offset)
+ {
+ return array_key_exists($offset, $this->match) ? $this->match[$offset] : null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetSet($offset, $value)
+ {
+ if ($offset === null) {
+ $this->match[] = $value;
+ } else {
+ $this->match[$offset] = $value;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetUnset($offset)
+ {
+ unset($this->match[$offset]);
+ }
+
+
+ /**
+ * Get the result of a performed ticket match
+ *
+ * @return array
+ */
+ public function getMatch()
+ {
+ return $this->match;
+ }
+
+ /**
+ * Set the result of a performed ticket match
+ *
+ * @param array $match
+ *
+ * @return $this
+ */
+ public function setMatch(array $match)
+ {
+ $this->match = $match;
+ return $this;
+ }
+
+ /**
+ * Get the name of the TTS integration
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the name of the TTS integration
+ *
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ /**
+ * Get the ticket pattern
+ *
+ * @return string
+ */
+ public function getPattern()
+ {
+ return $this->pattern;
+ }
+
+ /**
+ * Set the ticket pattern
+ *
+ * @param string $pattern
+ *
+ * @return $this
+ */
+ public function setPattern($pattern)
+ {
+ $this->pattern = $pattern;
+ return $this;
+ }
+
+ /**
+ * Whether the integration is properly configured, i.e. the pattern and the URL are not empty
+ *
+ * @return bool
+ */
+ public function isValid()
+ {
+ return ! empty($this->pattern);
+ }
+}
diff --git a/library/Icinga/Application/Hook/TicketHook.php b/library/Icinga/Application/Hook/TicketHook.php
index 72bb308ee..0cf106175 100644
--- a/library/Icinga/Application/Hook/TicketHook.php
+++ b/library/Icinga/Application/Hook/TicketHook.php
@@ -3,15 +3,17 @@
namespace Icinga\Application\Hook;
+use ArrayIterator;
use ErrorException;
use Exception;
+use Icinga\Application\Hook\Ticket\TicketPattern;
use Icinga\Application\Logger;
use Icinga\Exception\IcingaException;
/**
* Base class for ticket hooks
*
- * Extend this class if you want to integrate your ticketing solution Icinga Web 2
+ * Extend this class if you want to integrate your ticketing solution into Icinga Web 2.
*/
abstract class TicketHook
{
@@ -39,6 +41,100 @@ abstract class TicketHook
{
}
+ /**
+ * Create a link for each matched element in the subject text
+ *
+ * @param array|TicketPattern $match Matched element according to {@link getPattern()}
+ *
+ * @return string Replacement string
+ */
+ abstract public function createLink($match);
+
+ /**
+ * Get the pattern(s) to search for
+ *
+ * Return an array of TicketPattern instances here to support multiple TTS integrations.
+ *
+ * @return string|TicketPattern[]
+ */
+ abstract public function getPattern();
+
+ /**
+ * Apply ticket patterns to the given text
+ *
+ * @param string $text
+ * @param TicketPattern[] $ticketPatterns
+ *
+ * @return string
+ */
+ private function applyTicketPatterns($text, array $ticketPatterns)
+ {
+ $out = '';
+ $start = 0;
+
+ $iterator = new ArrayIterator($ticketPatterns);
+ $iterator->rewind();
+
+ while ($iterator->valid()) {
+ $ticketPattern = $iterator->current();
+
+ try {
+ preg_match($ticketPattern->getPattern(), $text, $match, PREG_OFFSET_CAPTURE, $start);
+ } catch (ErrorException $e) {
+ $this->fail('Can\'t create ticket links: Pattern is invalid: %s', IcingaException::describe($e));
+ $iterator->next();
+ continue;
+ }
+
+ if (empty($match)) {
+ $iterator->next();
+ continue;
+ }
+
+ // Remove preg_offset from match for the ticket pattern
+ $carry = array();
+ array_walk($match, function ($value, $key) use (&$carry) {
+ $carry[$key] = $value[0];
+ }, $carry);
+ $ticketPattern->setMatch($carry);
+
+ $offsetLeft = $match[0][1];
+ $matchLength = strlen($match[0][0]);
+
+ $out .= substr($text, $start, $offsetLeft - $start);
+
+ try {
+ $out .= $this->createLink($ticketPattern);
+ } catch (Exception $e) {
+ $this->fail('Can\'t create ticket links: %s', IcingaException::describe($e));
+ return $text;
+ }
+
+ $start = $offsetLeft + $matchLength;
+ }
+
+ $out .= substr($text, $start);
+
+ return $out;
+ }
+
+ /**
+ * Helper function to create a TicketPattern instance
+ *
+ * @param string $name Name of the TTS integration
+ * @param string $pattern Ticket pattern
+ *
+ * @return TicketPattern
+ */
+ protected function createTicketPattern($name, $pattern)
+ {
+ $ticketPattern = new TicketPattern();
+ $ticketPattern
+ ->setName($name)
+ ->setPattern($pattern);
+ return $ticketPattern;
+ }
+
/**
* Set the hook as failed w/ the given message
*
@@ -63,22 +159,6 @@ abstract class TicketHook
return $this->lastError;
}
- /**
- * Get the pattern
- *
- * @return string
- */
- abstract public function getPattern();
-
- /**
- * Create a link for each matched element in the subject text
- *
- * @param array $match Array of matched elements according to {@link getPattern()}
- *
- * @return string Replacement string
- */
- abstract public function createLink($match);
-
/**
* Create links w/ {@link createLink()} in the given text that matches to the subject from {@link getPattern()}
*
@@ -101,22 +181,28 @@ abstract class TicketHook
$this->fail('Can\'t create ticket links: Retrieving the pattern failed: %s', IcingaException::describe($e));
return $text;
}
+
if (empty($pattern)) {
$this->fail('Can\'t create ticket links: Pattern is empty');
return $text;
}
- try {
- $text = preg_replace_callback(
- $pattern,
- array($this, 'createLink'),
- $text
- );
- } catch (ErrorException $e) {
- $this->fail('Can\'t create ticket links: Pattern is invalid: %s', IcingaException::describe($e));
- return $text;
- } catch (Exception $e) {
- $this->fail('Can\'t create ticket links: %s', IcingaException::describe($e));
- return $text;
+
+ if (is_array($pattern)) {
+ $text = $this->applyTicketPatterns($text, $pattern);
+ } else {
+ try {
+ $text = preg_replace_callback(
+ $pattern,
+ array($this, 'createLink'),
+ $text
+ );
+ } catch (ErrorException $e) {
+ $this->fail('Can\'t create ticket links: Pattern is invalid: %s', IcingaException::describe($e));
+ return $text;
+ } catch (Exception $e) {
+ $this->fail('Can\'t create ticket links: %s', IcingaException::describe($e));
+ return $text;
+ }
}
return $text;
diff --git a/library/Icinga/Authentication/AdmissionLoader.php b/library/Icinga/Authentication/AdmissionLoader.php
index 391622fb1..0a80be127 100644
--- a/library/Icinga/Authentication/AdmissionLoader.php
+++ b/library/Icinga/Authentication/AdmissionLoader.php
@@ -5,6 +5,7 @@ namespace Icinga\Authentication;
use Icinga\Application\Config;
use Icinga\Application\Logger;
+use Icinga\Authentication\Role;
use Icinga\Exception\NotReadableError;
use Icinga\Data\ConfigObject;
use Icinga\User;
@@ -43,16 +44,12 @@ class AdmissionLoader
}
/**
- * Get user permissions and restrictions
+ * Apply permissions, restrictions and roles to the given user
*
- * @param User $user
- *
- * @return array
+ * @param User $user
*/
- public function getPermissionsAndRestrictions(User $user)
+ public function applyRoles(User $user)
{
- $permissions = array();
- $restrictions = array();
$username = $user->getUsername();
try {
$roles = Config::app('roles');
@@ -62,14 +59,18 @@ class AdmissionLoader
$username,
$e
);
- return array($permissions, $restrictions);
+ return;
}
$userGroups = $user->getGroups();
- foreach ($roles as $role) {
+ $permissions = array();
+ $restrictions = array();
+ $roleObjs = array();
+ foreach ($roles as $roleName => $role) {
if ($this->match($username, $userGroups, $role)) {
+ $permissionsFromRole = StringHelper::trimSplit($role->permissions);
$permissions = array_merge(
$permissions,
- array_diff(StringHelper::trimSplit($role->permissions), $permissions)
+ array_diff($permissionsFromRole, $permissions)
);
$restrictionsFromRole = $role->toArray();
unset($restrictionsFromRole['users']);
@@ -81,8 +82,16 @@ class AdmissionLoader
}
$restrictions[$name][] = $restriction;
}
+
+ $roleObj = new Role();
+ $roleObjs[] = $roleObj
+ ->setName($roleName)
+ ->setPermissions($permissionsFromRole)
+ ->setRestrictions($restrictionsFromRole);
}
}
- return array($permissions, $restrictions);
+ $user->setPermissions($permissions);
+ $user->setRestrictions($restrictions);
+ $user->setRoles($roleObjs);
}
}
diff --git a/library/Icinga/Authentication/Auth.php b/library/Icinga/Authentication/Auth.php
index 9fb43922c..814f1366e 100644
--- a/library/Icinga/Authentication/Auth.php
+++ b/library/Icinga/Authentication/Auth.php
@@ -160,9 +160,7 @@ class Auth
}
$user->setGroups($groups);
$admissionLoader = new AdmissionLoader();
- list($permissions, $restrictions) = $admissionLoader->getPermissionsAndRestrictions($user);
- $user->setPermissions($permissions);
- $user->setRestrictions($restrictions);
+ $admissionLoader->applyRoles($user);
$this->user = $user;
if ($persist) {
$this->persistCurrentUser();
@@ -242,10 +240,10 @@ class Auth
public function authenticateFromSession()
{
$this->user = Session::getSession()->get('user');
- if ($this->user !== null && $this->user->isExternalUser() === true) {
+ if ($this->user !== null && $this->user->isExternalUser()) {
list($originUsername, $field) = $this->user->getExternalUserInformation();
- $username = getenv($field); // usually REMOTE_USER here
- if ( !$username || $username !== $originUsername) {
+ $username = ExternalBackend::getRemoteUser($field);
+ if ($username === null || $username !== $originUsername) {
$this->removeAuthorization();
}
}
diff --git a/library/Icinga/Authentication/Role.php b/library/Icinga/Authentication/Role.php
new file mode 100644
index 000000000..f00d063e2
--- /dev/null
+++ b/library/Icinga/Authentication/Role.php
@@ -0,0 +1,109 @@
+name;
+ }
+
+ /**
+ * Set the name of the role
+ *
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ /**
+ * Get the permissions of the role
+ *
+ * @return string[]
+ */
+ public function getPermissions()
+ {
+ return $this->permissions;
+ }
+
+ /**
+ * Set the permissions of the role
+ *
+ * @param string[] $permissions
+ *
+ * @return $this
+ */
+ public function setPermissions(array $permissions)
+ {
+ $this->permissions = $permissions;
+ return $this;
+ }
+
+ /**
+ * Get the restrictions of the role
+ *
+ * @param string $name Optional name of the restriction
+ *
+ * @return string[]|null
+ */
+ public function getRestrictions($name = null)
+ {
+ $restrictions = $this->restrictions;
+
+ if ($name === null) {
+ return $restrictions;
+ }
+
+ if (isset($restrictions[$name])) {
+ return $restrictions[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the restrictions of the role
+ *
+ * @param string[] $restrictions
+ *
+ * @return $this
+ */
+ public function setRestrictions(array $restrictions)
+ {
+ $this->restrictions = $restrictions;
+ return $this;
+ }
+}
diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php
index db3212499..3baf1c8e0 100644
--- a/library/Icinga/Authentication/User/ExternalBackend.php
+++ b/library/Icinga/Authentication/User/ExternalBackend.php
@@ -35,6 +35,14 @@ class ExternalBackend implements UserBackendInterface
$this->stripUsernameRegexp = $config->get('strip_username_regexp');
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
/**
* {@inheritdoc}
*/
@@ -45,11 +53,22 @@ class ExternalBackend implements UserBackendInterface
}
/**
- * {@inheritdoc}
+ * Get the remote user from environment or $_SERVER, if any
+ *
+ * @param string $variable The name variable where to read the user from
+ *
+ * @return string|null
*/
- public function getName()
+ public static function getRemoteUser($variable = 'REMOTE_USER')
{
- return $this->name;
+ $username = getenv($variable);
+ if ($username !== false) {
+ return $username;
+ }
+ if (array_key_exists($variable, $_SERVER)) {
+ return $_SERVER[$variable];
+ }
+ return null;
}
@@ -58,8 +77,8 @@ class ExternalBackend implements UserBackendInterface
*/
public function authenticate(User $user, $password = null)
{
- $username = getenv('REMOTE_USER');
- if ($username !== false) {
+ $username = static::getRemoteUser();
+ if ($username !== null) {
$user->setExternalUserInformation($username, 'REMOTE_USER');
if ($this->stripUsernameRegexp) {
diff --git a/library/Icinga/Data/Db/DbConnection.php b/library/Icinga/Data/Db/DbConnection.php
index f643d4849..81afb2c3d 100644
--- a/library/Icinga/Data/Db/DbConnection.php
+++ b/library/Icinga/Data/Db/DbConnection.php
@@ -152,11 +152,12 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
*/
$driverOptions[PDO::MYSQL_ATTR_INIT_COMMAND] =
'SET SESSION SQL_MODE=\'STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,'
- . 'NO_AUTO_CREATE_USER,ANSI_QUOTES,PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\';';
- if (array_key_exists('charset', $adapterParamaters) && $adapterParamaters['charset']) {
- $driverOptions[PDO::MYSQL_ATTR_INIT_COMMAND] .= 'SET NAMES ' . $adapterParamaters['charset']. ';';
+ . 'NO_AUTO_CREATE_USER,ANSI_QUOTES,PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\'';
+ if (isset($adapterParamaters['charset'])) {
+ $driverOptions[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', NAMES ' . $adapterParamaters['charset'];
unset($adapterParamaters['charset']);
}
+ $driverOptions[PDO::MYSQL_ATTR_INIT_COMMAND] .=';';
$adapterParamaters['port'] = $this->config->get('port', 3306);
break;
diff --git a/library/Icinga/Data/Tree/TreeNodeIterator.php b/library/Icinga/Data/Tree/TreeNodeIterator.php
index dcba0f1e9..839b1a1d0 100644
--- a/library/Icinga/Data/Tree/TreeNodeIterator.php
+++ b/library/Icinga/Data/Tree/TreeNodeIterator.php
@@ -92,6 +92,6 @@ class TreeNodeIterator implements RecursiveIterator
*/
public function isEmpty()
{
- return empty($this->children);
+ return ! $this->children->count();
}
}
diff --git a/library/Icinga/Protocol/Ldap/LdapConnection.php b/library/Icinga/Protocol/Ldap/LdapConnection.php
index 0729addfc..e741e9678 100644
--- a/library/Icinga/Protocol/Ldap/LdapConnection.php
+++ b/library/Icinga/Protocol/Ldap/LdapConnection.php
@@ -3,20 +3,21 @@
namespace Icinga\Protocol\Ldap;
-use Exception;
use ArrayIterator;
+use Exception;
+use LogicException;
+use stdClass;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
+use Icinga\Data\Filter\Filter;
+use Icinga\Data\Filter\FilterChain;
+use Icinga\Data\Filter\FilterExpression;
use Icinga\Data\Inspectable;
use Icinga\Data\Inspection;
use Icinga\Data\Selectable;
use Icinga\Data\Sortable;
-use Icinga\Data\Filter\Filter;
-use Icinga\Data\Filter\FilterChain;
-use Icinga\Data\Filter\FilterExpression;
use Icinga\Exception\ProgrammingError;
-use Icinga\Protocol\Ldap\LdapException;
/**
* Encapsulate LDAP connections and query creation
@@ -548,6 +549,23 @@ class LdapConnection implements Selectable, Inspectable
return $pairs;
}
+ /**
+ * Fetch an LDAP entry by its DN
+ *
+ * @param string $dn
+ * @param array|null $fields
+ *
+ * @return StdClass|bool
+ */
+ public function fetchByDn($dn, array $fields = null)
+ {
+ return $this->select()
+ ->from('*', $fields)
+ ->setBase($dn)
+ ->setScope('base')
+ ->fetchRow();
+ }
+
/**
* Test the given LDAP credentials by establishing a connection and attempting a LDAP bind
*
@@ -706,7 +724,7 @@ class LdapConnection implements Selectable, Inspectable
'value' => $this->encodeSortRules($query->getOrder())
)
));
- } else {
+ } elseif (! empty($fields)) {
foreach ($query->getOrder() as $rule) {
if (! in_array($rule[0], $fields, true)) {
$fields[] = $rule[0];
@@ -825,7 +843,7 @@ class LdapConnection implements Selectable, Inspectable
$ds = $this->getConnection();
$serverSorting = false;//$this->getCapabilities()->hasOid(LdapCapabilities::LDAP_SERVER_SORT_OID);
- if (! $serverSorting && $query->hasOrder()) {
+ if (! $serverSorting && $query->hasOrder() && ! empty($fields)) {
foreach ($query->getOrder() as $rule) {
if (! in_array($rule[0], $fields, true)) {
$fields[] = $rule[0];
@@ -1179,6 +1197,8 @@ class LdapConnection implements Selectable, Inspectable
* @param int $deref
*
* @return resource|bool A search result identifier or false on error
+ *
+ * @throws LogicException If the LDAP query search scope is unsupported
*/
public function ldapSearch(
LdapQuery $query,
@@ -1190,6 +1210,7 @@ class LdapConnection implements Selectable, Inspectable
) {
$queryString = (string) $query;
$baseDn = $query->getBase() ?: $this->getDn();
+ $scope = $query->getScope();
if (Logger::getInstance()->getLevel() === Logger::DEBUG) {
// We're checking the level by ourself to avoid rendering the ldapsearch commandline for nothing
@@ -1213,11 +1234,12 @@ class LdapConnection implements Selectable, Inspectable
}
Logger::debug("Issueing LDAP search. Use '%s' to reproduce.", sprintf(
- 'ldapsearch -P 3%s -H "%s"%s -b "%s" -s "sub" -z %u -l %u -a "%s"%s%s%s',
+ 'ldapsearch -P 3%s -H "%s"%s -b "%s" -s "%s" -z %u -l %u -a "%s"%s%s%s',
$starttlsParam,
$ldapUrl,
$bindParams,
$baseDn,
+ $scope,
$sizelimit,
$timelimit,
$derefName,
@@ -1227,7 +1249,21 @@ class LdapConnection implements Selectable, Inspectable
));
}
- return @ldap_search(
+ switch($scope) {
+ case LdapQuery::SCOPE_SUB:
+ $function = 'ldap_search';
+ break;
+ case LdapQuery::SCOPE_ONE:
+ $function = 'ldap_list';
+ break;
+ case LdapQuery::SCOPE_BASE:
+ $function = 'ldap_read';
+ break;
+ default:
+ throw new LogicException('LDAP scope %s not supported by ldapSearch', $scope);
+ }
+
+ return @$function(
$this->getConnection(),
$baseDn,
$queryString,
diff --git a/library/Icinga/Protocol/Ldap/LdapQuery.php b/library/Icinga/Protocol/Ldap/LdapQuery.php
index 1184765ba..7f31d32a0 100644
--- a/library/Icinga/Protocol/Ldap/LdapQuery.php
+++ b/library/Icinga/Protocol/Ldap/LdapQuery.php
@@ -3,6 +3,7 @@
namespace Icinga\Protocol\Ldap;
+use LogicException;
use Icinga\Data\SimpleQuery;
/**
@@ -38,6 +39,39 @@ class LdapQuery extends SimpleQuery
*/
protected $nativeFilter;
+ /**
+ * Only fetch the entry at the base of the search
+ */
+ const SCOPE_BASE = 'base';
+
+ /**
+ * Fetch entries one below the base DN
+ */
+ const SCOPE_ONE = 'one';
+
+ /**
+ * Fetch all entries below the base DN
+ */
+ const SCOPE_SUB = 'sub';
+
+ /**
+ * All available scopes
+ *
+ * @var array
+ */
+ public static $scopes = array(
+ LdapQuery::SCOPE_BASE,
+ LdapQuery::SCOPE_ONE,
+ LdapQuery::SCOPE_SUB
+ );
+
+ /**
+ * LDAP search scope (default: SCOPE_SUB)
+ *
+ * @var string
+ */
+ protected $scope = LdapQuery::SCOPE_SUB;
+
/**
* Initialize this query
*/
@@ -223,4 +257,38 @@ class LdapQuery extends SimpleQuery
{
return $this->renderFilter();
}
+
+ /**
+ * Get LDAP search scope
+ *
+ * @return string
+ */
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
+ /**
+ * Set LDAP search scope
+ *
+ * Valid: sub one base (Default: sub)
+ *
+ * @param string $scope
+ *
+ * @return LdapQuery
+ *
+ * @throws LogicException If scope value is invalid
+ */
+ public function setScope($scope)
+ {
+ if (! in_array($scope, static::$scopes)) {
+ throw new LogicException(
+ 'Can\'t set scope %d, it is is invalid. Use one of %s or LdapQuery\'s constants.',
+ $scope, implode(', ', static::$scopes)
+ );
+ }
+ $this->scope = $scope;
+ return $this;
+ }
+
}
diff --git a/library/Icinga/Protocol/Ldap/LdapUtils.php b/library/Icinga/Protocol/Ldap/LdapUtils.php
index 68031fa57..3d86cf100 100644
--- a/library/Icinga/Protocol/Ldap/LdapUtils.php
+++ b/library/Icinga/Protocol/Ldap/LdapUtils.php
@@ -28,9 +28,11 @@ class LdapUtils
$res = ldap_explode_dn($dn, $with_type ? 0 : 1);
foreach ($res as $k => $v) {
- $res[$k] = preg_replace(
- '/\\\([0-9a-f]{2})/ei',
- "chr(hexdec('\\1'))",
+ $res[$k] = preg_replace_callback(
+ '/\\\([0-9a-f]{2})/i',
+ function ($m) {
+ return chr(hexdec($m[1]));
+ },
$v
);
}
diff --git a/library/Icinga/User.php b/library/Icinga/User.php
index cbd8c4743..738df109e 100644
--- a/library/Icinga/User.php
+++ b/library/Icinga/User.php
@@ -6,6 +6,7 @@ namespace Icinga;
use DateTimeZone;
use InvalidArgumentException;
use Icinga\Application\Config;
+use Icinga\Authentication\Role;
use Icinga\User\Preferences;
use Icinga\Web\Navigation\Navigation;
@@ -91,6 +92,13 @@ class User
*/
protected $groups = array();
+ /**
+ * Roles of this user
+ *
+ * @var Role[]
+ */
+ protected $roles = array();
+
/**
* Preferences object
*
@@ -229,13 +237,39 @@ class User
}
/**
- * Settter for restrictions
+ * Set the user's restrictions
*
- * @param array $restrictions
+ * @param string[] $restrictions
+ *
+ * @return $this
*/
public function setRestrictions(array $restrictions)
{
$this->restrictions = $restrictions;
+ return $this;
+ }
+
+ /**
+ * Get the roles of the user
+ *
+ * @return Role[]
+ */
+ public function getRoles()
+ {
+ return $this->roles;
+ }
+
+ /**
+ * Set the roles of the user
+ *
+ * @param Role[] $roles
+ *
+ * @return $this
+ */
+ public function setRoles(array $roles)
+ {
+ $this->roles = $roles;
+ return $this;
}
/**
diff --git a/library/Icinga/Util/DirectoryIterator.php b/library/Icinga/Util/DirectoryIterator.php
index 3addbd2bb..60368733e 100644
--- a/library/Icinga/Util/DirectoryIterator.php
+++ b/library/Icinga/Util/DirectoryIterator.php
@@ -3,14 +3,22 @@
namespace Icinga\Util;
+use ArrayIterator;
use InvalidArgumentException;
-use Iterator;
+use RecursiveIterator;
/**
* Iterator for traversing a directory
*/
-class DirectoryIterator implements Iterator
+class DirectoryIterator implements RecursiveIterator
{
+ /**
+ * Iterate files first
+ *
+ * @var int
+ */
+ const FILES_FIRST = 1;
+
/**
* Current directory item
*
@@ -26,11 +34,18 @@ class DirectoryIterator implements Iterator
protected $extension;
/**
- * Directory handle
+ * Scanned files
*
- * @var resource
+ * @var ArrayIterator
*/
- private $handle;
+ private $files;
+
+ /**
+ * Iterator flags
+ *
+ * @var int
+ */
+ protected $flags;
/**
* Current key
@@ -46,6 +61,13 @@ class DirectoryIterator implements Iterator
*/
protected $path;
+ /**
+ * Directory queue if FILES_FIRST flag is set
+ *
+ * @var array
+ */
+ private $queue;
+
/**
* Whether to skip empty files
*
@@ -72,8 +94,9 @@ class DirectoryIterator implements Iterator
*
* @param string $path The path of the directory to traverse
* @param string $extension The file extension to filter for. A leading dot is optional
+ * @param int $flags Iterator flags
*/
- public function __construct($path, $extension = null)
+ public function __construct($path, $extension = null, $flags = null)
{
if (empty($path)) {
throw new InvalidArgumentException('The path can\'t be empty');
@@ -82,6 +105,9 @@ class DirectoryIterator implements Iterator
if (! empty($extension)) {
$this->extension = '.' . ltrim($extension, '.');
}
+ if ($flags !== null) {
+ $this->flags = $flags;
+ }
}
/**
@@ -96,6 +122,23 @@ class DirectoryIterator implements Iterator
return is_dir($path) && is_readable($path);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function hasChildren()
+ {
+ return static::isReadable($this->current);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getChildren()
+ {
+ return new static($this->current, $this->extension, $this->flags);
+ }
+
+
/**
* {@inheritdoc}
*/
@@ -110,12 +153,14 @@ class DirectoryIterator implements Iterator
public function next()
{
do {
- $file = readdir($this->handle);
- if ($file === false) {
- $key = false;
+ $this->files->next();
+ $skip = false;
+ if (! $this->files->valid()) {
+ $file = false;
+ $path = false;
break;
} else {
- $skip = false;
+ $file = $this->files->current();
do {
if ($this->skipHidden && $file[0] === '.') {
$skip = true;
@@ -125,7 +170,10 @@ class DirectoryIterator implements Iterator
$path = $this->path . '/' . $file;
if (is_dir($path)) {
- $skip = true;
+ if ($this->flags & static::FILES_FIRST === static::FILES_FIRST) {
+ $this->queue[] = array($path, $file);
+ $skip = true;
+ }
break;
}
@@ -138,16 +186,18 @@ class DirectoryIterator implements Iterator
$skip = true;
break;
}
-
- $key = $file;
- $file = $path;
} while (0);
}
} while ($skip);
- $this->current = $file;
/** @noinspection PhpUndefinedVariableInspection */
- $this->key = $key;
+
+ if ($path === false && ! empty($this->queue)) {
+ list($path, $file) = array_shift($this->queue);
+ }
+
+ $this->current = $path;
+ $this->key = $file;
}
/**
@@ -171,21 +221,13 @@ class DirectoryIterator implements Iterator
*/
public function rewind()
{
- if ($this->handle === null) {
- $this->handle = opendir($this->path);
- } else {
- rewinddir($this->handle);
+ if ($this->files === null) {
+ $files = scandir($this->path);
+ natcasesort($files);
+ $this->files = new ArrayIterator($files);
}
+ $this->files->rewind();
+ $this->queue = array();
$this->next();
}
-
- /**
- * Close directory handle if created
- */
- public function __destruct()
- {
- if ($this->handle !== null) {
- closedir($this->handle);
- }
- }
}
diff --git a/library/Icinga/Web/Form/Decorator/Autosubmit.php b/library/Icinga/Web/Form/Decorator/Autosubmit.php
index e4392bbd9..f3ce02d53 100644
--- a/library/Icinga/Web/Form/Decorator/Autosubmit.php
+++ b/library/Icinga/Web/Form/Decorator/Autosubmit.php
@@ -100,7 +100,7 @@ class Autosubmit extends Zend_Form_Decorator_Abstract
: t('Upon its value has changed, this field issues an automatic update of this page.');
$content .= $this->getView()->icon('cw', $warning, array(
'aria-hidden' => $isForm ? 'false' : 'true',
- 'class' => 'spinner'
+ 'class' => 'spinner autosubmit-info'
));
if (! $isForm && $this->getAccessible()) {
$content = '' . $warning . '' . $content;
diff --git a/library/Icinga/Web/LessCompiler.php b/library/Icinga/Web/LessCompiler.php
index acf616d18..7ec34a143 100644
--- a/library/Icinga/Web/LessCompiler.php
+++ b/library/Icinga/Web/LessCompiler.php
@@ -70,7 +70,7 @@ class LessCompiler
*/
public function addLessFile($lessFile)
{
- $this->lessFiles[] = $lessFile;
+ $this->lessFiles[] = realpath($lessFile);
return $this;
}
@@ -87,7 +87,7 @@ class LessCompiler
if (! isset($this->moduleLessFiles[$moduleName])) {
$this->moduleLessFiles[$moduleName] = array();
}
- $this->moduleLessFiles[$moduleName][] = $lessFile;
+ $this->moduleLessFiles[$moduleName][] = realpath($lessFile);
return $this;
}
@@ -98,9 +98,12 @@ class LessCompiler
*/
public function getLessFiles()
{
- $lessFiles = iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator(
- $this->lessFiles + $this->moduleLessFiles
- )));
+ $lessFiles = $this->lessFiles;
+
+ foreach ($this->moduleLessFiles as $moduleLessFiles) {
+ $lessFiles = array_merge($lessFiles, $moduleLessFiles);
+ }
+
if ($this->theme !== null) {
$lessFiles[] = $this->theme;
}
diff --git a/library/Icinga/Web/Widget/Dashboard.php b/library/Icinga/Web/Widget/Dashboard.php
index 415a08857..ab47afb3f 100644
--- a/library/Icinga/Web/Widget/Dashboard.php
+++ b/library/Icinga/Web/Widget/Dashboard.php
@@ -201,12 +201,6 @@ class Dashboard extends AbstractWidget
{
/** @var $pane Pane */
foreach ($panes as $pane) {
- if ($pane->getDisabled()) {
- if ($this->hasPane($pane->getTitle()) === true) {
- $this->removePane($pane->getTitle());
- }
- continue;
- }
if ($this->hasPane($pane->getTitle()) === true) {
/** @var $current Pane */
$current = $this->panes[$pane->getName()];
@@ -231,6 +225,9 @@ class Dashboard extends AbstractWidget
$this->tabs = new Tabs();
foreach ($this->panes as $key => $pane) {
+ if ($pane->getDisabled()) {
+ continue;
+ }
$this->tabs->add(
$key,
array(
diff --git a/library/Icinga/Web/Widget/Dashboard/Pane.php b/library/Icinga/Web/Widget/Dashboard/Pane.php
index 587ea3a90..f323a613b 100644
--- a/library/Icinga/Web/Widget/Dashboard/Pane.php
+++ b/library/Icinga/Web/Widget/Dashboard/Pane.php
@@ -40,7 +40,7 @@ class Pane extends UserWidget
*
* @var bool
*/
- private $disabled;
+ private $disabled = false;
/**
* Create a new pane
diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php
index b8c058604..2f8094197 100644
--- a/library/Icinga/Web/Widget/Tabs.php
+++ b/library/Icinga/Web/Widget/Tabs.php
@@ -377,8 +377,8 @@ EOT;
array(
$tabs,
$drop,
- $close,
- $refresh
+ $refresh,
+ $close
),
$this->baseTpl
);
diff --git a/library/vendor/Parsedown/Parsedown.php b/library/vendor/Parsedown/Parsedown.php
index 71a033e2e..c8c92a392 100644
--- a/library/vendor/Parsedown/Parsedown.php
+++ b/library/vendor/Parsedown/Parsedown.php
@@ -17,7 +17,7 @@ class Parsedown
{
# ~
- const version = '1.5.0';
+ const version = '1.6.0';
# ~
@@ -107,12 +107,6 @@ class Parsedown
# ~
- protected $DefinitionTypes = array(
- '[' => array('Reference'),
- );
-
- # ~
-
protected $unmarkedBlockTypes = array(
'Code',
);
@@ -169,7 +163,7 @@ class Parsedown
# ~
- if (isset($CurrentBlock['incomplete']))
+ if (isset($CurrentBlock['continuable']))
{
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
@@ -185,8 +179,6 @@ class Parsedown
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
-
- unset($CurrentBlock['incomplete']);
}
}
@@ -226,7 +218,7 @@ class Parsedown
if (method_exists($this, 'block'.$blockType.'Continue'))
{
- $Block['incomplete'] = true;
+ $Block['continuable'] = true;
}
$CurrentBlock = $Block;
@@ -253,7 +245,7 @@ class Parsedown
# ~
- if (isset($CurrentBlock['incomplete']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
+ if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
@@ -394,16 +386,16 @@ class Parsedown
protected function blockFencedCode($Line)
{
- if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
+ if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
{
$Element = array(
'name' => 'code',
'text' => '',
);
- if (isset($matches[2]))
+ if (isset($matches[1]))
{
- $class = 'language-'.$matches[2];
+ $class = 'language-'.$matches[1];
$Element['attributes'] = array(
'class' => $class,
@@ -673,7 +665,9 @@ class Parsedown
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
- if (in_array($matches[1], $this->textLevelElements))
+ $element = strtolower($matches[1]);
+
+ if (in_array($element, $this->textLevelElements))
{
return;
}
@@ -736,8 +730,6 @@ class Parsedown
{
$Block['closed'] = true;
}
-
- $Block['markup'] .= $matches[1];
}
if (isset($Block['interrupted']))
@@ -989,15 +981,13 @@ class Parsedown
{
$markup = '';
- $unexaminedText = $text;
+ # $excerpt is based on the first occurrence of a marker
- $markerPosition = 0;
-
- while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList))
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList))
{
$marker = $excerpt[0];
- $markerPosition += strpos($unexaminedText, $marker);
+ $markerPosition = strpos($text, $marker);
$Excerpt = array('text' => $excerpt, 'context' => $text);
@@ -1010,34 +1000,42 @@ class Parsedown
continue;
}
- if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker
+ # makes sure that the inline belongs to "our" marker
+
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
{
continue;
}
+ # sets a default inline position
+
if ( ! isset($Inline['position']))
{
$Inline['position'] = $markerPosition;
}
+ # the text that comes before the inline
$unmarkedText = substr($text, 0, $Inline['position']);
+ # compile the unmarked text
$markup .= $this->unmarkedText($unmarkedText);
+ # compile the inline
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
+ # remove the examined text
$text = substr($text, $Inline['position'] + $Inline['extent']);
- $unexaminedText = $text;
-
- $markerPosition = 0;
-
continue 2;
}
- $unexaminedText = substr($excerpt, 1);
+ # the marker does not belong to an inline
- $markerPosition ++;
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ $text = substr($text, $markerPosition + 1);
}
$markup .= $this->unmarkedText($text);
@@ -1199,7 +1197,7 @@ class Parsedown
return;
}
- if (preg_match('/^[(]((?:[^ (]|[(][^ )]+[)])+)(?:[ ]+("[^"]+"|\'[^\']+\'))?[)]/', $remainder, $matches))
+ if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
@@ -1214,7 +1212,7 @@ class Parsedown
{
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
{
- $definition = $matches[1] ? $matches[1] : $Element['text'];
+ $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
$definition = strtolower($definition);
$extent += strlen($matches[0]);
@@ -1360,11 +1358,6 @@ class Parsedown
}
}
- #
- # ~
-
- protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url');
-
# ~
protected function unmarkedText($text)
@@ -1409,7 +1402,7 @@ class Parsedown
if (isset($Element['handler']))
{
- $markup .= $this->$Element['handler']($Element['text']);
+ $markup .= $this->{$Element['handler']}($Element['text']);
}
else
{
@@ -1483,7 +1476,7 @@ class Parsedown
return self::$instances[$name];
}
- $instance = new self();
+ $instance = new static();
self::$instances[$name] = $instance;
diff --git a/library/vendor/Parsedown/SOURCE b/library/vendor/Parsedown/SOURCE
index cbbe887be..38fc9f5bf 100644
--- a/library/vendor/Parsedown/SOURCE
+++ b/library/vendor/Parsedown/SOURCE
@@ -1,4 +1,4 @@
-RELEASE=1.5.0
+RELEASE=1.6.0
PARSEDOWN=parsedown-$RELEASE
curl https://codeload.github.com/erusev/parsedown/tar.gz/${RELEASE} -o ${PARSEDOWN}.tar.gz
tar xfz ${PARSEDOWN}.tar.gz --strip-components 1 ${PARSEDOWN}/Parsedown.php ${PARSEDOWN}/LICENSE.txt
diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php
index b6b2d6375..fafd94656 100644
--- a/modules/doc/application/controllers/ModuleController.php
+++ b/modules/doc/application/controllers/ModuleController.php
@@ -3,6 +3,8 @@
namespace Icinga\Module\Doc\Controllers;
+use finfo;
+use SplFileInfo;
use Icinga\Application\Icinga;
use Icinga\Module\Doc\DocController;
use Icinga\Module\Doc\Exception\DocException;
@@ -88,11 +90,12 @@ class ModuleController extends DocController
{
$module = $this->params->getRequired('moduleName');
$this->assertModuleInstalled($module);
- $this->view->moduleName = $module;
+ $moduleManager = Icinga::app()->getModuleManager();
+ $name = $moduleManager->getModule($module)->getTitle();
try {
$this->renderToc(
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
- $module,
+ $name,
'doc/module/chapter',
array('moduleName' => $module)
);
@@ -120,6 +123,7 @@ class ModuleController extends DocController
$this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')),
$chapter,
'doc/module/chapter',
+ 'doc/module/img',
array('moduleName' => $module)
);
} catch (DocException $e) {
@@ -127,6 +131,60 @@ class ModuleController extends DocController
}
}
+ /**
+ * Deliver images
+ */
+ public function imageAction()
+ {
+ $module = $this->params->getRequired('moduleName');
+ $image = $this->params->getRequired('image');
+ $docPath = $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc'));
+ $imagePath = realpath($docPath . '/' . $image);
+ if ($imagePath === false) {
+ $this->httpNotFound('%s does not exist', $image);
+ }
+
+ $this->_helper->viewRenderer->setNoRender(true);
+ $this->_helper->layout()->disableLayout();
+
+ $imageInfo = new SplFileInfo($imagePath);
+
+ $ETag = md5($imageInfo->getMTime() . $imagePath);
+ $lastModified = gmdate('D, d M Y H:i:s T', $imageInfo->getMTime());
+ $match = false;
+
+ if (isset($_SERER['HTTP_IF_NONE_MATCH'])) {
+ $ifNoneMatch = explode(', ', stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
+ foreach ($ifNoneMatch as $tag) {
+ if ($tag === $ETag) {
+ $match = true;
+ break;
+ }
+ }
+ } elseif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+ $lastModifiedSince = stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']);
+ if ($lastModifiedSince === $lastModified) {
+ $match = true;
+ }
+ }
+
+ header('ETag: "' . $ETag . '"');
+ header('Cache-Control: no-transform,public,max-age=3600');
+ header('Last-Modified: ' . $lastModified);
+ // Set additional headers for compatibility reasons (Cache-Control should have precedence) in case
+ // session.cache_limiter is set to no cache
+ header('Pragma: cache');
+ header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + 3600));
+
+ if ($match) {
+ header('HTTP/1.1 304 Not Modified');
+ } else {
+ $finfo = new finfo();
+ header('Content-Type: ' . $finfo->file($imagePath, FILEINFO_MIME_TYPE));
+ readfile($imagePath);
+ }
+ }
+
/**
* View a module's documentation as PDF
*
diff --git a/modules/doc/doc/1-module-documentation.md b/modules/doc/doc/1-module-documentation.md
index edf20aac2..2334324a9 100644
--- a/modules/doc/doc/1-module-documentation.md
+++ b/modules/doc/doc/1-module-documentation.md
@@ -1,6 +1,6 @@
# Writing Module Documentation
-
+
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
@@ -50,13 +50,15 @@ This syntax is also supported in Icinga Web 2.
## Including Images
-Images must placed in the `img` directory beneath your module's `public` directory, e.g.:
+Images must placed in the `doc` directory beneath your module's root directory, e.g.:
- example-module/public/img/doc
+ /path/to/icingaweb2/modules/example-module/doc/img/example.png
+
+Note that the `img` sub directory is not mandatory but good for organizing your directory structure.
Module images can be accessed using the following URL:
- {baseURL}/img/{moduleName}/{file} e.g. icingaweb/img/example-module/doc/example.png
+ {baseURL}/doc/module/{moduleName}/image/{image} e.g. icingaweb2/doc/module/example-module/image/img/example.png
Markdown's image syntax is very similar to Markdown's link syntax, but prefixed with an exclamation mark, e.g.:
@@ -64,4 +66,4 @@ Markdown's image syntax is very similar to Markdown's link syntax, but prefixed
URLs to images inside your Markdown documentation files must be specified without the base URL, e.g.:
- 
+ 
diff --git a/modules/doc/library/Doc/DocController.php b/modules/doc/library/Doc/DocController.php
index acd085d04..47c96b1a9 100644
--- a/modules/doc/library/Doc/DocController.php
+++ b/modules/doc/library/Doc/DocController.php
@@ -20,6 +20,9 @@ class DocController extends Controller
if ($this->hasParam('chapter')) {
$this->params->set('chapter', $this->getParam('chapter'));
}
+ if ($this->hasParam('image')) {
+ $this->params->set('image', $this->getParam('image'));
+ }
if ($this->hasParam('moduleName')) {
$this->params->set('moduleName', $this->getParam('moduleName'));
}
@@ -31,20 +34,27 @@ class DocController extends Controller
* @param string $path Path to the documentation
* @param string $chapter ID of the chapter
* @param string $url URL to replace links with
+ * @param string $imageUrl URL to images
* @param array $urlParams Additional URL parameters
*/
- protected function renderChapter($path, $chapter, $url, array $urlParams = array())
+ protected function renderChapter($path, $chapter, $url, $imageUrl = null, array $urlParams = array())
{
$parser = new DocParser($path);
$section = new DocSectionRenderer($parser->getDocTree(), DocSectionRenderer::decodeUrlParam($chapter));
$this->view->section = $section
+ ->setHighlightSearch($this->params->get('highlight-search'))
+ ->setImageUrl($imageUrl)
->setUrl($url)
- ->setUrlParams($urlParams)
- ->setHighlightSearch($this->params->get('highlight-search'));
- $this->view->title = $chapter;
+ ->setUrlParams($urlParams);
+ $first = null;
+ foreach ($section as $first) {
+ break;
+ }
+ $title = $first === null ? ucfirst($chapter) : $first->getTitle();
+ $this->view->title = $title;
$this->getTabs()->add('toc', array(
'active' => true,
- 'title' => ucfirst($chapter),
+ 'title' => $title,
'url' => Url::fromRequest()
));
$this->render('chapter', null, true);
@@ -66,10 +76,10 @@ class DocController extends Controller
->setUrl($url)
->setUrlParams($urlParams);
$name = ucfirst($name);
- $this->view->title = sprintf($this->translate('%s Documentation'), $name);
+ $title = sprintf($this->translate('%s Documentation'), $name);
$this->getTabs()->add('toc', array(
'active' => true,
- 'title' => $name,
+ 'title' => $title,
'url' => Url::fromRequest()
));
$this->render('toc', null, true);
diff --git a/modules/doc/library/Doc/DocParser.php b/modules/doc/library/Doc/DocParser.php
index 6d52665c7..ac98584b6 100644
--- a/modules/doc/library/Doc/DocParser.php
+++ b/modules/doc/library/Doc/DocParser.php
@@ -4,6 +4,7 @@
namespace Icinga\Module\Doc;
use CachingIterator;
+use RecursiveIteratorIterator;
use SplFileObject;
use SplStack;
use Icinga\Data\Tree\SimpleTree;
@@ -61,7 +62,7 @@ class DocParser
);
}
$this->path = $path;
- $this->docIterator = new DirectoryIterator($path, 'md');
+ $this->docIterator = new DirectoryIterator($path, 'md', DirectoryIterator::FILES_FIRST);
}
/**
@@ -117,9 +118,37 @@ class DocParser
} else {
$id = null;
}
+ /** @noinspection PhpUndefinedVariableInspection */
return array($header, $id, $level, $headerStyle);
}
+ /**
+ * Generate unique section ID
+ *
+ * @param string $id
+ * @param string $filename
+ * @param SimpleTree $tree
+ *
+ * @return string
+ */
+ protected function uuid($id, $filename, SimpleTree $tree)
+ {
+ $id = str_replace(' ', '-', $id);
+ if ($tree->getNode($id) === null) {
+ return $id;
+ }
+ $id = $id . '-' . md5($filename);
+ $offset = 0;
+ while ($tree->getNode($id)) {
+ if ($offset++ === 0) {
+ $id .= '-' . $offset;
+ } else {
+ $id = substr($id, 0, -1) . $offset;
+ }
+ }
+ return $id;
+ }
+
/**
* Get the documentation tree
*
@@ -128,7 +157,7 @@ class DocParser
public function getDocTree()
{
$tree = new SimpleTree();
- foreach ($this->docIterator as $filename) {
+ foreach (new RecursiveIteratorIterator($this->docIterator) as $filename) {
$file = new SplFileObject($filename);
$lastLine = null;
$stack = new SplStack();
@@ -154,9 +183,9 @@ class DocParser
} else {
$noFollow = false;
}
- if ($tree->getNode($id) !== null) {
- $id = uniqid($id);
- }
+
+ $id = $this->uuid($id, $filename, $tree);
+
$section = new DocSection();
$section
->setId($id)
@@ -178,10 +207,7 @@ class DocParser
} else {
if ($stack->isEmpty()) {
$title = ucfirst($file->getBasename('.' . pathinfo($file->getFilename(), PATHINFO_EXTENSION)));
- $id = $title;
- if ($tree->getNode($id) !== null) {
- $id = uniqid($id);
- }
+ $id = $this->uuid($title, $filename, $tree);
$section = new DocSection();
$section
->setId($id)
diff --git a/modules/doc/library/Doc/DocSection.php b/modules/doc/library/Doc/DocSection.php
index 3138a70be..ce5297e1d 100644
--- a/modules/doc/library/Doc/DocSection.php
+++ b/modules/doc/library/Doc/DocSection.php
@@ -88,14 +88,6 @@ class DocSection extends TreeNode
return $this->content;
}
- /**
- * {@inheritdoc}
- */
- public function setId($id)
- {
- return parent::setId(str_replace(' ', '-', (string) $id));
- }
-
/**
* Set the header level
*
diff --git a/modules/doc/library/Doc/Renderer/DocRenderer.php b/modules/doc/library/Doc/Renderer/DocRenderer.php
index feb420e91..3662fd574 100644
--- a/modules/doc/library/Doc/Renderer/DocRenderer.php
+++ b/modules/doc/library/Doc/Renderer/DocRenderer.php
@@ -13,6 +13,13 @@ use Icinga\Web\View;
*/
abstract class DocRenderer extends RecursiveIteratorIterator
{
+ /**
+ * URL to images
+ *
+ * @var string
+ */
+ protected $imageUrl;
+
/**
* URL to replace links with
*
@@ -34,6 +41,38 @@ abstract class DocRenderer extends RecursiveIteratorIterator
*/
protected $view;
+ /**
+ * Get the URL to images
+ *
+ * @return string
+ */
+ public function getImageUrl()
+ {
+ return $this->imageUrl;
+ }
+
+ /**
+ * Set the URL to images
+ *
+ * @param string $imageUrl
+ *
+ * @return $this
+ */
+ public function setImageUrl($imageUrl)
+ {
+ $this->imageUrl = (string) $imageUrl;
+ return $this;
+ }
+ /**
+ * Get the URL to replace links with
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
/**
* Set the URL to replace links with
*
@@ -48,13 +87,13 @@ abstract class DocRenderer extends RecursiveIteratorIterator
}
/**
- * Get the URL to replace links with
+ * Get additional URL parameters
*
- * @return string
+ * @return array
*/
- public function getUrl()
+ public function getUrlParams()
{
- return $this->url;
+ return $this->urlParams;
}
/**
@@ -71,13 +110,16 @@ abstract class DocRenderer extends RecursiveIteratorIterator
}
/**
- * Get additional URL parameters
+ * Get the view
*
- * @return array
+ * @return View
*/
- public function getUrlParams()
+ public function getView()
{
- return $this->urlParams;
+ if ($this->view === null) {
+ $this->view = Icinga::app()->getViewRenderer()->view;
+ }
+ return $this->view;
}
/**
@@ -93,19 +135,6 @@ abstract class DocRenderer extends RecursiveIteratorIterator
return $this;
}
- /**
- * Get the view
- *
- * @return View
- */
- public function getView()
- {
- if ($this->view === null) {
- $this->view = Icinga::app()->getViewRenderer()->view;
- }
- return $this->view;
- }
-
/**
* Encode an anchor identifier
*
diff --git a/modules/doc/library/Doc/Renderer/DocSectionRenderer.php b/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
index a39d24a77..113f707c5 100644
--- a/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
+++ b/modules/doc/library/Doc/Renderer/DocSectionRenderer.php
@@ -190,7 +190,20 @@ class DocSectionRenderer extends DocRenderer
$xpath = new DOMXPath($doc);
$img = $xpath->query('//img[1]')->item(0);
/** @var \DOMElement $img */
- $img->setAttribute('src', Url::fromPath($img->getAttribute('src'))->getAbsoluteUrl());
+ $path = $this->getView()->getHelper('Url')->url(
+ array_merge(
+ array(
+ 'image' => $img->getAttribute('src')
+ ),
+ $this->urlParams
+ ),
+ $this->imageUrl,
+ false,
+ false
+ );
+ $url = $this->getView()->url($path);
+ /** @var \Icinga\Web\Url $url */
+ $img->setAttribute('src', $url->getAbsoluteUrl());
return substr_replace($doc->saveXML($img), '', -2, 1); // Replace '/>' with '>'
}
@@ -274,7 +287,7 @@ class DocSectionRenderer extends DocRenderer
$html
);
$html = preg_replace_callback(
- '#.+ #ms',
+ '#.+? #ms',
array($this, 'markupNotes'),
$html
);
diff --git a/modules/doc/public/css/module.less b/modules/doc/public/css/module.less
index 26dd18fd0..35cfc3d3a 100644
--- a/modules/doc/public/css/module.less
+++ b/modules/doc/public/css/module.less
@@ -25,6 +25,7 @@ pre > code {
.chapter a {
border-bottom: 1px @gray-light dotted;
+ font-weight: @font-weight-bold;
&:hover {
border-bottom: 1px @text-color solid;
@@ -60,14 +61,11 @@ pre > code {
a {
&:before {
- .rounded-corners();
-
- background-color: @icinga-blue;
- color: @text-color-on-icinga-blue;
- content: counter(li) ".";
+ color: @icinga-blue;
+ content: counters(li,".") " ";
display: inline-block;
font-size: small;
- margin-right: 0.25em;
+ font-weight: @font-weight-bold;
min-width: 1.5em;
padding: 0.25em;
text-align: center;
diff --git a/modules/doc/run.php b/modules/doc/run.php
index 9d49b89d4..df9dd0930 100644
--- a/modules/doc/run.php
+++ b/modules/doc/run.php
@@ -43,7 +43,22 @@ $docModulePdf = new Zend_Controller_Router_Route(
)
);
+$docModuleImg = new Zend_Controller_Router_Route_Regex(
+ 'doc/module/([^/]+)/image/(.+)',
+ array(
+ 'controller' => 'module',
+ 'action' => 'image',
+ 'module' => 'doc'
+ ),
+ array(
+ 'moduleName' => 1,
+ 'image' => 2
+ ),
+ 'doc/module/%s/image/%s'
+);
+
$this->addRoute('doc/module/chapter', $docModuleChapter);
$this->addRoute('doc/icingaweb/chapter', $docIcingaWebChapter);
$this->addRoute('doc/module/toc', $docModuleToc);
$this->addRoute('doc/module/pdf', $docModulePdf);
+$this->addRoute('doc/module/img', $docModuleImg);
diff --git a/modules/monitoring/application/clicommands/ListCommand.php b/modules/monitoring/application/clicommands/ListCommand.php
index 26f50f592..7e875e37b 100644
--- a/modules/monitoring/application/clicommands/ListCommand.php
+++ b/modules/monitoring/application/clicommands/ListCommand.php
@@ -55,6 +55,9 @@ class ListCommand extends Command
$query->limit($limit, $this->params->shift('offset'));
}
foreach ($this->params->getParams() as $col => $filter) {
+ if (strtolower($col) === 'problems') {
+ $col = 'service_problem';
+ }
$query->where($col, $filter);
}
// $query->applyFilters($this->params->getParams());
diff --git a/modules/monitoring/application/controllers/HostsController.php b/modules/monitoring/application/controllers/HostsController.php
index 12017657c..17969df8d 100644
--- a/modules/monitoring/application/controllers/HostsController.php
+++ b/modules/monitoring/application/controllers/HostsController.php
@@ -49,7 +49,8 @@ class HostsController extends Controller
'host_obsessing',
'host_passive_checks_enabled',
'host_problem',
- 'host_state'
+ 'host_state',
+ 'instance_name'
));
$this->view->baseFilter = $this->hostList->getFilter();
$this->getTabs()->add(
diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php
index 35ce34797..8e493ed44 100644
--- a/modules/monitoring/application/controllers/ListController.php
+++ b/modules/monitoring/application/controllers/ListController.php
@@ -39,10 +39,18 @@ class ListController extends Controller
}
/**
- * Display host list
+ * List hosts
*/
public function hostsAction()
{
+ $this->addTitleTab(
+ 'hosts',
+ $this->translate('Hosts'),
+ $this->translate('List hosts')
+ );
+
+ $this->setAutorefreshInterval(10);
+
// Handle soft and hard states
if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') {
$stateColumn = 'host_hard_state';
@@ -51,9 +59,8 @@ class ListController extends Controller
$stateColumn = 'host_state';
$stateChangeColumn = 'host_last_state_change';
}
- $this->addTitleTab('hosts', $this->translate('Hosts'), $this->translate('List hosts'));
- $this->setAutorefreshInterval(10);
- $query = $this->backend->select()->from('hoststatus', array_merge(array(
+
+ $hosts = $this->backend->select()->from('hoststatus', array_merge(array(
'host_icon_image',
'host_icon_image_alt',
'host_name',
@@ -71,9 +78,20 @@ class ListController extends Controller
'host_active_checks_enabled',
'host_passive_checks_enabled'
), $this->addColumns()));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->hosts = $query;
+ $this->applyRestriction('monitoring/filter/objects', $hosts);
+ $this->filterQuery($hosts);
+
+ $this->setupPaginationControl($hosts);
+ $this->setupLimitControl();
+ $this->setupSortControl(array(
+ 'host_severity' => $this->translate('Severity'),
+ 'host_state' => $this->translate('Current State'),
+ 'host_display_name' => $this->translate('Hostname'),
+ 'host_address' => $this->translate('Address'),
+ 'host_last_check' => $this->translate('Last Check'),
+ 'host_last_state_change' => $this->translate('Last State Change')
+ ), $hosts);
+
$stats = $this->backend->select()->from('hoststatussummary', array(
'hosts_total',
'hosts_up',
@@ -86,28 +104,26 @@ class ListController extends Controller
'hosts_pending',
));
$this->applyRestriction('monitoring/filter/objects', $stats);
- $this->view->stats = $stats;
- $this->setupLimitControl();
- $this->setupPaginationControl($this->view->hosts);
- $this->setupSortControl(array(
- 'host_severity' => $this->translate('Severity'),
- 'host_state' => $this->translate('Current State'),
- 'host_display_name' => $this->translate('Hostname'),
- 'host_address' => $this->translate('Address'),
- 'host_last_check' => $this->translate('Last Check'),
- 'host_last_state_change' => $this->translate('Last State Change')
- ), $query);
- $summary = $query->getQuery()->queryServiceProblemSummary();
+ $summary = $hosts->getQuery()->queryServiceProblemSummary();
$this->applyRestriction('monitoring/filter/objects', $summary);
+
+ $this->view->hosts = $hosts;
+ $this->view->stats = $stats;
$this->view->summary = $summary->fetchPairs();
}
/**
- * Display service list
+ * List services
*/
public function servicesAction()
{
+ $this->addTitleTab(
+ 'services',
+ $this->translate('Services'),
+ $this->translate('List services')
+ );
+
// Handle soft and hard states
if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') {
$stateColumn = 'service_hard_state';
@@ -117,14 +133,9 @@ class ListController extends Controller
$stateChangeColumn = 'service_last_state_change';
}
- $this->addTitleTab('services', $this->translate('Services'), $this->translate('List services'));
- $this->view->showHost = true;
- if (strpos($this->params->get('host_name', '*'), '*') === false) {
- $this->view->showHost = false;
- }
$this->setAutorefreshInterval(10);
- $columns = array_merge(array(
+ $services = $this->backend->select()->from('servicestatus', array_merge(array(
'host_name',
'host_display_name',
'host_state',
@@ -147,14 +158,12 @@ class ListController extends Controller
'service_notifications_enabled',
'service_active_checks_enabled',
'service_passive_checks_enabled'
- ), $this->addColumns());
- $query = $this->backend->select()->from('servicestatus', $columns);
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->services = $query;
+ ), $this->addColumns()));
+ $this->applyRestriction('monitoring/filter/objects', $services);
+ $this->filterQuery($services);
+ $this->setupPaginationControl($services);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->services);
$this->setupSortControl(array(
'service_severity' => $this->translate('Service Severity'),
'service_state' => $this->translate('Current Service State'),
@@ -166,7 +175,7 @@ class ListController extends Controller
'host_display_name' => $this->translate('Hostname'),
'host_address' => $this->translate('Host Address'),
'host_last_check' => $this->translate('Last Host Check')
- ), $query);
+ ), $services);
$stats = $this->backend->select()->from('servicestatussummary', array(
'services_critical',
@@ -183,18 +192,30 @@ class ListController extends Controller
'services_warning_unhandled'
));
$this->applyRestriction('monitoring/filter/objects', $stats);
+
+ $this->view->services = $services;
$this->view->stats = $stats;
+ if (strpos($this->params->get('host_name', '*'), '*') === false) {
+ $this->view->showHost = false;
+ } else {
+ $this->view->showHost = true;
+ }
}
/**
- * Fetch the current downtimes and put them into the view property `downtimes`
+ * List downtimes
*/
public function downtimesAction()
{
- $this->addTitleTab('downtimes', $this->translate('Downtimes'), $this->translate('List downtimes'));
+ $this->addTitleTab(
+ 'downtimes',
+ $this->translate('Downtimes'),
+ $this->translate('List downtimes')
+ );
+
$this->setAutorefreshInterval(12);
- $query = $this->backend->select()->from('downtime', array(
+ $downtimes = $this->backend->select()->from('downtime', array(
'id' => 'downtime_internal_id',
'objecttype' => 'object_type',
'comment' => 'downtime_comment',
@@ -215,13 +236,11 @@ class ListController extends Controller
'host_display_name',
'service_display_name'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
-
- $this->view->downtimes = $query;
+ $this->applyRestriction('monitoring/filter/objects', $downtimes);
+ $this->filterQuery($downtimes);
+ $this->setupPaginationControl($downtimes);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->downtimes);
$this->setupSortControl(array(
'downtime_is_in_effect' => $this->translate('Is In Effect'),
'host_display_name' => $this->translate('Host'),
@@ -233,7 +252,9 @@ class ListController extends Controller
'downtime_scheduled_start' => $this->translate('Scheduled Start'),
'downtime_scheduled_end' => $this->translate('Scheduled End'),
'downtime_duration' => $this->translate('Duration')
- ), $query);
+ ), $downtimes);
+
+ $this->view->downtimes = $downtimes;
if ($this->Auth()->hasPermission('monitoring/command/downtime/delete')) {
$this->view->delDowntimeForm = new DeleteDowntimeCommandForm();
@@ -242,7 +263,7 @@ class ListController extends Controller
}
/**
- * Display notification overview
+ * List notifications
*/
public function notificationsAction()
{
@@ -251,9 +272,10 @@ class ListController extends Controller
$this->translate('Notifications'),
$this->translate('List notifications')
);
+
$this->setAutorefreshInterval(15);
- $query = $this->backend->select()->from('notification', array(
+ $notifications = $this->backend->select()->from('notification', array(
'host_name',
'service_description',
'notification_output',
@@ -263,22 +285,30 @@ class ListController extends Controller
'host_display_name',
'service_display_name'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->notifications = $query;
+ $this->applyRestriction('monitoring/filter/objects', $notifications);
+ $this->filterQuery($notifications);
+ $this->setupPaginationControl($notifications);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->notifications);
$this->setupSortControl(array(
'notification_start_time' => $this->translate('Notification Start')
- ), $query);
+ ), $notifications);
+
+ $this->view->notifications = $notifications;
}
+ /**
+ * List contacts
+ */
public function contactsAction()
{
- $this->addTitleTab('contacts', $this->translate('Contacts'), $this->translate('List contacts'));
+ $this->addTitleTab(
+ 'contacts',
+ $this->translate('Contacts'),
+ $this->translate('List contacts')
+ );
- $query = $this->backend->select()->from('contact', array(
+ $contacts = $this->backend->select()->from('contact', array(
'contact_name',
'contact_alias',
'contact_email',
@@ -286,20 +316,19 @@ class ListController extends Controller
'contact_notify_service_timeperiod',
'contact_notify_host_timeperiod'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->contacts = $query;
+ $this->applyRestriction('monitoring/filter/objects', $contacts);
+ $this->filterQuery($contacts);
+ $this->setupPaginationControl($contacts);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->contacts);
$this->setupSortControl(array(
- 'contact_name' => $this->translate('Name'),
+ 'contact_name' => $this->translate('Name'),
'contact_alias' => $this->translate('Alias'),
'contact_email' => $this->translate('Email'),
- 'contact_pager' => $this->translate('Pager Address / Number'),
- 'contact_notify_service_timeperiod' => $this->translate('Service Notification Timeperiod'),
- 'contact_notify_host_timeperiod' => $this->translate('Host Notification Timeperiod')
- ), $query);
+ 'contact_pager' => $this->translate('Pager Address / Number')
+ ), $contacts);
+
+ $this->view->contacts = $contacts;
}
public function eventgridAction()
@@ -342,6 +371,9 @@ class ListController extends Controller
$this->view->orientation = $orientation;
}
+ /**
+ * List contact groups
+ */
public function contactgroupsAction()
{
$this->addTitleTab(
@@ -350,47 +382,38 @@ class ListController extends Controller
$this->translate('List contact groups')
);
- $query = $this->backend->select()->from('contactgroup', array(
+ $contactGroups = $this->backend->select()->from('contactgroup', array(
'contactgroup_name',
'contactgroup_alias',
- 'contact_name',
- 'contact_alias',
- 'contact_email',
- 'contact_pager'
+ 'contact_count'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
+ $this->applyRestriction('monitoring/filter/objects', $contactGroups);
+ $this->filterQuery($contactGroups);
+ $this->setupPaginationControl($contactGroups);
+ $this->setupLimitControl();
$this->setupSortControl(array(
'contactgroup_name' => $this->translate('Contactgroup Name'),
'contactgroup_alias' => $this->translate('Contactgroup Alias')
- ), $query);
+ ), $contactGroups);
- // Fetch and prepare all contact groups:
- $contactgroups = $query->getQuery()->fetchAll();
- $groupData = array();
- foreach ($contactgroups as $c) {
- if (!array_key_exists($c->contactgroup_name, $groupData)) {
- $groupData[$c->contactgroup_name] = array(
- 'alias' => $c->contactgroup_alias,
- 'contacts' => array()
- );
- }
- if (isset ($c->contact_name)) {
- $groupData[$c->contactgroup_name]['contacts'][] = $c;
- }
- }
-
- // TODO: Find a better naming
- $this->view->groupData = $groupData;
+ $this->view->contactGroups = $contactGroups;
}
+ /**
+ * List all comments
+ */
public function commentsAction()
{
- $this->addTitleTab('comments', $this->translate('Comments'), $this->translate('List comments'));
+ $this->addTitleTab(
+ 'comments',
+ $this->translate('Comments'),
+ $this->translate('List comments')
+ );
+
$this->setAutorefreshInterval(12);
- $query = $this->backend->select()->from('comment', array(
+ $comments = $this->backend->select()->from('comment', array(
'id' => 'comment_internal_id',
'objecttype' => 'object_type',
'comment' => 'comment_data',
@@ -404,12 +427,11 @@ class ListController extends Controller
'host_display_name',
'service_display_name'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->comments = $query;
+ $this->applyRestriction('monitoring/filter/objects', $comments);
+ $this->filterQuery($comments);
+ $this->setupPaginationControl($comments);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->comments);
$this->setupSortControl(
array(
'comment_timestamp' => $this->translate('Comment Timestamp'),
@@ -418,15 +440,20 @@ class ListController extends Controller
'comment_type' => $this->translate('Comment Type'),
'comment_expiration' => $this->translate('Expiration')
),
- $query
+ $comments
);
+ $this->view->comments = $comments;
+
if ($this->Auth()->hasPermission('monitoring/command/comment/delete')) {
$this->view->delCommentForm = new DeleteCommentCommandForm();
$this->view->delCommentForm->handleRequest();
}
}
+ /**
+ * List service groups
+ */
public function servicegroupsAction()
{
$this->addTitleTab(
@@ -434,9 +461,10 @@ class ListController extends Controller
$this->translate('Service Groups'),
$this->translate('List service groups')
);
+
$this->setAutorefreshInterval(12);
- $query = $this->backend->select()->from('servicegroupsummary', array(
+ $serviceGroups = $this->backend->select()->from('servicegroupsummary', array(
'servicegroup_alias',
'servicegroup_name',
'services_critical_handled',
@@ -449,25 +477,34 @@ class ListController extends Controller
'services_warning_handled',
'services_warning_unhandled'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->servicegroups = $query;
+ $this->applyRestriction('monitoring/filter/objects', $serviceGroups);
+ $this->filterQuery($serviceGroups);
+ $this->setupPaginationControl($serviceGroups);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->servicegroups);
$this->setupSortControl(array(
'services_severity' => $this->translate('Severity'),
'servicegroup_alias' => $this->translate('Service Group Name'),
'services_total' => $this->translate('Total Services')
- ), $query);
+ ), $serviceGroups);
+
+ $this->view->serviceGroups = $serviceGroups;
}
+ /**
+ * List host groups
+ */
public function hostgroupsAction()
{
- $this->addTitleTab('hostgroups', $this->translate('Host Groups'), $this->translate('List host groups'));
+ $this->addTitleTab(
+ 'hostgroups',
+ $this->translate('Host Groups'),
+ $this->translate('List host groups')
+ );
+
$this->setAutorefreshInterval(12);
- $query = $this->backend->select()->from('hostgroupsummary', array(
+ $hostGroups = $this->backend->select()->from('hostgroupsummary', array(
'hostgroup_alias',
'hostgroup_name',
'hosts_down_handled',
@@ -487,18 +524,19 @@ class ListController extends Controller
'services_warning_handled',
'services_warning_unhandled'
));
- $this->applyRestriction('monitoring/filter/objects', $query);
- $this->filterQuery($query);
- $this->view->hostgroups = $query;
+ $this->applyRestriction('monitoring/filter/objects', $hostGroups);
+ $this->filterQuery($hostGroups);
+ $this->setupPaginationControl($hostGroups);
$this->setupLimitControl();
- $this->setupPaginationControl($this->view->hostgroups);
$this->setupSortControl(array(
'hosts_severity' => $this->translate('Severity'),
'hostgroup_alias' => $this->translate('Host Group Name'),
'hosts_total' => $this->translate('Total Hosts'),
'services_total' => $this->translate('Total Services')
- ), $query);
+ ), $hostGroups);
+
+ $this->view->hostGroups = $hostGroups;
}
public function eventhistoryAction()
diff --git a/modules/monitoring/application/controllers/ServicesController.php b/modules/monitoring/application/controllers/ServicesController.php
index 53312ae48..17e82124b 100644
--- a/modules/monitoring/application/controllers/ServicesController.php
+++ b/modules/monitoring/application/controllers/ServicesController.php
@@ -41,6 +41,7 @@ class ServicesController extends Controller
'host_name',
'host_problem',
'host_state',
+ 'instance_name',
'service_acknowledged',
'service_active_checks_enabled',
'service_description',
diff --git a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php
index ed50466fe..dbd786d5e 100644
--- a/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php
+++ b/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php
@@ -58,24 +58,27 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
*/
public function createElements(array $formData = array())
{
- if ((bool) $this->status->notifications_enabled) {
- if ($this->hasPermission('monitoring/command/feature/instance')) {
+ $notificationDescription = null;
+ $isIcinga2 = $this->getBackend()->isIcinga2($this->status->program_version);
+
+ if (! $isIcinga2) {
+ if ((bool) $this->status->notifications_enabled) {
+ if ($this->hasPermission('monitoring/command/feature/instance')) {
+ $notificationDescription = sprintf(
+ '%3$s',
+ $this->translate('Disable notifications for a specific time on a program-wide basis'),
+ $this->getView()->href('monitoring/health/disable-notifications'),
+ $this->translate('Disable temporarily')
+ );
+ } else {
+ $notificationDescription = null;
+ }
+ } elseif ($this->status->disable_notif_expire_time) {
$notificationDescription = sprintf(
- '%3$s',
- $this->translate('Disable notifications for a specific time on a program-wide basis'),
- $this->getView()->href('monitoring/health/disable-notifications'),
- $this->translate('Disable temporarily')
+ $this->translate('Notifications will be re-enabled in %s'),
+ $this->getView()->timeUntil($this->status->disable_notif_expire_time)
);
- } else {
- $notificationDescription = null;
}
- } elseif ($this->status->disable_notif_expire_time) {
- $notificationDescription = sprintf(
- $this->translate('Notifications will be re-enabled in %s'),
- $this->getView()->timeUntil($this->status->disable_notif_expire_time)
- );
- } else {
- $notificationDescription = null;
}
$toggleDisabled = $this->hasPermission('monitoring/command/feature/instance') ? null : '';
@@ -138,7 +141,7 @@ class ToggleInstanceFeaturesCommandForm extends CommandForm
)
);
- if (! $this->getBackend()->isIcinga2($this->status->program_version)) {
+ if (! $isIcinga2) {
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING,
diff --git a/modules/monitoring/application/views/helpers/IconImage.php b/modules/monitoring/application/views/helpers/IconImage.php
index 3fee8e3a7..8ae72d83e 100644
--- a/modules/monitoring/application/views/helpers/IconImage.php
+++ b/modules/monitoring/application/views/helpers/IconImage.php
@@ -27,7 +27,7 @@ class Zend_View_Helper_IconImage extends Zend_View_Helper_Abstract
public function host($object)
{
if ($object->host_icon_image && ! preg_match('/[\'"]/', $object->host_icon_image)) {
- return $this->view->img(
+ return $this->view->icon(
Macro::resolveMacros($object->host_icon_image, $object),
null,
array(
@@ -50,7 +50,7 @@ class Zend_View_Helper_IconImage extends Zend_View_Helper_Abstract
public function service($object)
{
if ($object->service_icon_image && ! preg_match('/[\'"]/', $object->service_icon_image)) {
- return $this->view->img(
+ return $this->view->icon(
Macro::resolveMacros($object->service_icon_image, $object),
null,
array(
diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php
index f5c73ee21..ba3013a3a 100644
--- a/modules/monitoring/application/views/helpers/PluginOutput.php
+++ b/modules/monitoring/application/views/helpers/PluginOutput.php
@@ -51,6 +51,14 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract
$isHtml = false;
}
$output = $this->fixLinks($output);
+ // Help browsers to break words in plugin output
+ $output = trim($output);
+ // Add space after comma where missing
+ $output = preg_replace('/,[^\s]/', ', ', $output);
+ // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces
+ $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2$3', $output);
+ // Add zero width space before '(' and '[' if not surrounded by whitespaces
+ $output = preg_replace('/([^\s])([([])([^\s])/', '$1$2$3', $output);
if (! $raw) {
if ($isHtml) {
diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml
index 0801a7e4d..4941922a9 100644
--- a/modules/monitoring/application/views/scripts/list/comments.phtml
+++ b/modules/monitoring/application/views/scripts/list/comments.phtml
@@ -1,11 +1,11 @@
compact): ?>
- = $tabs ?>
+ = $this->tabs ?>
= $this->render('list/components/selectioninfo.phtml') ?>
-
- = $this->sortBox ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
diff --git a/modules/monitoring/application/views/scripts/list/contactgroups.phtml b/modules/monitoring/application/views/scripts/list/contactgroups.phtml
index eea6302b1..c0656471a 100644
--- a/modules/monitoring/application/views/scripts/list/contactgroups.phtml
+++ b/modules/monitoring/application/views/scripts/list/contactgroups.phtml
@@ -2,52 +2,49 @@
if (! $this->compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
-translate('No contactgroups found matching the filter') . ' ';
- return;
-}
-?>
-
+hasResult()): ?>
+ = $this->translate('No contact groups found matching the filter') ?>
+
+
+
-
- |
- = $this->translate('Contact Group ') ?> |
- = $this->translate('Alias') ?> |
-
+
+ |
+ = $this->translate('Contact Group ') ?> |
+ = $this->translate('Alias') ?> |
+
-
- $groupInfo): ?>
-
-
- = count($groupInfo['contacts']) ?>
- |
+
+
+
+ = $contactGroup->contact_count ?>
+ |
+
+ = $this->qlink(
+ $contactGroup->contactgroup_name,
+ 'monitoring/list/contacts',
+ array('contactgroup_name' => $contactGroup->contactgroup_name),
+ array('title' => sprintf(
+ $this->translate('Show detailed information about %s'),
+ $contactGroup->contactgroup_name
+ ))
+ ) ?>
+ |
- = $this->qlink(
- $groupName,
- 'monitoring/list/contacts',
- array('contactgroup_name' => $groupName),
- array('title' => sprintf(
- $this->translate('Show detailed information about %s'),
- $groupName
- ))
- ) ?>
- |
-
-
- = $groupInfo['alias'] ?>
-
+ contactgroup_name !== $contactGroup->contactgroup_alias): ?>
+ = $contactGroup->contactgroup_alias ?>
+
|
diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml
index aca29d88e..f712ada8a 100644
--- a/modules/monitoring/application/views/scripts/list/contacts.phtml
+++ b/modules/monitoring/application/views/scripts/list/contacts.phtml
@@ -1,79 +1,83 @@
compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
-hasResult()): ?>
-
-
+hasResult()): ?>
+ = $this->translate('No contacts found matching the filter') ?>
+
+
+
+
= $this->translate('Name') ?> |
= $this->translate('Email') ?> |
= $this->translate('Pager') ?> |
-
-
- peekAhead($this->compact) as $contact): ?>
-
-
- = $this->qlink(
- $contact->contact_name,
- 'monitoring/show/contact',
- array('contact_name' => $contact->contact_name),
- array('title' => sprintf(
+
+ |
+ peekAhead($this->compact) as $contact): ?>
+
+
+ = $this->qlink(
+ $contact->contact_name,
+ 'monitoring/show/contact',
+ array('contact_name' => $contact->contact_name),
+ array(
+ 'title' => sprintf(
$this->translate('Show detailed information about %s'),
$contact->contact_alias
- ), 'class' => 'rowaction')
- ); ?>
- |
+ )
+ )
+ ) ?>
+
+
+ = $this->translate('Email') ?>:
+
+ = $this->escape($contact->contact_email) ?>
+
+ |
- = $this->translate('Email') ?>:
-
- = $this->escape($contact->contact_email); ?>
-
+ contact_pager): ?>
+ = $this->escape($contact->contact_pager) ?>
+
|
-
- contact_pager): ?>
- = $this->escape($contact->contact_pager) ?>
-
- |
- contact_notify_service_timeperiod): ?>
-
- = $this->escape($contact->contact_notify_service_timeperiod) ?>
- |
-
+ contact_notify_service_timeperiod): ?>
+
+ = $this->escape($contact->contact_notify_service_timeperiod) ?>
+ |
+
- contact_notify_host_timeperiod): ?>
-
- = $this->escape($contact->contact_notify_host_timeperiod) ?>
- |
-
-
-
-
+ contact_notify_host_timeperiod): ?>
+
+ = $this->escape($contact->contact_notify_host_timeperiod) ?>
+ |
+
+
+
+
- hasMore()): ?>
+hasMore()): ?>
+
= $this->qlink(
$this->translate('Show More'),
$this->url()->without(array('view', 'limit')),
null,
array(
- 'data-base-target' => '_next',
- 'class' => 'pull-right action-link'
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
)
- ); ?>
-
-
- = $this->translate('No contacts found matching the filter'); ?>
+ ) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml
index 1033fdfe8..cefddfa53 100644
--- a/modules/monitoring/application/views/scripts/list/downtimes.phtml
+++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml
@@ -3,51 +3,56 @@ use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
if (! $this->compact): ?>
-
- = $tabs ?>
+
+ = $this->tabs ?>
= $this->render('list/components/selectioninfo.phtml') ?>
-
- = $this->sortBox ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
- "
- data-icinga-multiselect-data="downtime_id">
+hasResult()): ?>
+ = $this->translate('No downtimes found matching the filter.') ?>
+
+
+ "
+ data-icinga-multiselect-data="downtime_id">
peekAhead($this->compact) as $downtime):
- if (isset($downtime->service_description)) {
- $this->isService = true;
- $this->stateName = Service::getStateText($downtime->service_state);
- } else {
- $this->isService = false;
- $this->stateName = Host::getStateText($downtime->host_state);
- }
- $this->downtime = $downtime;
- ?>
-
- = $this->render('partials/downtime/downtime-header.phtml'); ?>
-
+ if (isset($downtime->service_description)) {
+ $this->isService = true;
+ $this->stateName = Service::getStateText($downtime->service_state);
+ } else {
+ $this->isService = false;
+ $this->stateName = Host::getStateText($downtime->host_state);
+ }
+ // Set downtime for partials
+ $this->downtime = $downtime;
+ ?>
+
+ = $this->render('partials/downtime/downtime-header.phtml') ?>
+
-
-hasResult()): ?>
- = $this->translate('No downtimes found matching the filter, maybe the downtime already expired.'); ?>
-hasMore()): ?>
- = $this->qlink(
- $this->translate('Show More'),
- $this->url()->without(array('view', 'limit')),
- null,
- array(
- 'data-base-target' => '_next',
- 'class' => 'pull-right action-link'
- )
- ); ?>
+
+hasMore()): ?>
+
+ = $this->qlink(
+ $this->translate('Show More'),
+ $this->url()->without(array('view', 'limit')),
+ null,
+ array(
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
+ )
+ ) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/eventgrid.phtml b/modules/monitoring/application/views/scripts/list/eventgrid.phtml
index 37024b841..aafa9b6b1 100644
--- a/modules/monitoring/application/views/scripts/list/eventgrid.phtml
+++ b/modules/monitoring/application/views/scripts/list/eventgrid.phtml
@@ -4,12 +4,8 @@ use Icinga\Web\Widget\Chart\HistoryColorGrid;
if (! $this->compact): ?>
- = $this->tabs; ?>
- = $this->sortBox; ?>
- = $this->limiter; ?>
- = $this->paginator; ?>
- = $this->filterEditor; ?>
- = $form; ?>
+ = $this->tabs ?>
+ = $this->form ?>
diff --git a/modules/monitoring/application/views/scripts/list/eventhistory.phtml b/modules/monitoring/application/views/scripts/list/eventhistory.phtml
index 32bb3d128..2ee788d60 100644
--- a/modules/monitoring/application/views/scripts/list/eventhistory.phtml
+++ b/modules/monitoring/application/views/scripts/list/eventhistory.phtml
@@ -3,11 +3,11 @@
if (! $this->compact): ?>
= $this->tabs ?>
-
- = $this->sortBox ?>
+
= $this->limiter ?>
- = $this->filterEditor ?>
+ = $this->sortBox ?>
+ = $this->filterEditor ?>
= $this->partial(
diff --git a/modules/monitoring/application/views/scripts/list/hostgroups.phtml b/modules/monitoring/application/views/scripts/list/hostgroups.phtml
index c39a80d33..2fbd9d4c5 100644
--- a/modules/monitoring/application/views/scripts/list/hostgroups.phtml
+++ b/modules/monitoring/application/views/scripts/list/hostgroups.phtml
@@ -1,278 +1,280 @@
compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
-hasResult()): ?>
+hasResult()): ?>
= $this->translate('No host groups found matching the filter.') ?>
-
-
-
- |
- = $this->translate('Host Group') ?> |
- = $this->translate('Host States') ?> |
- |
- = $this->translate('Service States') ?> |
-
-
-
-peekAhead($this->compact) as $hostgroup): ?>
-
-
- = $hostgroup->hosts_total ?>
- |
-
- = $this->qlink(
- $hostgroup->hostgroup_alias,
- 'monitoring/list/hosts',
- array('hostgroup_name' => $hostgroup->hostgroup_name),
- array('title' => sprintf(
- $this->translate('List all hosts in the group "%s"'),
- $hostgroup->hostgroup_alias
- ))
- ) ?>
- |
-
- setUrl('monitoring/list/hosts')
- ->setBaseFilter($this->filterEditor->getFilter())
- ->add(
- StateBadges::STATE_UP,
- $hostgroup->hosts_up,
- array(
- 'host_state' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state UP in the host group "%s"',
- 'List %u hosts which are currently in state UP in the host group "%s"',
- array($hostgroup->hosts_up, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_DOWN,
- $hostgroup->hosts_down_unhandled,
- array(
- 'host_state' => 1,
- 'host_acknowledged' => 0,
- 'host_in_downtime' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state DOWN in the host group "%s"',
- 'List %u hosts which are currently in state DOWN in the host group "%s"',
- array($hostgroup->hosts_down_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_DOWN_HANDLED,
- $hostgroup->hosts_down_handled,
- array(
- 'host_state' => 1,
- 'host_handled' => 1,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state DOWN (Acknowledged) in the host group "%s"',
- 'List %u hosts which are currently in state DOWN (Acknowledged) in the host group "%s"',
- array($hostgroup->hosts_down_handled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_UNREACHABLE,
- $hostgroup->hosts_unreachable_unhandled,
- array(
- 'host_state' => 2,
- 'host_acknowledged' => 0,
- 'host_in_downtime' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state UNREACHABLE in the host group "%s"',
- 'List %u hosts which are currently in state UNREACHABLE in the host group "%s"',
- array($hostgroup->hosts_unreachable_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_UNREACHABLE_HANDLED,
- $hostgroup->hosts_unreachable_handled,
- array(
- 'host_state' => 2,
- 'host_handled' => 1,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state UNREACHABLE (Acknowledged) in the host group "%s"',
- 'List %u hosts which are currently in state UNREACHABLE (Acknowledged) in the host group "%s"',
- array($hostgroup->hosts_unreachable_handled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_PENDING,
- $hostgroup->hosts_pending,
- array(
- 'host_state' => 99,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'host_severity'
- ),
- 'List %u host that is currently in state PENDING in the host group "%s"',
- 'List %u hosts which are currently in state PENDING in the host group "%s"',
- array($hostgroup->hosts_pending, $hostgroup->hostgroup_alias)
- );
- echo $stateBadges->render();
- ?>
- |
-
- = $this->qlink(
- $hostgroup->services_total,
- 'monitoring/list/services',
- array('hostgroup_name' => $hostgroup->hostgroup_name),
- array('title' => sprintf(
- $this->translate('List all services of all hosts in host group "%s"'),
- $hostgroup->hostgroup_alias
- ), 'class' => 'badge')
- ) ?>
- |
-
- setUrl('monitoring/list/services')
- ->add(
- StateBadges::STATE_OK,
- $hostgroup->services_ok,
- array(
- 'service_state' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state OK on hosts in the host group "%s"',
- 'List %u services which are currently in state OK on hosts in the host group "%s"',
- array($hostgroup->services_ok, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_CRITICAL,
- $hostgroup->services_critical_unhandled,
- array(
- 'service_state' => 2,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state CRITICAL on hosts in the host group "%s"',
- 'List %u services which are currently in state CRITICAL on hosts in the host group "%s"',
- array($hostgroup->services_critical_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_CRITICAL_HANDLED,
- $hostgroup->services_critical_handled,
- array(
- 'service_state' => 2,
- 'service_handled' => 1,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"',
- 'List %u services which are currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"',
- array($hostgroup->services_critical_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_UNKNOWN,
- $hostgroup->services_unknown_unhandled,
- array(
- 'service_state' => 3,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state UNKNOWN on hosts in the host group "%s"',
- 'List %u services which are currently in state UNKNOWN on hosts in the host group "%s"',
- array($hostgroup->services_unknown_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_UNKNOWN_HANDLED,
- $hostgroup->services_unknown_handled,
- array(
- 'service_state' => 3,
- 'service_handled' => 1,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"',
- 'List %u services which are currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"',
- array($hostgroup->services_unknown_handled, $hostgroup->hostgroup_alias)
+
+
+
+ |
+ = $this->translate('Host Group') ?> |
+ = $this->translate('Host States') ?> |
+ |
+ = $this->translate('Service States') ?> |
+
+
+
+ peekAhead($this->compact) as $hostGroup): ?>
+
+
+ = $hostGroup->hosts_total ?>
+ |
+
+ = $this->qlink(
+ $hostGroup->hostgroup_alias,
+ 'monitoring/list/hosts',
+ array('hostgroup_name' => $hostGroup->hostgroup_name),
+ array('title' => sprintf(
+ $this->translate('List all hosts in the group "%s"'),
+ $hostGroup->hostgroup_alias
+ ))
+ ) ?>
+ |
+
+ setUrl('monitoring/list/hosts')
+ ->setBaseFilter($this->filterEditor->getFilter())
+ ->add(
+ StateBadges::STATE_UP,
+ $hostGroup->hosts_up,
+ array(
+ 'host_state' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state UP in the host group "%s"',
+ 'List %u hosts which are currently in state UP in the host group "%s"',
+ array($hostGroup->hosts_up, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_DOWN,
+ $hostGroup->hosts_down_unhandled,
+ array(
+ 'host_state' => 1,
+ 'host_acknowledged' => 0,
+ 'host_in_downtime' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state DOWN in the host group "%s"',
+ 'List %u hosts which are currently in state DOWN in the host group "%s"',
+ array($hostGroup->hosts_down_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_DOWN_HANDLED,
+ $hostGroup->hosts_down_handled,
+ array(
+ 'host_state' => 1,
+ 'host_handled' => 1,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state DOWN (Acknowledged) in the host group "%s"',
+ 'List %u hosts which are currently in state DOWN (Acknowledged) in the host group "%s"',
+ array($hostGroup->hosts_down_handled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNREACHABLE,
+ $hostGroup->hosts_unreachable_unhandled,
+ array(
+ 'host_state' => 2,
+ 'host_acknowledged' => 0,
+ 'host_in_downtime' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state UNREACHABLE in the host group "%s"',
+ 'List %u hosts which are currently in state UNREACHABLE in the host group "%s"',
+ array($hostGroup->hosts_unreachable_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNREACHABLE_HANDLED,
+ $hostGroup->hosts_unreachable_handled,
+ array(
+ 'host_state' => 2,
+ 'host_handled' => 1,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state UNREACHABLE (Acknowledged) in the host group "%s"',
+ 'List %u hosts which are currently in state UNREACHABLE (Acknowledged) in the host group "%s"',
+ array($hostGroup->hosts_unreachable_handled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_PENDING,
+ $hostGroup->hosts_pending,
+ array(
+ 'host_state' => 99,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'host_severity'
+ ),
+ 'List %u host that is currently in state PENDING in the host group "%s"',
+ 'List %u hosts which are currently in state PENDING in the host group "%s"',
+ array($hostGroup->hosts_pending, $hostGroup->hostgroup_alias)
+ );
+ echo $stateBadges->render();
+ ?>
+ |
+
+ = $this->qlink(
+ $hostGroup->services_total,
+ 'monitoring/list/services',
+ array('hostgroup_name' => $hostGroup->hostgroup_name),
+ array('title' => sprintf(
+ $this->translate('List all services of all hosts in host group "%s"'),
+ $hostGroup->hostgroup_alias
+ ), 'class' => 'badge')
+ ) ?>
+ |
+
+ setUrl('monitoring/list/services')
+ ->add(
+ StateBadges::STATE_OK,
+ $hostGroup->services_ok,
+ array(
+ 'service_state' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state OK on hosts in the host group "%s"',
+ 'List %u services which are currently in state OK on hosts in the host group "%s"',
+ array($hostGroup->services_ok, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_CRITICAL,
+ $hostGroup->services_critical_unhandled,
+ array(
+ 'service_state' => 2,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state CRITICAL on hosts in the host group "%s"',
+ 'List %u services which are currently in state CRITICAL on hosts in the host group "%s"',
+ array($hostGroup->services_critical_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_CRITICAL_HANDLED,
+ $hostGroup->services_critical_handled,
+ array(
+ 'service_state' => 2,
+ 'service_handled' => 1,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"',
+ 'List %u services which are currently in state CRITICAL (Acknowledged) on hosts in the host group "%s"',
+ array($hostGroup->services_critical_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNKNOWN,
+ $hostGroup->services_unknown_unhandled,
+ array(
+ 'service_state' => 3,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state UNKNOWN on hosts in the host group "%s"',
+ 'List %u services which are currently in state UNKNOWN on hosts in the host group "%s"',
+ array($hostGroup->services_unknown_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNKNOWN_HANDLED,
+ $hostGroup->services_unknown_handled,
+ array(
+ 'service_state' => 3,
+ 'service_handled' => 1,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"',
+ 'List %u services which are currently in state UNKNOWN (Acknowledged) on hosts in the host group "%s"',
+ array($hostGroup->services_unknown_handled, $hostGroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_WARNING,
- $hostgroup->services_warning_unhandled,
- array(
- 'service_state' => 1,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state WARNING on hosts in the host group "%s"',
- 'List %u services which are currently in state WARNING on hosts in the host group "%s"',
- array($hostgroup->services_warning_unhandled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_WARNING_HANDLED,
- $hostgroup->services_warning_handled,
- array(
- 'service_state' => 1,
- 'service_handled' => 1,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state WARNING (Acknowledged) on hosts in the host group "%s"',
- 'List %u services which are currently in state WARNING (Acknowledged) on hosts in the host group "%s"',
- array($hostgroup->services_warning_handled, $hostgroup->hostgroup_alias)
- )
- ->add(
- StateBadges::STATE_PENDING,
- $hostgroup->services_pending,
- array(
- 'service_state' => 99,
- 'hostgroup_name' => $hostgroup->hostgroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %u service that is currently in state PENDING on hosts in the host group "%s"',
- 'List %u services which are currently in state PENDING on hosts in the host group "%s"',
- array($hostgroup->services_pending, $hostgroup->hostgroup_alias)
- );
- echo $stateBadges->render();
- ?>
- |
-
-
-
-
-hasMore()): ?>
-= $this->qlink(
- $this->translate('Show More'),
- $this->url()->without(array('view', 'limit')),
- null,
- array(
- 'data-base-target' => '_next',
- 'class' => 'pull-right action-link'
- )
-) ?>
+ )
+ ->add(
+ StateBadges::STATE_WARNING,
+ $hostGroup->services_warning_unhandled,
+ array(
+ 'service_state' => 1,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state WARNING on hosts in the host group "%s"',
+ 'List %u services which are currently in state WARNING on hosts in the host group "%s"',
+ array($hostGroup->services_warning_unhandled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_WARNING_HANDLED,
+ $hostGroup->services_warning_handled,
+ array(
+ 'service_state' => 1,
+ 'service_handled' => 1,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state WARNING (Acknowledged) on hosts in the host group "%s"',
+ 'List %u services which are currently in state WARNING (Acknowledged) on hosts in the host group "%s"',
+ array($hostGroup->services_warning_handled, $hostGroup->hostgroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_PENDING,
+ $hostGroup->services_pending,
+ array(
+ 'service_state' => 99,
+ 'hostgroup_name' => $hostGroup->hostgroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %u service that is currently in state PENDING on hosts in the host group "%s"',
+ 'List %u services which are currently in state PENDING on hosts in the host group "%s"',
+ array($hostGroup->services_pending, $hostGroup->hostgroup_alias)
+ );
+ echo $stateBadges->render();
+ ?>
+ |
+
+
+
+
+hasMore()): ?>
+
+ = $this->qlink(
+ $this->translate('Show More'),
+ $this->url()->without(array('view', 'limit')),
+ null,
+ array(
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
+ )
+ ) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml
index f5ff2569a..6de773435 100644
--- a/modules/monitoring/application/views/scripts/list/hosts.phtml
+++ b/modules/monitoring/application/views/scripts/list/hosts.phtml
@@ -4,14 +4,12 @@ use Icinga\Module\Monitoring\Object\Host;
if (! $this->compact): ?>
= $this->tabs ?>
-
- = $this->render('list/components/hostssummary.phtml') ?>
- = $this->render('list/components/selectioninfo.phtml') ?>
-
-
- = $this->sortBox ?>
+ = $this->render('list/components/hostssummary.phtml') ?>
+ = $this->render('list/components/selectioninfo.phtml') ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
@@ -19,7 +17,7 @@ if (! $this->compact): ?>
hasResult()): ?>
= $this->translate('No hosts found matching the filter.') ?>
-
+
hasMore()): ?>
-
+
= $this->qlink(
$this->translate('Show More'),
$this->url()->without(array('view', 'limit')),
null,
array(
- 'data-base-target' => '_next',
- 'class' => 'action-link'
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
)
) ?>
diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml
index 6428055cc..8316665d1 100644
--- a/modules/monitoring/application/views/scripts/list/notifications.phtml
+++ b/modules/monitoring/application/views/scripts/list/notifications.phtml
@@ -4,11 +4,11 @@ use Icinga\Module\Monitoring\Object\Service;
if (! $this->compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
@@ -77,7 +77,7 @@ if (! $this->compact): ?>
hasMore()): ?>
-
+
= $this->qlink(
$this->translate('Show More'),
$this->url(isset($notificationsUrl) ? $notificationsUrl : null)->without(array('view', 'limit')),
diff --git a/modules/monitoring/application/views/scripts/list/servicegroups.phtml b/modules/monitoring/application/views/scripts/list/servicegroups.phtml
index 6f39a90ff..1100d7b0d 100644
--- a/modules/monitoring/application/views/scripts/list/servicegroups.phtml
+++ b/modules/monitoring/application/views/scripts/list/servicegroups.phtml
@@ -2,173 +2,175 @@
if (! $this->compact): ?>
- = $tabs ?>
-
- = $this->sortBox ?>
+ = $this->tabs ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
-hasResult()): ?>
+hasResult()): ?>
= $this->translate('No service groups found matching the filter.') ?>
-
-
-
- |
- = $this->translate('Service Group') ?> |
- = $this->translate('Service States') ?> |
-
-
-
-peekAhead($this->compact) as $serviceGroup): ?>
-
-
- = $serviceGroup->services_total ?>
- |
-
- = $this->qlink(
- $serviceGroup->servicegroup_alias,
- 'monitoring/list/services',
- array('servicegroup_name' => $serviceGroup->servicegroup_name),
- array('title' => sprintf($this->translate('List all services in the group "%s"'), $serviceGroup->servicegroup_alias))
- ) ?>
- |
-
- setUrl('monitoring/list/services')
- ->setBaseFilter($this->filterEditor->getFilter())
- ->add(
- StateBadges::STATE_OK,
- $serviceGroup->services_ok,
- array(
- 'service_state' => 0,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state OK in service group "%s"',
- 'List %s services which are currently in state OK in service group "%s"',
- array($serviceGroup->services_ok, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_CRITICAL,
- $serviceGroup->services_critical_unhandled,
- array(
- 'service_state' => 2,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state CRITICAL in service group "%s"',
- 'List %s services which are currently in state CRITICAL in service group "%s"',
- array($serviceGroup->services_critical_unhandled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_CRITICAL_HANDLED,
- $serviceGroup->services_critical_handled,
- array(
- 'service_state' => 2,
- 'service_handled' => 1,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state CRITICAL (Acknowledged) in service group "%s"',
- 'List %s services which are currently in state CRITICAL (Acknowledged) in service group "%s"',
- array($serviceGroup->services_critical_unhandled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_UNKNOWN,
- $serviceGroup->services_unknown_unhandled,
- array(
- 'service_state' => 3,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state UNKNOWN in service group "%s"',
- 'List %s services which are currently in state UNKNOWN in service group "%s"',
- array($serviceGroup->services_unknown_unhandled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_UNKNOWN_HANDLED,
- $serviceGroup->services_unknown_handled,
- array(
- 'service_state' => 3,
- 'service_handled' => 1,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state UNKNOWN (Acknowledged) in service group "%s"',
- 'List %s services which are currently in state UNKNOWN (Acknowledged) in service group "%s"',
- array($serviceGroup->services_unknown_handled, $serviceGroup->servicegroup_alias)
+
+
+
+ |
+ = $this->translate('Service Group') ?> |
+ = $this->translate('Service States') ?> |
+
+
+
+ peekAhead($this->compact) as $serviceGroup): ?>
+
+
+ = $serviceGroup->services_total ?>
+ |
+
+ = $this->qlink(
+ $serviceGroup->servicegroup_alias,
+ 'monitoring/list/services',
+ array('servicegroup_name' => $serviceGroup->servicegroup_name),
+ array('title' => sprintf($this->translate('List all services in the group "%s"'), $serviceGroup->servicegroup_alias))
+ ) ?>
+ |
+
+ setUrl('monitoring/list/services')
+ ->setBaseFilter($this->filterEditor->getFilter())
+ ->add(
+ StateBadges::STATE_OK,
+ $serviceGroup->services_ok,
+ array(
+ 'service_state' => 0,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state OK in service group "%s"',
+ 'List %s services which are currently in state OK in service group "%s"',
+ array($serviceGroup->services_ok, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_CRITICAL,
+ $serviceGroup->services_critical_unhandled,
+ array(
+ 'service_state' => 2,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state CRITICAL in service group "%s"',
+ 'List %s services which are currently in state CRITICAL in service group "%s"',
+ array($serviceGroup->services_critical_unhandled, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_CRITICAL_HANDLED,
+ $serviceGroup->services_critical_handled,
+ array(
+ 'service_state' => 2,
+ 'service_handled' => 1,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state CRITICAL (Acknowledged) in service group "%s"',
+ 'List %s services which are currently in state CRITICAL (Acknowledged) in service group "%s"',
+ array($serviceGroup->services_critical_unhandled, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNKNOWN,
+ $serviceGroup->services_unknown_unhandled,
+ array(
+ 'service_state' => 3,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state UNKNOWN in service group "%s"',
+ 'List %s services which are currently in state UNKNOWN in service group "%s"',
+ array($serviceGroup->services_unknown_unhandled, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_UNKNOWN_HANDLED,
+ $serviceGroup->services_unknown_handled,
+ array(
+ 'service_state' => 3,
+ 'service_handled' => 1,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state UNKNOWN (Acknowledged) in service group "%s"',
+ 'List %s services which are currently in state UNKNOWN (Acknowledged) in service group "%s"',
+ array($serviceGroup->services_unknown_handled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_WARNING,
- $serviceGroup->services_warning_unhandled,
- array(
- 'service_state' => 1,
- 'service_acknowledged' => 0,
- 'service_in_downtime' => 0,
- 'host_problem' => 0,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state WARNING in service group "%s"',
- 'List %s services which are currently in state WARNING in service group "%s"',
- array($serviceGroup->services_warning_unhandled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_WARNING_HANDLED,
- $serviceGroup->services_warning_handled,
- array(
- 'service_state' => 1,
- 'service_handled' => 1,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currently in state WARNING (Acknowledged) in service group "%s"',
- 'List %s services which are currently in state WARNING (Acknowledged) in service group "%s"',
- array($serviceGroup->services_warning_handled, $serviceGroup->servicegroup_alias)
- )
- ->add(
- StateBadges::STATE_PENDING,
- $serviceGroup->services_pending,
- array(
- 'service_state' => 99,
- 'servicegroup_name' => $serviceGroup->servicegroup_name,
- 'sort' => 'service_severity'
- ),
- 'List %s service that is currenlty in state PENDING in service group "%s"',
- 'List %s services which are currently in state PENDING in service group "%s"',
- array($serviceGroup->services_pending, $serviceGroup->servicegroup_alias)
- );
- echo $stateBadges->render();
- ?>
- |
-
-
-
-
-hasMore()): ?>
-= $this->qlink(
- $this->translate('Show More'),
- $this->url()->without(array('view', 'limit')),
- null,
- array(
- 'data-base-target' => '_next',
- 'class' => 'pull-right action-link'
- )
-) ?>
+ )
+ ->add(
+ StateBadges::STATE_WARNING,
+ $serviceGroup->services_warning_unhandled,
+ array(
+ 'service_state' => 1,
+ 'service_acknowledged' => 0,
+ 'service_in_downtime' => 0,
+ 'host_problem' => 0,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state WARNING in service group "%s"',
+ 'List %s services which are currently in state WARNING in service group "%s"',
+ array($serviceGroup->services_warning_unhandled, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_WARNING_HANDLED,
+ $serviceGroup->services_warning_handled,
+ array(
+ 'service_state' => 1,
+ 'service_handled' => 1,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currently in state WARNING (Acknowledged) in service group "%s"',
+ 'List %s services which are currently in state WARNING (Acknowledged) in service group "%s"',
+ array($serviceGroup->services_warning_handled, $serviceGroup->servicegroup_alias)
+ )
+ ->add(
+ StateBadges::STATE_PENDING,
+ $serviceGroup->services_pending,
+ array(
+ 'service_state' => 99,
+ 'servicegroup_name' => $serviceGroup->servicegroup_name,
+ 'sort' => 'service_severity'
+ ),
+ 'List %s service that is currenlty in state PENDING in service group "%s"',
+ 'List %s services which are currently in state PENDING in service group "%s"',
+ array($serviceGroup->services_pending, $serviceGroup->servicegroup_alias)
+ );
+ echo $stateBadges->render();
+ ?>
+ |
+
+
+
+
+hasMore()): ?>
+
+ = $this->qlink(
+ $this->translate('Show More'),
+ $this->url()->without(array('view', 'limit')),
+ null,
+ array(
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
+ )
+ ) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml
index 211347f53..d8fd5d4b5 100644
--- a/modules/monitoring/application/views/scripts/list/services.phtml
+++ b/modules/monitoring/application/views/scripts/list/services.phtml
@@ -5,14 +5,12 @@ use Icinga\Module\Monitoring\Object\Service;
if (! $this->compact): ?>
= $this->tabs ?>
-
- = $this->render('list/components/servicesummary.phtml') ?>
- = $this->render('list/components/selectioninfo.phtml') ?>
-
-
- = $this->sortBox ?>
+ = $this->render('list/components/servicesummary.phtml') ?>
+ = $this->render('list/components/selectioninfo.phtml') ?>
+ = $this->paginator ?>
+
= $this->limiter ?>
- = $this->paginator ?>
+ = $this->sortBox ?>
= $this->filterEditor ?>
@@ -101,16 +99,16 @@ if (! $this->compact): ?>
hasMore()): ?>
-
- = $this->qlink(
- $this->translate('Show More'),
- $this->url()->without(array('view', 'limit')),
- null,
- array(
- 'data-base-target' => '_next',
- 'class' => 'action-link'
- )
- ) ?>
-
+
+ = $this->qlink(
+ $this->translate('Show More'),
+ $this->url()->without(array('view', 'limit')),
+ null,
+ array(
+ 'class' => 'action-link',
+ 'data-base-target' => '_next'
+ )
+ ) ?>
+
diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php
index f0afc0768..c861d1fc9 100644
--- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php
+++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php
@@ -16,12 +16,12 @@ class ContactgroupQuery extends IdoQuery
/**
* {@inheritdoc}
*/
- protected $groupBase = array('contactgroups' => array('cg.contactgroup_id', 'cgo.object_id'));
+ protected $groupBase = array('contactgroups' => array('cg.contactgroup_id'));
/**
* {@inheritdoc}
*/
- protected $groupOrigin = array('contacts', 'hosts', 'services');
+ protected $groupOrigin = array('hosts', 'members', 'services');
/**
* {@inheritdoc}
@@ -32,28 +32,8 @@ class ContactgroupQuery extends IdoQuery
'contactgroup_name' => 'cgo.name1',
'contactgroup_alias' => 'cg.alias COLLATE latin1_general_ci'
),
- 'contacts' => array(
- 'contact_id' => 'c.contact_id',
- 'contact' => 'co.name1 COLLATE latin1_general_ci',
- 'contact_name' => 'co.name1',
- 'contact_alias' => 'c.alias COLLATE latin1_general_ci',
- 'contact_email' => 'c.email_address COLLATE latin1_general_ci',
- 'contact_pager' => 'c.pager_address',
- 'contact_object_id' => 'c.contact_object_id',
- 'contact_has_host_notfications' => 'c.host_notifications_enabled',
- 'contact_has_service_notfications' => 'c.service_notifications_enabled',
- 'contact_can_submit_commands' => 'c.can_submit_commands',
- 'contact_notify_service_recovery' => 'c.notify_service_recovery',
- 'contact_notify_service_warning' => 'c.notify_service_warning',
- 'contact_notify_service_critical' => 'c.notify_service_critical',
- 'contact_notify_service_unknown' => 'c.notify_service_unknown',
- 'contact_notify_service_flapping' => 'c.notify_service_flapping',
- 'contact_notify_service_downtime' => 'c.notify_service_recovery',
- 'contact_notify_host_recovery' => 'c.notify_host_recovery',
- 'contact_notify_host_down' => 'c.notify_host_down',
- 'contact_notify_host_unreachable' => 'c.notify_host_unreachable',
- 'contact_notify_host_flapping' => 'c.notify_host_flapping',
- 'contact_notify_host_downtime' => 'c.notify_host_downtime'
+ 'members' => array(
+ 'contact_count' => 'COUNT(cgm.contactgroup_member_id)'
),
'hostgroups' => array(
'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci',
@@ -99,22 +79,18 @@ class ContactgroupQuery extends IdoQuery
}
/**
- * Join contacts
+ * Join contact group members
*/
- protected function joinContacts()
+ protected function joinMembers()
{
$this->select->joinLeft(
array('cgm' => $this->prefix . 'contactgroup_members'),
'cgm.contactgroup_id = cg.contactgroup_id',
array()
- )->joinLeft(
+ )->join(
array('co' => $this->prefix . 'objects'),
'co.object_id = cgm.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10',
array()
- )->joinLeft(
- array('c' => $this->prefix . 'contacts'),
- 'c.contact_object_id = co.object_id',
- array()
);
}
diff --git a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php
index cb12b0e2f..84eecd101 100644
--- a/modules/monitoring/library/Monitoring/DataView/Contactgroup.php
+++ b/modules/monitoring/library/Monitoring/DataView/Contactgroup.php
@@ -11,31 +11,9 @@ class Contactgroup extends DataView
public function getColumns()
{
return array(
- 'instance_name',
'contactgroup_name',
'contactgroup_alias',
- 'contact_object_id',
- 'contact_id',
- 'contact_name',
- 'contact_alias',
- 'contact_email',
- 'contact_pager',
- 'contact_has_host_notfications',
- 'contact_has_service_notfications',
- 'contact_can_submit_commands',
- 'contact_notify_service_recovery',
- 'contact_notify_service_warning',
- 'contact_notify_service_critical',
- 'contact_notify_service_unknown',
- 'contact_notify_service_flapping',
- 'contact_notify_service_downtime',
- 'contact_notify_host_recovery',
- 'contact_notify_host_down',
- 'contact_notify_host_unreachable',
- 'contact_notify_host_flapping',
- 'contact_notify_host_downtime',
- 'contact_notify_host_timeperiod',
- 'contact_notify_service_timeperiod'
+ 'contact_count'
);
}
@@ -60,9 +38,10 @@ class Contactgroup extends DataView
public function getStaticFilterColumns()
{
return array(
- 'contactgroup', 'contact',
+ 'contactgroup',
'host', 'host_name', 'host_display_name', 'host_alias',
'hostgroup', 'hostgroup_alias', 'hostgroup_name',
+ 'instance_name',
'service', 'service_description', 'service_display_name',
'servicegroup', 'servicegroup_alias', 'servicegroup_name'
);
diff --git a/modules/monitoring/library/Monitoring/DataView/Hoststatus.php b/modules/monitoring/library/Monitoring/DataView/Hoststatus.php
index 8484dd9cc..8f02b70ac 100644
--- a/modules/monitoring/library/Monitoring/DataView/Hoststatus.php
+++ b/modules/monitoring/library/Monitoring/DataView/Hoststatus.php
@@ -11,58 +11,56 @@ class HostStatus extends DataView
public function getColumns()
{
return array_merge($this->getHookedColumns(), array(
- 'instance_name',
- 'host_name',
- 'host_display_name',
- 'host_alias',
+ 'host_acknowledged',
+ 'host_acknowledgement_type',
+ 'host_action_url',
+ 'host_active_checks_enabled',
+ 'host_active_checks_enabled_changed',
'host_address',
'host_address6',
- 'host_state',
- 'host_hard_state',
- 'host_state_type',
- 'host_handled',
- 'host_unhandled',
- 'host_in_downtime',
- 'host_acknowledged',
- 'host_last_state_change',
- 'host_last_state_change',
- 'host_last_notification',
- 'host_last_check',
- 'host_next_check',
+ 'host_alias',
+ 'host_check_command',
'host_check_execution_time',
'host_check_latency',
- 'host_output',
- 'host_long_output',
- 'host_check_command',
- 'host_check_timeperiod',
- 'host_perfdata',
'host_check_source',
- 'host_passive_checks_enabled',
- 'host_passive_checks_enabled_changed',
- 'host_obsessing',
- 'host_obsessing_changed',
- 'host_notifications_enabled',
- 'host_notifications_enabled_changed',
+ 'host_check_timeperiod',
+ 'host_current_check_attempt',
+ 'host_current_notification_number',
+ 'host_display_name',
'host_event_handler_enabled',
'host_event_handler_enabled_changed',
'host_flap_detection_enabled',
'host_flap_detection_enabled_changed',
- 'host_active_checks_enabled',
- 'host_active_checks_enabled_changed',
- 'host_current_check_attempt',
- 'host_max_check_attempts',
- 'host_last_notification',
- 'host_current_notification_number',
- 'host_percent_state_change',
- 'host_is_flapping',
- 'host_action_url',
- 'host_notes_url',
- 'host_percent_state_change',
- 'host_modified_host_attributes',
- 'host_severity',
- 'host_problem',
+ 'host_handled',
+ 'host_hard_state',
+ 'host_in_downtime',
'host_ipv4',
- 'host_acknowledgement_type'
+ 'host_is_flapping',
+ 'host_is_reachable',
+ 'host_last_check',
+ 'host_last_notification',
+ 'host_last_state_change',
+ 'host_long_output',
+ 'host_max_check_attempts',
+ 'host_modified_host_attributes',
+ 'host_name',
+ 'host_next_check',
+ 'host_notes_url',
+ 'host_notifications_enabled',
+ 'host_notifications_enabled_changed',
+ 'host_obsessing',
+ 'host_obsessing_changed',
+ 'host_output',
+ 'host_passive_checks_enabled',
+ 'host_passive_checks_enabled_changed',
+ 'host_percent_state_change',
+ 'host_perfdata',
+ 'host_problem',
+ 'host_severity',
+ 'host_state',
+ 'host_state_type',
+ 'host_unhandled',
+ 'instance_name'
));
}
diff --git a/modules/monitoring/library/Monitoring/DataView/Servicestatus.php b/modules/monitoring/library/Monitoring/DataView/Servicestatus.php
index 1b03bd8ec..be6aab849 100644
--- a/modules/monitoring/library/Monitoring/DataView/Servicestatus.php
+++ b/modules/monitoring/library/Monitoring/DataView/Servicestatus.php
@@ -11,93 +11,91 @@ class ServiceStatus extends DataView
public function getColumns()
{
return array_merge($this->getHookedColumns(), array(
- 'instance_name',
- 'host_name',
- 'host_display_name',
- 'host_state',
- 'host_hard_state',
- 'host_state_type',
- 'host_last_state_change',
+ 'host_acknowledged',
+ 'host_action_url',
+ 'host_active_checks_enabled',
'host_address',
'host_address6',
- 'host_problem',
+ 'host_alias',
+ 'host_check_source',
+ 'host_display_name',
'host_handled',
- 'service_description',
- 'service_display_name',
- 'service_state',
- 'service_hard_state',
- 'service_in_downtime',
- 'service_acknowledged',
- 'service_handled',
- 'service_unhandled',
- 'service_output',
- 'service_last_state_change',
- 'service_long_output',
- 'service_is_flapping',
- 'service_state_type',
- 'service_severity',
- 'service_last_check',
- 'service_notifications_enabled',
- 'service_notifications_enabled_changed',
- 'service_action_url',
- 'service_notes',
- 'service_notes_url',
- 'service_last_check',
- 'service_next_check',
- 'service_attempt',
- 'service_last_notification',
- 'service_check_command',
- 'service_current_notification_number',
- 'host_acknowledged',
- 'host_output',
- 'host_long_output',
+ 'host_hard_state',
'host_in_downtime',
+ 'host_ipv4',
'host_is_flapping',
'host_last_check',
- 'host_notifications_enabled',
- 'host_unhandled_service_count',
- 'host_action_url',
- 'host_notes_url',
- 'host_display_name',
- 'host_alias',
- 'host_ipv4',
- 'host_severity',
- 'host_perfdata',
- 'host_check_source',
- 'host_active_checks_enabled',
- 'host_passive_checks_enabled',
'host_last_hard_state',
'host_last_hard_state_change',
- 'host_last_time_up',
+ 'host_last_state_change',
'host_last_time_down',
'host_last_time_unreachable',
+ 'host_last_time_up',
+ 'host_long_output',
'host_modified_host_attributes',
- 'service_hard_state',
- 'service_problem',
- 'service_perfdata',
- 'service_check_source',
- 'service_check_timeperiod',
+ 'host_name',
+ 'host_notes_url',
+ 'host_notifications_enabled',
+ 'host_output',
+ 'host_passive_checks_enabled',
+ 'host_perfdata',
+ 'host_problem',
+ 'host_severity',
+ 'host_state',
+ 'host_state_type',
+ 'host_unhandled_service_count',
+ 'instance_name',
+ 'service_acknowledged',
+ 'service_acknowledgement_type',
+ 'service_action_url',
'service_active_checks_enabled',
'service_active_checks_enabled_changed',
- 'service_passive_checks_enabled',
- 'service_passive_checks_enabled_changed',
- 'service_last_hard_state',
- 'service_last_hard_state_change',
- 'service_last_time_ok',
- 'service_last_time_warning',
- 'service_last_time_critical',
- 'service_last_time_unknown',
+ 'service_attempt',
+ 'service_check_command',
+ 'service_check_source',
+ 'service_check_timeperiod',
'service_current_check_attempt',
- 'service_max_check_attempts',
- 'service_obsessing',
- 'service_obsessing_changed',
+ 'service_current_notification_number',
+ 'service_description',
+ 'service_display_name',
'service_event_handler_enabled',
'service_event_handler_enabled_changed',
'service_flap_detection_enabled',
'service_flap_detection_enabled_changed',
- 'service_modified_service_attributes',
+ 'service_handled',
+ 'service_hard_state',
'service_host_name',
- 'service_acknowledgement_type',
+ 'service_in_downtime',
+ 'service_is_flapping',
+ 'service_is_reachable',
+ 'service_last_check',
+ 'service_last_hard_state',
+ 'service_last_hard_state_change',
+ 'service_last_notification',
+ 'service_last_state_change',
+ 'service_last_time_critical',
+ 'service_last_time_ok',
+ 'service_last_time_unknown',
+ 'service_last_time_warning',
+ 'service_long_output',
+ 'service_max_check_attempts',
+ 'service_modified_service_attributes',
+ 'service_next_check',
+ 'service_notes',
+ 'service_notes_url',
+ 'service_notifications_enabled',
+ 'service_notifications_enabled_changed',
+ 'service_obsessing',
+ 'service_obsessing_changed',
+ 'service_output',
+ 'service_passive_checks_enabled',
+ 'service_passive_checks_enabled_changed',
+ 'service_perfdata',
+ 'service_problem',
+ 'service_severity',
+ 'service_state',
+ 'service_state_type',
+ 'service_unhandled'
));
}
diff --git a/modules/monitoring/library/Monitoring/Web/Widget/StateBadges.php b/modules/monitoring/library/Monitoring/Web/Widget/StateBadges.php
index adf9344c3..04aa0261a 100644
--- a/modules/monitoring/library/Monitoring/Web/Widget/StateBadges.php
+++ b/modules/monitoring/library/Monitoring/Web/Widget/StateBadges.php
@@ -257,6 +257,7 @@ class StateBadges extends AbstractWidget
$groupItem = new NavigationItem(
uniqid(),
array(
+ 'cssClass' => 'state-badge-group',
'label' => '',
'priority' => $this->priority++
)
diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less
index c0c2fb023..af2458b8b 100644
--- a/modules/monitoring/public/css/module.less
+++ b/modules/monitoring/public/css/module.less
@@ -45,16 +45,27 @@
padding-right: 0;
}
}
-}
-// Multi-selection info
-.selection-info {
- float: right;
- font-size: @font-size-small;
- padding: @vertical-padding / 2 0;
+ .state-badge-group li {
+ margin-right: 1px;
+ }
- &:hover {
- cursor: help;
+ .state-badge-group li:last-child {
+ margin-right: 0;
+ }
+
+ .state-badge-group .badge {
+ border-radius: 0;
+ }
+
+ .state-badge-group li:first-child > .badge {
+ border-top-left-radius: 0.4em;
+ border-bottom-left-radius: 0.4em;
+ }
+
+ .state-badge-group li:last-child > .badge {
+ border-top-right-radius: 0.4em;
+ border-bottom-right-radius: 0.4em;
}
}
@@ -79,6 +90,11 @@
}
}
+.controls > .hosts-summary,
+.controls > .services-summary {
+ float: left;
+}
+
// State table in the host and service multi-selection and detail views
.host-detail-state,
.service-detail-state {
@@ -94,10 +110,6 @@
/* Generic box element */
-.boxview a {
- text-decoration: none;
-}
-
.boxview > div.box {
text-align: center;
vertical-align: top;
@@ -126,7 +138,6 @@
/* Any line of a box entry */
.boxview div.box.entry a {
display: block;
- color: inherit;
}
.boxview div.box.badge {
@@ -370,13 +381,7 @@ div.timeline {
a {
font-weight: bold;
- text-decoration: none;
white-space: nowrap;
-
- &:hover {
- text-decoration: underline;
-
- }
}
}
diff --git a/modules/monitoring/public/css/tables.less b/modules/monitoring/public/css/tables.less
index 276f9f130..5f0d4f49a 100644
--- a/modules/monitoring/public/css/tables.less
+++ b/modules/monitoring/public/css/tables.less
@@ -78,13 +78,32 @@
font-size: @font-size-small;
}
-// Plugin output in overviews
+// Plugin output in detail views
+.plugin-output,
+// Plugin output in overvies
+.overview-plugin-output {
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ -ms-hyphens: auto;
+ hyphens: auto;
+
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+}
+
+// Plugin output in overvies
.overview-plugin-output {
color: @text-color-light;
font-family: @font-family-fixed;
font-size: @font-size-small;
margin: 0;
white-space: pre-wrap;
+ // Long text in table cells overflows the table's width if the table's layout is not fixed.
+ // Thus overflow-wrap will not have any effect. But w/ the following we set a width of any value
+ // plus a min-width of 100% to consume the full width nonetheless which seems to always
+ // instruct browsers to not overflow the table. Ridiculous.
+ min-width: 100%;
+ width: 1em;
}
// Table for performance data in detail views
diff --git a/modules/translation/library/Translation/Util/GettextTranslationHelper.php b/modules/translation/library/Translation/Util/GettextTranslationHelper.php
index 49df57fb2..2178ca371 100644
--- a/modules/translation/library/Translation/Util/GettextTranslationHelper.php
+++ b/modules/translation/library/Translation/Util/GettextTranslationHelper.php
@@ -284,6 +284,7 @@ class GettextTranslationHelper
'--keyword=translatePlural:1,2',
'--keyword=translatePlural:1,2,4c',
'--keyword=mt:2',
+ '--keyword=mt:2,3c',
'--keyword=mtp:2,3',
'--keyword=mtp:2,3,5c',
'--keyword=t',
diff --git a/public/css/icinga/base.less b/public/css/icinga/base.less
index f3c3328fa..2491dad7a 100644
--- a/public/css/icinga/base.less
+++ b/public/css/icinga/base.less
@@ -44,7 +44,7 @@
@tr-hover-color: #F5FDFF;
// Font families
-@font-family: Calibri, Helvetica, sans-serif;
+@font-family: Calibri, "Lucida Grande", sans-serif;
@font-family-fixed: "Liberation Mono", "Lucida Console", Courier, monospace;
@font-family-wide: Tahoma, Verdana, sans-serif;
@@ -80,15 +80,19 @@ a {
color: inherit;
text-decoration: none;
- &:focus {
- outline-color: @icinga-blue;
- }
-
&:hover {
text-decoration: underline;
}
}
+a:focus,
+button:focus,
+input[type="checkbox"]:focus {
+ outline-color: @icinga-blue;
+ outline-style: solid; // IE
+ outline-style: auto;
+}
+
// Default margin for block text
blockquote, p, pre {
margin: 0 0 1em 0;
diff --git a/public/css/icinga/controls.less b/public/css/icinga/controls.less
index a4582a56d..554be4048 100644
--- a/public/css/icinga/controls.less
+++ b/public/css/icinga/controls.less
@@ -1,7 +1,49 @@
/*! Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+// TODO(el): Rename .filter to .filter-control
+
+// Hide auto sumbit info in controls but keep information for screen readers
+.controls .autosubmit-info {
+ .sr-only();
+}
+
+
+// Backend selection control in user and group list views
+.backend-selection {
+ float: left;
+
+ > .control-group {
+ padding: 0;
+
+ > .control-label-group {
+ text-align: left;
+ width: auto;
+ }
+ }
+}
+
+.backend-selection,
+.pagination-control,
+.selection-info,
+.sort-controls-container {
+ margin-bottom: 0.5em;
+}
+
+.backend-selection,
+.pagination-control,
+.sort-controls-container {
+ // Select controls may not respect font-size thus leading to improperly vertical alignment w/o big enough line-height
+ line-height: 2em;
+}
+
+.filter {
+ // Display filter control on a new line
+ clear: both;
+}
+
.limiter-control > .control-group {
padding: 0;
+ // Note that the sort-control form does not have padding as it's utilizing different decorators
> .control-label-group {
text-align: left;
@@ -18,7 +60,17 @@
}
}
+.limiter-control,
+.sort-control {
+ // Display limiter and sort control both on the same line; floating could be used here too to achieve the same
+ display: inline-block;
+}
+
.pagination-control {
+ // Display the pagination-control on a new line
+ clear: both;
+ float: left;
+
li {
&.active {
> a,
@@ -57,7 +109,19 @@
}
}
+// Multi-selection info
+.selection-info {
+ float: right;
+ font-size: @font-size-small;
+
+ &:hover {
+ cursor: help;
+ }
+}
+
.sort-control {
+ margin-left: 0.25em;
+
label {
width: auto;
margin-right: 0.5em;
@@ -72,10 +136,16 @@
width: 8em;
margin-left: 0;
}
+}
- > form > i {
- .sr-only();
- }
+.sort-controls-container {
+ clear: right;
+ float: right;
+}
+
+.sort-direction-control {
+ margin-left: 0.25em;
+ width: 1em;
}
html.no-js .sort-control form {
diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less
index 25fd24265..e4cf350c3 100644
--- a/public/css/icinga/forms.less
+++ b/public/css/icinga/forms.less
@@ -83,6 +83,10 @@ form.inline {
margin-left: 10em;
}
+.autosubmit-info {
+ margin-left: 0.5em;
+}
+
button:hover .icon-cancel {
color: @color-critical;
}
diff --git a/public/css/icinga/layout.less b/public/css/icinga/layout.less
index 9b155b9c1..ca090326e 100644
--- a/public/css/icinga/layout.less
+++ b/public/css/icinga/layout.less
@@ -76,7 +76,6 @@
@gutter: 1em;
// x-column-layout
-
#main {
.clearfix();
@@ -109,6 +108,22 @@
}
}
+// Mobile menu
+#mobile-menu-toggle {
+ text-align: right;
+
+ > button {
+ background: none;
+ border: none;
+ height: 2.5em;
+
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ }
+}
+
#sidebar {
background-color: @gray-lighter;
}
diff --git a/public/css/icinga/login.less b/public/css/icinga/login.less
index 9c9a28a98..9a91a87ef 100644
--- a/public/css/icinga/login.less
+++ b/public/css/icinga/login.less
@@ -3,8 +3,6 @@
// Login page styles
#login {
- font-size: 1em;
-
#icinga-logo {
.fadein();
}
diff --git a/public/css/icinga/main.less b/public/css/icinga/main.less
index 61373e772..8737c5c99 100644
--- a/public/css/icinga/main.less
+++ b/public/css/icinga/main.less
@@ -21,6 +21,7 @@
}
.icon-col {
+ text-align: center;
width: 1em;
}
@@ -68,6 +69,16 @@ a:hover > .icon-cancel {
.button-link {
.action-link();
+ .rounded-corners(3px);
+
+ background: @gray-lighter;
+ display: inline-block;
+ padding: 0.25em 0.5em;
+
+ &:hover {
+ background: @gray-lightest;
+ text-decoration: none;
+ }
}
// List styles
@@ -177,41 +188,6 @@ a:hover > .icon-cancel {
width: @name-value-table-name-width;
}
-// TODO(el): Fix
-.controls {
- .limiter-control {
- float: right;
- padding: @vertical-padding / 2 0;
- margin-bottom: 0.25em;
- }
-
- .pagination-control {
- padding: @vertical-padding / 2 0;
- margin-bottom: 0.25em;
- }
-
- .sort-control {
- float: right;
- padding: @vertical-padding / 2 0;
- margin-bottom: 0.25em;
- margin-left: 0.5em;
- }
-
- .sort-direction-control {
- margin-bottom: 0.5em;
- margin-left: 0.5em;
- width: 1em;
- }
-
- .filter {
- margin-bottom: 0.5em;
- }
-
- .selection-info {
- margin-left: 0.5em;
- }
-}
-
/* Styles for centering content of unknown width and height both horizontally and vertically
*
* Example markup:
diff --git a/public/css/icinga/mixins.less b/public/css/icinga/mixins.less
index 40d98f457..33c6fe022 100644
--- a/public/css/icinga/mixins.less
+++ b/public/css/icinga/mixins.less
@@ -56,9 +56,7 @@
}
.rounded-corners(@border-radius: 0.4em) {
- -webkit-border-radius: @border-radius;
- -moz-border-radius: @border-radius;
- border-radius: @border-radius;
+ border-radius: @border-radius;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
diff --git a/public/css/icinga/tabs.less b/public/css/icinga/tabs.less
index 4ab8ace23..d1cf11e74 100644
--- a/public/css/icinga/tabs.less
+++ b/public/css/icinga/tabs.less
@@ -29,10 +29,6 @@
> a {
color: @body-bg-color;
-
- &:hover {
- text-decoration: underline;
- }
}
&.active > a,
diff --git a/public/css/themes/high-contrast.less b/public/css/themes/high-contrast.less
index 299e1cce5..d5db9795c 100644
--- a/public/css/themes/high-contrast.less
+++ b/public/css/themes/high-contrast.less
@@ -59,3 +59,8 @@
#menu .active > a {
text-decoration: underline;
}
+
+.boxview a:focus {
+ color: @text-color;
+ text-decoration: underline;
+}
diff --git a/public/js/icinga/behavior/form.js b/public/js/icinga/behavior/form.js
index 063df5093..750f02223 100644
--- a/public/js/icinga/behavior/form.js
+++ b/public/js/icinga/behavior/form.js
@@ -55,6 +55,20 @@
* @returns {string|NULL} The content to be rendered, or NULL, when nothing should be changed
*/
Form.prototype.renderHook = function(content, $container, action, autorefresh) {
+ if ($container.attr('id') === 'menu') {
+ var $search = $container.find('#search');
+ if ($search[0] === document.activeElement) {
+ return null;
+ }
+ var search = $container.find('#search').val();
+ if (search.length) {
+ var $content = $('').append(content);
+ $content.find('#search').attr('value', search).addClass('active');
+ return $content.html();
+ }
+ return content;
+ }
+
var origFocus = document.activeElement;
var containerId = $container.attr('id');
var icinga = this.icinga;
diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js
index c1bc4c223..829ede335 100644
--- a/public/js/icinga/events.js
+++ b/public/js/icinga/events.js
@@ -421,7 +421,7 @@
var $target;
var formerUrl;
var remote = /^(?:[a-z]+:)\/\//;
- if (href.match(/^(mailto|javascript):/)) {
+ if (href.match(/^(mailto|javascript|data):/)) {
return true;
}
diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js
index 09876e954..af60a3ce9 100644
--- a/public/js/icinga/ui.js
+++ b/public/js/icinga/ui.js
@@ -100,9 +100,6 @@
var $oldLink = $(this);
if ($oldLink.hasAttr('type') && $oldLink.attr('type').indexOf('css') > -1) {
var base = location.protocol + '//' + location.host;
- if (location.port) {
- base += location.port;
- }
var url = icinga.utils.addUrlParams(
$(this).attr('href'),
{ id: new Date().getTime() }
@@ -587,7 +584,7 @@
});
if ($layout.hasClass('minimal-layout')) {
- if (! this.mobileMenu) {
+ if (! this.mobileMenu && $sidebar.length) {
$header.css({
top: $sidebar.outerHeight() + 'px'
});
@@ -599,19 +596,18 @@
zIndex: 2
});
$sidebar
+ .on(
+ 'click',
+ this.toggleMobileMenu
+ )
+ .prepend(
+ $('')
+ )
.css({
- paddingBottom: 32,
+ paddingBottom: $('#mobile-menu-toggle').height(),
top: 0,
zIndex: 3
- })
- .on('click', this.toggleMobileMenu)
- .prepend(
- $('').css({
- cursor: 'pointer',
- display: 'block',
- height: '32px'
- })
- );
+ });
$search.on('keypress', this.closeMobileMenu);
this.mobileMenu = true;
|