diff --git a/doc/authentication.md b/doc/authentication.md
index 604c85a2e..13a821441 100644
--- a/doc/authentication.md
+++ b/doc/authentication.md
@@ -107,7 +107,7 @@ Icinga Web 2 uses the MD5 based BSD password algorithm. For generating a passwor
command:
````
-openssl passwd -1 "password"
+openssl passwd -1 password
````
> Note: The switch to `openssl passwd` is the **number one** (`-1`) for using the MD5 based BSD password algorithm.
diff --git a/doc/installation.md b/doc/installation.md
index ef968974a..133f6cb98 100644
--- a/doc/installation.md
+++ b/doc/installation.md
@@ -3,21 +3,133 @@
The preferred way of installing Icinga Web 2 is to use the official package repositories depending on which operating
system and distribution you are running. But it is also possible to install Icinga Web 2 directly from source.
-## Installing Requirements
+## Installing Requirements
* A web server, e.g. Apache or nginx
-* PHP >= 5.3.0 w/ gettext and OpenSSL support
-* MySQL or PostgreSQL PHP libraries when using a database for authentication or storing user preferences into a database
+* PHP >= 5.3.0 w/ gettext, intl and OpenSSL support
+* MySQL or PostgreSQL PHP libraries when using a database for authentication or for storing preferences into a database
* LDAP PHP library when using Active Directory or LDAP for authentication
-* Icinga 1.x w/ Livestatus or IDO, Icinga 2 w/ Livestatus or IDO feature enabled
+* Icinga 1.x w/ Livestatus or IDO; Icinga 2.x w/ Livestatus or IDO feature enabled
+* MySQL or PostgreSQL PHP libraries when using IDO
-## Installing Icinga Web 2 from Package
+## Installing Icinga Web 2 from Package
-A guide on how to install Icinga Web 2 from package will follow shortly.
+Below is a list of official package repositories for installing Icinga Web 2 for various operating systems.
-## Installing Icinga Web 2 from Source
+Distribution | Repository
+------------------------|---------------------------
+Debian | [debmon](http://debmon.org/packages/debmon-wheezy/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/)
+Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/)
+RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/)
+openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/)
+SLES | [Icinga Repository](http://packages.icinga.org/SUSE/)
+Gentoo | -
+FreeBSD | -
+ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2)
-**Step 1: Getting the Source**
+Packages for distributions other than the ones listed above may also be available.
+Please contact your distribution packagers.
+
+### Setting up Package Repositories
+
+You need to add the Icinga repository to your package management configuration for installing Icinga Web 2.
+Below is a list with examples for various distributions.
+
+Debian (debmon):
+````
+wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add -
+echo 'deb http://debmon.org/debmon debmon-wheezy main' >/etc/apt/sources.list.d/debmon.list
+apt-get update
+````
+
+Ubuntu Trusty:
+````
+wget -O - http://packages.icinga.org/icinga.key | apt-key add -
+add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-trusty main'
+apt-get update
+````
+
+For other Ubuntu versions just replace trusty with your distribution's code name.
+
+RHEL and CentOS:
+````
+rpm --import http://packages.icinga.org/icinga.key
+curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/epel/ICINGA-release.repo
+yum makecache
+````
+
+Fedora:
+````
+rpm --import http://packages.icinga.org/icinga.key
+curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/fedora/ICINGA-release.repo
+yum makecache
+````
+
+SLES 11:
+````
+zypper ar http://packages.icinga.org/SUSE/ICINGA-release-11.repo
+zypper ref
+````
+
+SLES 12:
+````
+zypper ar http://packages.icinga.org/SUSE/ICINGA-release.repo
+zypper ref
+````
+
+openSUSE:
+````
+zypper ar http://packages.icinga.org/openSUSE/ICINGA-release.repo
+zypper ref
+````
+
+The packages for RHEL/CentOS depend on other packages which are distributed as part of the
+[EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following
+[these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F).
+
+### Installing Icinga Web 2
+
+You can install Icinga Web 2 by using your distribution's package manager to install the `icingaweb2` package.
+Below is a list with examples for various distributions.
+
+Debian and Ubuntu:
+````
+apt-get install icingaweb2
+````
+
+RHEL, CentOS and Fedora:
+````
+yum install icingaweb2
+````
+
+SLES and openSUSE:
+````
+zypper install icingaweb2
+````
+
+### Preparing Web Setup
+
+You can set up Icinga Web 2 quickly and easily with the Icinga Web 2 setup wizard which is available the first time
+you visit Icinga Web 2 in your browser. When using the web setup you are required to authenticate using a token.
+In order to generate a token use the `icingacli`:
+````
+icingacli setup token create
+````
+
+In case you do not remember the token you can show it using the `icingacli`:
+````
+icingacli setup token show
+````
+
+Finally visit Icinga Web 2 in your browser to access the setup wizard and complete the installation:
+`/icingaweb2/setup`.
+
+## Installing Icinga Web 2 from Source
+
+Although the preferred way of installing Icinga Web 2 is to use packages, it is also possible to install Icinga Web 2
+directly from source.
+
+### Getting the Source
First of all, you need to download the sources. Icinga Web 2 is available through a Git repository. You can clone this
repository either via git or http protocol using the following URLs:
@@ -33,7 +145,7 @@ This version also offers snapshots for easy download which you can use if you do
git clone git://git.icinga.org/icingaweb2.git
````
-**Step 2: Install the Source**
+### Installing Icinga Web 2
Choose a target directory and move Icinga Web 2 there.
@@ -41,7 +153,7 @@ Choose a target directory and move Icinga Web 2 there.
mv icingaweb2 /usr/share/icingaweb2
````
-**Step 3: Configuring the Web Server**
+### Configuring the Web Server
Use `icingacli` to generate web server configuration for either Apache or nginx.
@@ -57,13 +169,15 @@ nginx:
Save the output as new file in your webserver's configuration directory.
-Example for Apache on RHEL/CentOS:
+Example for Apache on RHEL or CentOS:
````
./bin/icingacli setup config webserver apache --document-root /usr/share/icingaweb2/public > /etc/httpd/conf.d/icingaweb2.conf
````
+### Preparing Web Setup
-**Step 4: Preparing Web Setup**
+You can set up Icinga Web 2 quickly and easily with the Icinga Web 2 setup wizard which is available the first time
+you visit Icinga Web 2 in your browser. Please follow the steps listed below for preparing the web setup.
Because both web and CLI must have access to configuration and logs, permissions will be managed using a special
system group. The web server user and CLI user have to be added to this system group.
@@ -102,6 +216,7 @@ Use `icingacli` to create the configuration directory which defaults to **/etc/i
./bin/icingacli setup config directory
````
+
When using the web setup you are required to authenticate using a token. In order to generate a token use the
`icingacli`:
````
@@ -113,11 +228,10 @@ In case you do not remember the token you can show it using the `icingacli`:
./bin/icingacli setup token show
````
-**Step 5: Web Setup**
+Finally visit Icinga Web 2 in your browser to access the setup wizard and complete the installation:
+`/icingaweb2/setup`.
-Visit Icinga Web 2 in your browser and complete installation using the web setup: /icingaweb2/setup
-
-## Upgrading to Icinga Web 2 Beta 2
+## 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
@@ -134,7 +248,7 @@ If you delegated authentication to your web server using the `autologin` backend
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`.
-## Upgrading to Icinga Web 2 Beta 3
+## 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.
diff --git a/library/Icinga/Application/Modules/Manager.php b/library/Icinga/Application/Modules/Manager.php
index 8f48d7b77..90e55f0b4 100644
--- a/library/Icinga/Application/Modules/Manager.php
+++ b/library/Icinga/Application/Modules/Manager.php
@@ -12,7 +12,6 @@ use Icinga\Exception\ConfigurationError;
use Icinga\Exception\SystemPermissionException;
use Icinga\Exception\ProgrammingError;
use Icinga\Exception\NotReadableError;
-use Icinga\Exception\NotFoundError;
/**
* Module manager that handles detecting, enabling and disabling of modules
@@ -199,7 +198,6 @@ class Manager
*
* @return $this
* @throws ConfigurationError When trying to enable a module that is not installed
- * @throws NotFoundError In case the "enabledModules" directory does not exist
* @throws SystemPermissionException When insufficient permissions for the application exist
*/
public function enableModule($name)
@@ -218,14 +216,15 @@ class Manager
if (! is_dir($this->enableDir) && !@mkdir($this->enableDir, 02770, true)) {
$error = error_get_last();
throw new SystemPermissionException(
- 'Failed to create enabledModule directory "%s" (%s)',
+ 'Failed to create enabledModules directory "%s" (%s)',
$this->enableDir,
$error['message']
);
} elseif (! is_writable($this->enableDir)) {
throw new SystemPermissionException(
- 'Cannot enable module "%s". Insufficient system permissions for enabling modules.',
- $name
+ 'Cannot enable module "%s". Check the permissions for the enabledModules directory: %s',
+ $name,
+ $this->enableDir
);
}
@@ -237,10 +236,11 @@ class Manager
$error = error_get_last();
if (strstr($error["message"], "File exists") === false) {
throw new SystemPermissionException(
- 'Could not enable module "%s" due to file system errors. '
+ 'Cannot enable module "%s" at %s due to file system errors. '
. 'Please check path and mounting points because this is not a permission error. '
. 'Primary error was: %s',
$name,
+ $this->enableDir,
$error['message']
);
}
@@ -259,32 +259,37 @@ class Manager
* @return $this
*
* @throws ConfigurationError When the module is not installed or it's not a symlink
- * @throws SystemPermissionException When the module can't be disabled
+ * @throws SystemPermissionException When insufficient permissions for the application exist
*/
public function disableModule($name)
{
if (! $this->hasEnabled($name)) {
return $this;
}
+
if (! is_writable($this->enableDir)) {
throw new SystemPermissionException(
- 'Could not disable module. Module path is not writable.'
+ 'Cannot disable module "%s". Check the permissions for the enabledModules directory: %s',
+ $name,
+ $this->enableDir
);
}
+
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
if (! file_exists($link)) {
throw new ConfigurationError(
- 'Could not disable module. The module %s was not found.',
+ 'Cannot disable module "%s". Module is not installed.',
$name
);
}
if (! is_link($link)) {
throw new ConfigurationError(
- 'Could not disable module. The module "%s" is not a symlink. '
+ 'Cannot disable module %s at %s. '
. 'It looks like you have installed this module manually and moved it to your module folder. '
. 'In order to dynamically enable and disable modules, you have to create a symlink to '
- . 'the enabled_modules folder.',
- $name
+ . 'the enabledModules folder.',
+ $name,
+ $this->enableDir
);
}
@@ -292,10 +297,11 @@ class Manager
if (! @unlink($link)) {
$error = error_get_last();
throw new SystemPermissionException(
- 'Could not disable module "%s" due to file system errors. '
+ 'Cannot enable module "%s" at %s due to file system errors. '
. 'Please check path and mounting points because this is not a permission error. '
. 'Primary error was: %s',
$name,
+ $this->enableDir,
$error['message']
);
}
diff --git a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php
index d1c9a677f..8ffd3ea00 100644
--- a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php
+++ b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php
@@ -127,7 +127,8 @@ class HistoryColorGrid extends AbstractWidget {
. ' opacity: ' . $this->opacity . ';" ' .
'aria-label="' . $entry['caption'] . '" ' .
'title="' . $entry['caption'] . '" ' .
- 'href="' . $entry['url'] . '"' .
+ 'href="' . $entry['url'] . '" ' .
+ 'data-tooltip-delay="0"' .
'>';
} else {
return '
- X
+
EOT;
diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php
index 068f85e1b..b9ab60065 100644
--- a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php
+++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php
@@ -17,29 +17,20 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm
public function init()
{
$this->addDescription(
- $this->translate(
- 'This command is used to send custom notifications for hosts or'
- . ' services.'
- )
+ $this->translate('This command is used to send custom notifications about hosts or services.')
);
}
/**
- * (non-PHPDoc)
- * @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
+ * {@inheritdoc}
*/
public function getSubmitLabel()
{
- return $this->translatePlural(
- 'Send custom notification',
- 'Send custom notifications',
- count($this->objects)
- );
+ return $this->translatePlural('Send custom notification', 'Send custom notifications', count($this->objects));
}
/**
- * (non-PHPDoc)
- * @see \Icinga\Web\Form::createElements() For the method documentation.
+ * {@inheritdoc}
*/
public function createElements(array $formData = array())
{
@@ -64,9 +55,8 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm
'label' => $this->translate('Forced'),
'value' => false,
'description' => $this->translate(
- 'If you check this option, a notification is sent'
- . 'regardless of the current time and whether'
- . ' notifications are enabled.'
+ 'If you check this option, the notification is sent out regardless of time restrictions and'
+ . ' whether or not notifications are enabled.'
)
)
),
@@ -77,8 +67,7 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm
'label' => $this->translate('Broadcast'),
'value' => false,
'description' => $this->translate(
- 'If you check this option, a notification is sent to'
- . ' all normal and escalated contacts.'
+ 'If you check this option, the notification is sent out to all normal and escalated contacts.'
)
)
)
@@ -87,24 +76,24 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm
}
/**
- * (non-PHPDoc)
- * @see \Icinga\Web\Form::onSuccess() For the method documentation.
+ * {@inheritdoc}
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
- $comment = new SendCustomNotificationCommand();
- $comment->setObject($object);
- $comment->setComment($this->getElement('comment')->getValue());
- $comment->setAuthor($this->request->getUser()->getUsername());
- $comment->setForced($this->getElement('forced')->isChecked());
- $comment->setBroadcast($this->getElement('broadcast')->isChecked());
- $this->getTransport($this->request)->send($comment);
+ $notification = new SendCustomNotificationCommand();
+ $notification
+ ->setObject($object)
+ ->setComment($this->getElement('comment')->getValue())
+ ->setAuthor($this->request->getUser()->getUsername())
+ ->setForced($this->getElement('forced')->isChecked())
+ ->setBroadcast($this->getElement('broadcast')->isChecked());
+ $this->getTransport($this->request)->send($notification);
}
Notification::success($this->translatePlural(
- 'Send custom notification..',
- 'Send custom notifications..',
+ 'Sending custom notification..',
+ 'Sending custom notifications..',
count($this->objects)
));
return true;
diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml
index ee752b037..53a00835a 100644
--- a/modules/monitoring/application/views/scripts/list/comments.phtml
+++ b/modules/monitoring/application/views/scripts/list/comments.phtml
@@ -34,10 +34,12 @@ if (count($comments) === 0) {
$icon = 'plug';
$title = $this->translate('Downtime');
$tooltip = $this->translate('Comment was caused by a downtime.');
+ break;
case 'ack':
$icon = 'ok';
$title = $this->translate('Acknowledgement');
$tooltip = $this->translate('Comment was caused by an acknowledgement.');
+ break;
}
?>
diff --git a/modules/monitoring/application/views/scripts/list/servicegrid.phtml b/modules/monitoring/application/views/scripts/list/servicegrid.phtml
index 6fb6db63e..069a60619 100644
--- a/modules/monitoring/application/views/scripts/list/servicegrid.phtml
+++ b/modules/monitoring/application/views/scripts/list/servicegrid.phtml
@@ -50,7 +50,8 @@ foreach ($serviceDescriptions as $service_description): ?>
'service_description' => $service_description
),
array(
- 'title' => sprintf($this->translate('List all services with the name "%s" on all reported hosts'), $service_description)
+ 'title' => sprintf($this->translate('List all services with the name "%s" on all reported hosts'), $service_description),
+ 'data-tooltip-gravity' => 's'
),
false
); ?>
diff --git a/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php
index ac5e4be61..192bae850 100644
--- a/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php
+++ b/modules/monitoring/library/Monitoring/Command/Object/ScheduleServiceCheckCommand.php
@@ -9,8 +9,7 @@ namespace Icinga\Module\Monitoring\Command\Object;
class ScheduleServiceCheckCommand extends ObjectCommand
{
/**
- * (non-PHPDoc)
- * @see \Icinga\Module\Monitoring\Command\Object\ObjectCommand::$allowedObjects For the property documentation.
+ * {@inheritdoc}
*/
protected $allowedObjects = array(
self::TYPE_SERVICE
@@ -74,7 +73,7 @@ class ScheduleServiceCheckCommand extends ObjectCommand
}
/**
- * Is the check forced?
+ * Get whether the check is forced
*
* @return bool
*/
@@ -84,8 +83,7 @@ class ScheduleServiceCheckCommand extends ObjectCommand
}
/**
- * (non-PHPDoc)
- * @see \Icinga\Module\Monitoring\Command\Object\IcingaCommand::getName() For the method documentation.
+ * {@inheritdoc}
*/
public function getName()
{
diff --git a/modules/monitoring/library/Monitoring/Command/Object/SendCustomNotificationCommand.php b/modules/monitoring/library/Monitoring/Command/Object/SendCustomNotificationCommand.php
index 83f054295..647804310 100644
--- a/modules/monitoring/library/Monitoring/Command/Object/SendCustomNotificationCommand.php
+++ b/modules/monitoring/library/Monitoring/Command/Object/SendCustomNotificationCommand.php
@@ -15,29 +15,27 @@ class SendCustomNotificationCommand extends WithCommentCommand
self::TYPE_HOST,
self::TYPE_SERVICE
);
-
+
/**
- * Whether a notification is forced to send
+ * Whether the notification is forced
*
- * Forced notifications are send regardless of time and if notifications
- * are enabled.
+ * Forced notifications are sent out regardless of time restrictions and whether or not notifications are enabled.
*
* @var bool
*/
protected $forced;
/**
- * Broadcast the notification
+ * Whether to broadcast the notification
*
- * If broadcast is true, the notification is send to all normal and
- * escalated contacts for the object
+ * Broadcast notifications are sent out to all normal and escalated contacts.
*
* @var bool
*/
protected $broadcast;
/**
- * Get notification force flag
+ * Get whether to force the notification
*
* @return bool
*/
@@ -47,17 +45,20 @@ class SendCustomNotificationCommand extends WithCommentCommand
}
/**
- * Set whether notification should be forced
+ * Set whether to force the notification
*
- * @param bool $forced
+ * @param bool $forced
+ *
+ * @return $this
*/
public function setForced($forced = true)
{
$this->forced = $forced;
+ return $this;
}
/**
- * Get notification broadcast flag
+ * Get whether to broadcast the notification
*
* @return bool
*/
@@ -67,12 +68,15 @@ class SendCustomNotificationCommand extends WithCommentCommand
}
/**
- * Set notification to broadcast
+ * Set whether to broadcast the notification
*
- * @param bool $broadcast
+ * @param bool $broadcast
+ *
+ * @return $this
*/
public function setBroadcast($broadcast = true)
{
$this->broadcast = $broadcast;
+ return $this;
}
}
diff --git a/public/js/icinga/behavior/tooltip.js b/public/js/icinga/behavior/tooltip.js
index c9a64257f..d3dc6c539 100644
--- a/public/js/icinga/behavior/tooltip.js
+++ b/public/js/icinga/behavior/tooltip.js
@@ -28,9 +28,19 @@
$el.attr('title', $el.data('title-rich') || $el.attr('title'));
});
$('svg .chart-data', el).tipsy({ gravity: 'se', html: true });
- $('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 });
- $('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 });
- $('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 });
+ $('i[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 });
+ $('[title]', el).each(function (i, el) {
+ var $el = $(el);
+ var delay = 500;
+ if ($el.data('tooltip-delay') !== undefined) {
+ delay = $el.data('tooltip-delay');
+ }
+ var gravity = $.fn.tipsy.autoNS;
+ if ($el.data('tooltip-gravity')) {
+ gravity = $el.data('tooltip-gravity');
+ }
+ $el.tipsy({ gravity: gravity, delayIn: delay });
+ });
// migrate or remove all orphaned tooltips
$('.tipsy').each(function () {
diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js
index 491ca82f2..7e87a8fb6 100644
--- a/public/js/icinga/events.js
+++ b/public/js/icinga/events.js
@@ -100,6 +100,7 @@
// Destroy Icinga, clean up and interrupt pending requests on unload
$( window ).on('unload', { self: this }, this.onUnload);
+ $( window ).on('beforeunload', { self: this }, this.onUnload);
// We catch scroll events in our containers
$('.container').on('scroll', { self: this }, this.icinga.events.onContainerScroll);
@@ -555,6 +556,7 @@
$(window).off('resize', this.onWindowResize);
$(window).off('load', this.onLoad);
$(window).off('unload', this.onUnload);
+ $(window).off('beforeunload', this.onUnload);
$(document).off('scroll', '.container', this.onContainerScroll);
$(document).off('click', 'a', this.linkClicked);
$(document).off('click', 'table.action tr[href]', this.rowSelected);
diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js
index e63125a4d..99667ac1c 100644
--- a/public/js/icinga/loader.js
+++ b/public/js/icinga/loader.js
@@ -640,7 +640,7 @@
this.icinga.logger.error(
req.status,
errorThrown + ':',
- $(req.responseText).text().slice(0, 100)
+ $(req.responseText).text().replace(/\s+/g, ' ').slice(0, 100)
);
this.renderContentToContainer(
req.responseText,