= $this->translate('Create New Authentication Backend'); ?>
- Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.
+ = $this->translate(
+ 'Create a new backend for authenticating your users. This backend will be added at the end of your authentication order.'
+ ); ?>
-
-= $this->form ?>
\ No newline at end of file
+= $form; ?>
\ No newline at end of file
diff --git a/application/views/scripts/config/authentication/modify.phtml b/application/views/scripts/config/authentication/modify.phtml
index ca2cef6f9..b01d7095a 100644
--- a/application/views/scripts/config/authentication/modify.phtml
+++ b/application/views/scripts/config/authentication/modify.phtml
@@ -1,18 +1,5 @@
-
-
-messageBox)): ?>
- = $this->messageBox->render() ?>
-
-
-= $this->form ?>
\ No newline at end of file
+
+ = $this->tabs->showOnlyCloseButton() ?>
+
+
= $this->translate('Remove Backend'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/application/views/scripts/config/authentication/reorder.phtml b/application/views/scripts/config/authentication/reorder.phtml
new file mode 100644
index 000000000..0ae7850eb
--- /dev/null
+++ b/application/views/scripts/config/authentication/reorder.phtml
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/application/views/scripts/config/modules.phtml b/application/views/scripts/config/modules.phtml
index 086d0a61a..e0204b24f 100644
--- a/application/views/scripts/config/modules.phtml
+++ b/application/views/scripts/config/modules.phtml
@@ -1,6 +1,6 @@
= $this->tabs ?>
-
Installed Modules
+
= $this->translate('Installed Modules') ?>
= $this->paginationControl($modules) ?>
@@ -11,9 +11,9 @@
enabled): ?>
- = $this->icon('success.png', 'Module is enabled') ?>
+ = $this->icon('success.png', $this->translate('Module is enabled')) ?>
- = $this->icon('remove.png', 'Module is disabled') ?>
+ = $this->icon('remove.png', $this->translate('Module is disabled')) ?>
endif ?>
-= $this->tabs ?>
+ = $tabs; ?>
-
\ No newline at end of file
diff --git a/application/views/scripts/config/resource/create.phtml b/application/views/scripts/config/resource/create.phtml
index f82354557..0750a5aa9 100644
--- a/application/views/scripts/config/resource/create.phtml
+++ b/application/views/scripts/config/resource/create.phtml
@@ -1,14 +1,6 @@
-
- Your = $this->escape($this->file); ?> configuration couldn't be stored (error: "= $this->exceptionMessage; ?>").
- This could have one or more of the following reasons:
-
-
-
You don't have file-system permissions to write to the = $this->escape($this->file); ?>.ini file
-
Something went wrong while writing the file
-
There's an application error preventing you from persisting the configuration
-
-
-
-
- Details can be seen in your application log (if you don't have access to this file, call your administrator in this case).
-
- In case you can access the configuration file (config/= $this->escape($this->file); ?>.ini) by yourself, you can open it and
- insert the config manually:
-
-
\ No newline at end of file
diff --git a/application/views/scripts/error/error.phtml b/application/views/scripts/error/error.phtml
index e3a71f829..2a900651b 100644
--- a/application/views/scripts/error/error.phtml
+++ b/application/views/scripts/error/error.phtml
@@ -11,7 +11,4 @@
+ = sprintf(
+ $this->translate('The file %s couldn\'t be stored. (Error: "%s")'),
+ $this->escape($filePath),
+ $this->escape($errorMessage)
+ ); ?>
+
+ = $this->translate('This could have one or more of the following reasons:'); ?>
+
+
+
= $this->translate('You don\'t have file-system permissions to write to the file'); ?>
+
= $this->translate('Something went wrong while writing the file'); ?>
+
= $this->translate('There\'s an application error preventing you from persisting the configuration'); ?>
+
+
+
+ = $this->translate('Details can be found in the application log. (If you don\'t have access to this log, call your administrator in this case)'); ?>
+
+ = $this->translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?>
+
+
+
+ = $this->escape($configString); ?>
+
+
\ No newline at end of file
diff --git a/config/authentication.ini.in b/config/authentication.ini.in
index 71d9e4402..2a2d2a969 100644
--- a/config/authentication.ini.in
+++ b/config/authentication.ini.in
@@ -18,6 +18,10 @@ backend = ldap
resource = internal_ldap
user_class = @ldap_user_objectclass@
user_name_attribute = @ldap_attribute_username@
+group_base_dn = @ldap_group_base_dn@
+group_attribute = @ldap_group_attribute@
+group_member_attribute = @ldap_group_member_attribute@
+group_class = @ldap_group_class@
[internal_db_authentication]
@internal_auth_disabled@
diff --git a/icingaweb2.spec b/icingaweb2.spec
index 5d5b9ec9c..db0929f7b 100644
--- a/icingaweb2.spec
+++ b/icingaweb2.spec
@@ -26,12 +26,12 @@
%define revision 1
-%define configdir %{_sysconfdir}/icingaweb
-%define sharedir %{_datadir}/icingaweb
-%define prefixdir %{_datadir}/icingaweb
-%define logdir %{sharedir}/log
+%define configdir %{_sysconfdir}/%{name}
+%define sharedir %{_datadir}/%{name}
+%define prefixdir %{_datadir}/%{name}
%define usermodparam -a -G
-%define logdir %{_localstatedir}/log/icingaweb
+%define logdir %{_localstatedir}/log/%{name}
+%define docdir %{sharedir}/log
%if "%{_vendor}" == "suse"
%define phpname php5
@@ -172,25 +172,26 @@ install -D -m0644 packages/rpm/etc/httpd/conf.d/icingaweb.conf %{buildroot}/%{ap
# install public, library, modules
%{__mkdir} -p %{buildroot}/%{sharedir}
%{__mkdir} -p %{buildroot}/%{logdir}
-%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb
+%{__mkdir} -p %{buildroot}/%{docdir}
+%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}
%{__mkdir} -p %{buildroot}/%{_sysconfdir}/dashboard
-%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules
-%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring
-%{__mkdir} -p %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules
+%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules
+%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring
+%{__mkdir} -p %{buildroot}/%{_sysconfdir}/%{name}/enabledModules
-%{__cp} -r application library modules public %{buildroot}/%{sharedir}/
+%{__cp} -r application doc library modules public %{buildroot}/%{sharedir}/
## config
# authentication is db only
-install -D -m0644 packages/rpm/etc/icingaweb/authentication.ini %{buildroot}/%{_sysconfdir}/icingaweb/authentication.ini
+install -D -m0644 packages/rpm/etc/%{name}/authentication.ini %{buildroot}/%{_sysconfdir}/%{name}/authentication.ini
# custom resource paths
-install -D -m0644 packages/rpm/etc/icingaweb/resources.ini %{buildroot}/%{_sysconfdir}/icingaweb/resources.ini
+install -D -m0644 packages/rpm/etc/%{name}/resources.ini %{buildroot}/%{_sysconfdir}/%{name}/resources.ini
# monitoring module (icinga2)
-install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/backends.ini
-install -D -m0644 packages/rpm/etc/icingaweb/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/icingaweb/modules/monitoring/instances.ini
+install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/backends.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/backends.ini
+install -D -m0644 packages/rpm/etc/%{name}/modules/monitoring/instances.ini %{buildroot}/%{_sysconfdir}/%{name}/modules/monitoring/instances.ini
# enable the monitoring module by default
-ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/icingaweb/enabledModules/monitoring
+ln -s %{sharedir}/modules/monitoring %{buildroot}/%{_sysconfdir}/%{name}/enabledModules/monitoring
## config
# install icingacli
@@ -228,6 +229,8 @@ fi
%config(noreplace) %attr(-,%{apacheuser},%{apachegroup}) %{configdir}
# logs
%attr(2775,%{apacheuser},%{apachegroup}) %dir %{logdir}
+# shipped docs
+%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/doc
%files -n php-Icinga
%attr(755,%{apacheuser},%{apachegroup}) %{sharedir}/application
diff --git a/library/Icinga/Application/ApplicationBootstrap.php b/library/Icinga/Application/ApplicationBootstrap.php
index 7736f8cf9..958642940 100644
--- a/library/Icinga/Application/ApplicationBootstrap.php
+++ b/library/Icinga/Application/ApplicationBootstrap.php
@@ -4,10 +4,10 @@
namespace Icinga\Application;
+use ErrorException;
use Exception;
use Zend_Config;
use Icinga\Application\Modules\Manager as ModuleManager;
-use Icinga\Application\Config;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
@@ -348,11 +348,7 @@ abstract class ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
- 'enable' => true,
- 'level' => Logger::$ERROR,
- 'type' => 'syslog',
- 'facility' => 'LOG_USER',
- 'application' => 'icingaweb'
+ 'log' => 'syslog'
)
)
);
@@ -383,9 +379,20 @@ abstract class ApplicationBootstrap
*/
protected function setupErrorHandling()
{
- error_reporting(E_ALL | E_NOTICE);
+ error_reporting(E_ALL | E_STRICT);
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
+ set_error_handler(function ($errno, $errstr, $errfile, $errline) {
+ if (error_reporting() === 0) {
+ // Error was suppressed with the @-operator
+ return false; // Continue with the normal error handler
+ }
+ switch($errno) {
+ case E_STRICT:
+ throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
+ }
+ return false; // Continue with the normal error handler
+ });
return $this;
}
diff --git a/library/Icinga/Application/Cli.php b/library/Icinga/Application/Cli.php
index 58daf5f2f..d329ce439 100644
--- a/library/Icinga/Application/Cli.php
+++ b/library/Icinga/Application/Cli.php
@@ -51,10 +51,9 @@ class Cli extends ApplicationBootstrap
Logger::create(
new Zend_Config(
array(
- 'enable' => true,
- 'level' => Logger::$INFO,
- 'type' => 'file',
- 'target' => 'php://stderr'
+ 'level' => Logger::INFO,
+ 'log' => 'file',
+ 'file' => 'php://stderr'
)
)
);
diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php
index f30684883..c7998a0bb 100644
--- a/library/Icinga/Application/Config.php
+++ b/library/Icinga/Application/Config.php
@@ -7,7 +7,6 @@ namespace Icinga\Application;
use Zend_Config;
use Zend_Config_Ini;
use Icinga\Exception\NotReadableError;
-use Icinga\Exception\ProgrammingError;
/**
* Global registry of application and module configuration.
@@ -22,11 +21,11 @@ class Config extends Zend_Config
public static $configDir;
/**
- * The INI file this configuration has been loaded from
+ * The INI file this configuration has been loaded from or should be written to
*
* @var string
*/
- private $configFile;
+ protected $configFile;
/**
* Application config instances per file
@@ -42,30 +41,28 @@ class Config extends Zend_Config
*/
protected static $modules = array();
- private $instance;
-
/**
- * Load configuration from the config file $filename
+ * Load configuration from the given INI file
+ *
+ * @param string $file The file to parse
*
- * @param string $filename The filename to parse
-
* @throws NotReadableError When the file does not exist or cannot be read
*/
- public function __construct($filename)
+ public static function fromIni($file)
{
- parent::__construct(array(), true);
- $filepath = realpath($filename);
+ $config = new static(array(), true);
+ $filepath = realpath($file);
+
if ($filepath === false) {
- $this->configFile = $filename;
+ $config->setConfigFile($file);
} elseif (is_readable($filepath)) {
- $this->configFile = $filepath;
- $this->merge(new Zend_Config_Ini($filepath));
+ $config->setConfigFile($filepath);
+ $config->merge(new Zend_Config_Ini($filepath));
} else {
- throw new NotReadableError(
- 'Cannot read config file "%s". Permission denied',
- $filename
- );
+ throw new NotReadableError('Cannot read config file "%s". Permission denied', $filepath);
}
+
+ return $config;
}
/**
@@ -80,7 +77,7 @@ class Config extends Zend_Config
public static function app($configname = 'config', $fromDisk = false)
{
if (!isset(self::$app[$configname]) || $fromDisk) {
- self::$app[$configname] = new Config(self::resolvePath($configname . '.ini'));
+ self::$app[$configname] = Config::fromIni(self::resolvePath($configname . '.ini'));
}
return self::$app[$configname];
}
@@ -113,7 +110,7 @@ class Config extends Zend_Config
}
$moduleConfigs = self::$modules[$modulename];
if (!isset($moduleConfigs[$configname]) || $fromDisk) {
- $moduleConfigs[$configname] = new Config(
+ $moduleConfigs[$configname] = Config::fromIni(
self::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
);
}
@@ -138,15 +135,28 @@ class Config extends Zend_Config
}
/**
- * Return the application wide config file
+ * Return this config's file path
*
- * @return string
+ * @return string
*/
public function getConfigFile()
{
return $this->configFile;
}
+ /**
+ * Set this config's file path
+ *
+ * @param string $filepath The path to the config file
+ *
+ * @return self
+ */
+ public function setConfigFile($filepath)
+ {
+ $this->configFile = $filepath;
+ return $this;
+ }
+
/**
* Prepend configuration base dir if input is relative
*
diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php
index 6d66412e6..b1b051a6c 100644
--- a/library/Icinga/Application/Modules/Module.php
+++ b/library/Icinga/Application/Modules/Module.php
@@ -918,10 +918,21 @@ class Module
* Translate a string with the global mt()
*
* @param $string
+ * @param null $context
+ *
* @return mixed|string
*/
- protected function translate($string)
+ protected function translate($string, $context = null)
{
- return mt($this->name, $string);
+ return mt($this->name, $string, $context);
+ }
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translatePlural() For the function documentation.
+ */
+ protected function translatePlural($textSingular, $textPlural, $number, $context = null)
+ {
+ return mtp($this->name, $textSingular, $textPlural, $number, $context);
}
}
diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php
index 6a15d392a..89cad22ab 100644
--- a/library/Icinga/Application/Web.php
+++ b/library/Icinga/Application/Web.php
@@ -278,7 +278,8 @@ class Web extends ApplicationBootstrap
if ($this->user !== null && $this->user->getPreferences() !== null) {
$detect = new TimezoneDetect();
- $userTimezone = $this->user->getPreferences()->get('app.timezone', $detect->getTimezoneName());
+ $userTimezone = $this->user->getPreferences()
+ ->getValue('icingaweb', 'timezone', $detect->getTimezoneName());
}
try {
@@ -302,7 +303,7 @@ class Web extends ApplicationBootstrap
{
parent::setupInternationalization();
if ($this->user !== null && $this->user->getPreferences() !== null
- && (($locale = $this->user->getPreferences()->get('app.language')) !== null)
+ && (($locale = $this->user->getPreferences()->getValue('icingaweb', 'language')) !== null)
) {
try {
Translator::setupLocale($locale);
diff --git a/library/Icinga/Application/functions.php b/library/Icinga/Application/functions.php
index 422dfeab7..304c75093 100644
--- a/library/Icinga/Application/functions.php
+++ b/library/Icinga/Application/functions.php
@@ -5,23 +5,85 @@
use Icinga\Util\Translator;
if (extension_loaded('gettext')) {
- function t($messageId)
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translate() For the function documentation.
+ */
+ function t($messageId, $context = null)
{
- return Translator::translate($messageId, Translator::DEFAULT_DOMAIN);
+ return Translator::translate($messageId, Translator::DEFAULT_DOMAIN, $context);
}
- function mt($domain, $messageId)
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translate() For the function documentation.
+ */
+ function mt($domain, $messageId, $context = null)
{
- return Translator::translate($messageId, $domain);
+ return Translator::translate($messageId, $domain, $context);
}
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translatePlural() For the function documentation.
+ */
+ function tp($messageId, $messageId2, $number, $context = null)
+ {
+ return Translator::translatePlural($messageId, $messageId2, $number, Translator::DEFAULT_DOMAIN, $context);
+ }
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translatePlural() For the function documentation.
+ */
+ function mtp($domain, $messageId, $messageId2, $number, $context = null)
+ {
+ return Translator::translatePlural($messageId, $messageId2, $number, $domain, $context);
+ }
+
} else {
- function t($messageId)
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translate() For the function documentation.
+ */
+ function t($messageId, $context = null)
{
return $messageId;
}
- function mt($domain, $messageId)
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translate() For the function documentation.
+ */
+ function mt($domain, $messageId, $context = null)
{
return $messageId;
}
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translatePlural() For the function documentation.
+ */
+ function tp($messageId, $messageId2, $number, $context = null)
+ {
+ if ((int) $number !== 1) {
+ return $messageId2;
+ }
+ return $messageId;
+ }
+
+ /**
+ * (non-PHPDoc)
+ * @see Translator::translatePlural() For the function documentation.
+ */
+ function mtp($domain, $messageId, $messageId2, $number, $context = null)
+ {
+ if ((int) $number !== 1) {
+ return $messageId2;
+ }
+ return $messageId;
+ }
+
}
diff --git a/library/Icinga/Authentication/AdmissionLoader.php b/library/Icinga/Authentication/AdmissionLoader.php
index f961fb87c..098afda81 100644
--- a/library/Icinga/Authentication/AdmissionLoader.php
+++ b/library/Icinga/Authentication/AdmissionLoader.php
@@ -52,12 +52,8 @@ class AdmissionLoader
return $permissions;
}
foreach ($config as $section) {
- if ($this->match($section, $username, $groups)) {
- foreach ($section as $key => $value) {
- if (strpos($key, 'permission') === 0) {
- $permissions = array_merge($permissions, String::trimSplit($value));
- }
- }
+ if ($this->match($section, $username, $groups) && isset($section->permissions)) {
+ $permissions += String::trimSplit($section->permissions);
}
}
return $permissions;
@@ -79,12 +75,12 @@ class AdmissionLoader
} catch (NotReadableError $e) {
return $restrictions;
}
- foreach ($config as $section) {
+ foreach ($config as $name => $section) {
if ($this->match($section, $username, $groups)) {
if (!array_key_exists($section->name, $restrictions)) {
$restrictions[$section->name] = array();
}
- $restrictions[$section->name][] = $section->restriction;
+ $restrictions[$section->name][$name] = $section->restriction;
}
}
return $restrictions;
diff --git a/library/Icinga/Authentication/Backend/LdapUserBackend.php b/library/Icinga/Authentication/Backend/LdapUserBackend.php
index 23a79781d..8ef6c89de 100644
--- a/library/Icinga/Authentication/Backend/LdapUserBackend.php
+++ b/library/Icinga/Authentication/Backend/LdapUserBackend.php
@@ -4,6 +4,7 @@
namespace Icinga\Authentication\Backend;
+use Icinga\Logger\Logger;
use Icinga\User;
use Icinga\Authentication\UserBackend;
use Icinga\Protocol\Ldap\Connection;
@@ -23,11 +24,14 @@ class LdapUserBackend extends UserBackend
protected $userNameAttribute;
- public function __construct(Connection $conn, $userClass, $userNameAttribute)
+ protected $groupOptions;
+
+ public function __construct(Connection $conn, $userClass, $userNameAttribute, $groupOptions = null)
{
$this->conn = $conn;
$this->userClass = $userClass;
$this->userNameAttribute = $userNameAttribute;
+ $this->groupOptions = $groupOptions;
}
/**
@@ -83,6 +87,43 @@ class LdapUserBackend extends UserBackend
}
}
+ /**
+ * Retrieve the user groups
+ *
+ * @TODO: Subject to change, see #7343
+ *
+ * @param string $dn
+ *
+ * @return array|null
+ */
+ public function getGroups($dn)
+ {
+ if (empty($this->groupOptions) || ! isset($this->groupOptions['group_base_dn'])) {
+ return null;
+ }
+
+ $q = $this->conn->select()
+ ->setBase($this->groupOptions['group_base_dn'])
+ ->from(
+ $this->groupOptions['group_class'],
+ array($this->groupOptions['group_attribute'])
+ )
+ ->where(
+ $this->groupOptions['group_member_attribute'],
+ $dn
+ );
+
+ $result = $this->conn->fetchAll($q);
+
+ $groups = array();
+
+ foreach ($result as $group) {
+ $groups[] = $group->{$this->groupOptions['group_attribute']};
+ }
+
+ return $groups;
+ }
+
/**
* Test whether the given user exists
*
@@ -127,10 +168,15 @@ class LdapUserBackend extends UserBackend
return false;
}
try {
- return $this->conn->testCredentials(
- $this->conn->fetchDN($this->createQuery($user->getUsername())),
+ $userDn = $this->conn->fetchDN($this->createQuery($user->getUsername()));
+ $authenticated = $this->conn->testCredentials(
+ $userDn,
$password
);
+ if ($authenticated) {
+ $user->setGroups($this->getGroups($userDn));
+ }
+ return $authenticated;
} catch (LdapException $e) {
// Error during authentication of this specific user
throw new AuthenticationException(
@@ -160,4 +206,3 @@ class LdapUserBackend extends UserBackend
);
}
}
-
diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php
index 75cf81481..efe9b5815 100644
--- a/library/Icinga/Authentication/Manager.php
+++ b/library/Icinga/Authentication/Manager.php
@@ -113,30 +113,32 @@ class Manager
}
/**
- * Tries to authenticate the user with the current session
+ * Try to authenticate the user with the current session
+ *
+ * Authentication for externally-authenticated users will be revoked if the username changed or external
+ * authentication is no longer in effect
*/
public function authenticateFromSession()
{
$this->user = Session::getSession()->get('user');
-
if ($this->user !== null && $this->user->isRemoteUser() === true) {
list($originUsername, $field) = $this->user->getRemoteUserInformation();
- if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) {
+ if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization();
}
}
}
/**
- * Returns true when the user is currently authenticated
+ * Whether the user is authenticated
*
- * @param Boolean $ignoreSession Set to true to prevent authentication by session
+ * @param bool $ignoreSession True to prevent session authentication
*
* @return bool
*/
public function isAuthenticated($ignoreSession = false)
{
- if ($this->user === null && !$ignoreSession) {
+ if ($this->user === null && ! $ignoreSession) {
$this->authenticateFromSession();
}
return is_object($this->user);
@@ -145,25 +147,16 @@ class Manager
/**
* Whether an authenticated user has a given permission
*
- * This is true if the user owns this permission, false if not.
- * Also false if there is no authenticated user
- *
- * TODO: I'd like to see wildcard support, e.g. module/*
- *
* @param string $permission Permission name
- * @return bool
+ *
+ * @return bool True if the user owns the given permission, false if not or if not authenticated
*/
public function hasPermission($permission)
{
if (! $this->isAuthenticated()) {
return false;
}
- foreach ($this->user->getPermissions() as $p) {
- if ($p === $permission) {
- return true;
- }
- }
- return false;
+ return $this->user->can($permission);
}
/**
diff --git a/library/Icinga/Authentication/UserBackend.php b/library/Icinga/Authentication/UserBackend.php
index 9e7abd62a..7829210fd 100644
--- a/library/Icinga/Authentication/UserBackend.php
+++ b/library/Icinga/Authentication/UserBackend.php
@@ -93,26 +93,44 @@ abstract class UserBackend implements Countable
$backend = new DbUserBackend($resource);
break;
case 'msldap':
+ $groupOptions = array(
+ 'group_base_dn' => $backendConfig->group_base_dn,
+ 'group_attribute' => $backendConfig->group_attribute,
+ 'group_member_attribute' => $backendConfig->group_member_attribute,
+ 'group_class' => $backendConfig->group_class
+ );
$backend = new LdapUserBackend(
$resource,
$backendConfig->get('user_class', 'user'),
- $backendConfig->get('user_name_attribute', 'sAMAccountName')
+ $backendConfig->get('user_name_attribute', 'sAMAccountName'),
+ $groupOptions
);
break;
case 'ldap':
- if (($userClass = $backendConfig->user_class) === null) {
+ if ($backendConfig->user_class === null) {
throw new ConfigurationError(
'Authentication configuration for backend "%s" is missing the user_class directive',
$name
);
}
- if (($userNameAttribute = $backendConfig->user_name_attribute) === null) {
+ if ($backendConfig->user_name_attribute === null) {
throw new ConfigurationError(
'Authentication configuration for backend "%s" is missing the user_name_attribute directive',
$name
);
}
- $backend = new LdapUserBackend($resource, $userClass, $userNameAttribute);
+ $groupOptions = array(
+ 'group_base_dn' => $backendConfig->group_base_dn,
+ 'group_attribute' => $backendConfig->group_attribute,
+ 'group_member_attribute' => $backendConfig->group_member_attribute,
+ 'group_class' => $backendConfig->group_class
+ );
+ $backend = new LdapUserBackend(
+ $resource,
+ $backendConfig->user_class,
+ $backendConfig->user_name_attribute,
+ $groupOptions
+ );
break;
default:
throw new ConfigurationError(
diff --git a/library/Icinga/Cli/Params.php b/library/Icinga/Cli/Params.php
index 7236dcca2..5c6cbfe8b 100644
--- a/library/Icinga/Cli/Params.php
+++ b/library/Icinga/Cli/Params.php
@@ -108,6 +108,18 @@ class Params
return $this->standalone;
}
+ /**
+ * Support isset() and empty() checks on options
+ *
+ * @param $name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ return isset($this->params[$name]);
+ }
+
/**
* @see Params::get()
*/
diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php
index a89544be8..94765d98f 100644
--- a/library/Icinga/Data/Db/DbQuery.php
+++ b/library/Icinga/Data/Db/DbQuery.php
@@ -59,6 +59,13 @@ class DbQuery extends SimpleQuery
*/
protected $count;
+ /**
+ * GROUP BY clauses
+ *
+ * @var string|array
+ */
+ protected $group;
+
protected function init()
{
$this->db = $this->ds->getDbAdapter();
@@ -89,17 +96,21 @@ class DbQuery extends SimpleQuery
public function getSelectQuery()
{
$select = $this->dbSelect();
-
// Add order fields to select for postgres distinct queries (#6351)
if ($this->hasOrder()
&& $this->getDatasource()->getDbType() === 'pgsql'
&& $select->getPart(Zend_Db_Select::DISTINCT) === true) {
foreach ($this->getOrder() as $fieldAndDirection) {
- list($alias, $field) = explode('.', $fieldAndDirection[0]);
- $this->columns[$field] = $fieldAndDirection[0];
+ if (array_search($fieldAndDirection[0], $this->columns) === false) {
+ $this->columns[] = $fieldAndDirection[0];
+ }
}
}
+ if ($this->group) {
+ $select->group($this->group);
+ }
+
$select->columns($this->columns);
$this->applyFilterSql($select);
@@ -117,7 +128,7 @@ class DbQuery extends SimpleQuery
return $select;
}
- protected function applyFilterSql($query)
+ public function applyFilterSql($query)
{
$where = $this->renderFilter($this->filter);
if ($where !== '') {
@@ -223,7 +234,7 @@ class DbQuery extends SimpleQuery
*/
public function isTimestamp($field)
{
- return $this;
+ return false;
}
public function whereToSql($col, $sign, $expression)
@@ -253,6 +264,7 @@ class DbQuery extends SimpleQuery
$this->applyFilterSql($count);
if ($this->useSubqueryCount) {
+ $count->columns($this->columns);
$columns = array('cnt' => 'COUNT(*)');
return $this->db->select()->from($count, $columns);
}
@@ -300,4 +312,17 @@ class DbQuery extends SimpleQuery
{
return (string) $this->getSelectQuery();
}
+
+ /**
+ * Add a GROUP BY clause
+ *
+ * @param string|array $group
+ *
+ * @return $this
+ */
+ public function group($group)
+ {
+ $this->group = $group;
+ return $this;
+ }
}
diff --git a/library/Icinga/Data/Filter/FilterExpression.php b/library/Icinga/Data/Filter/FilterExpression.php
index c29414166..8378b5ae3 100644
--- a/library/Icinga/Data/Filter/FilterExpression.php
+++ b/library/Icinga/Data/Filter/FilterExpression.php
@@ -12,6 +12,8 @@ class FilterExpression extends Filter
public function __construct($column, $sign, $expression)
{
+ $column = trim($column);
+ $expression = is_array($expression) ? array_map('trim', $expression) : trim($expression);
$this->column = $column;
$this->sign = $sign;
$this->expression = $expression;
diff --git a/library/Icinga/Data/ResourceFactory.php b/library/Icinga/Data/ResourceFactory.php
index 30597ad2d..e85413615 100644
--- a/library/Icinga/Data/ResourceFactory.php
+++ b/library/Icinga/Data/ResourceFactory.php
@@ -10,7 +10,6 @@ use Icinga\Util\ConfigAwareFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Data\Db\DbConnection;
use Icinga\Protocol\Livestatus\Connection as LivestatusConnection;
-use Icinga\Protocol\Statusdat\Reader as StatusdatReader;
use Icinga\Protocol\Ldap\Connection as LdapConnection;
use Icinga\Protocol\File\FileReader;
@@ -102,7 +101,7 @@ class ResourceFactory implements ConfigAwareFactory
*
* @param Zend_Config $config The configuration for the created resource.
*
- * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader An objects that can be used to access
+ * @return DbConnection|LdapConnection|LivestatusConnection An object that can be used to access
* the given resource. The returned class depends on the configuration property 'type'.
* @throws ConfigurationError When an unsupported type is given
*/
@@ -115,9 +114,6 @@ class ResourceFactory implements ConfigAwareFactory
case 'ldap':
$resource = new LdapConnection($config);
break;
- case 'statusdat':
- $resource = new StatusdatReader($config);
- break;
case 'livestatus':
$resource = new LivestatusConnection($config->socket);
break;
@@ -137,7 +133,7 @@ class ResourceFactory implements ConfigAwareFactory
* Create a resource from name
*
* @param string $resourceName
- * @return DbConnection|LdapConnection|LivestatusConnection|StatusdatReader
+ * @return DbConnection|LdapConnection|LivestatusConnection
*/
public static function create($resourceName)
{
diff --git a/library/Icinga/Exception/InvalidPropertyException.php b/library/Icinga/Exception/InvalidPropertyException.php
new file mode 100644
index 000000000..f79055f0b
--- /dev/null
+++ b/library/Icinga/Exception/InvalidPropertyException.php
@@ -0,0 +1,10 @@
+ 'DEBUG',
+ Logger::INFO => 'INFO',
+ Logger::WARNING => 'WARNING',
+ Logger::ERROR => 'ERROR'
+ );
+
/**
* This logger's instance
*
- * @var Logger
+ * @var static
*/
protected static $instance;
/**
- * The log writer to use
+ * Log writer
*
* @var \Icinga\Logger\LogWriter
*/
protected $writer;
/**
- * The configured type
- *
- * @string Type (syslog, file)
- */
- protected $type = 'none';
-
- /**
- * The maximum severity to emit
+ * Maximum level to emit
*
* @var int
*/
- protected $verbosity;
-
- /**
- * The supported severities
- */
- public static $ERROR = 0;
- public static $WARNING = 1;
- public static $INFO = 2;
- public static $DEBUG = 3;
+ protected $level;
/**
* Create a new logger object
*
- * @param Zend_Config $config
+ * @param Zend_Config $config
+ *
+ * @throws ConfigurationError If the logging configuration directive 'log' is missing or if the logging level is
+ * not defined
*/
public function __construct(Zend_Config $config)
{
- $this->verbosity = $config->level;
+ if ($config->log === null) {
+ throw new ConfigurationError('Required logging configuration directive \'log\' missing');
+ }
- if ($config->enable) {
+ if (($level = $config->level) !== null) {
+ if (is_numeric($level)) {
+ if (! isset(static::$levels[(int) $level])) {
+ throw new ConfigurationError(
+ 'Can\'t set logging level %d. Logging level is not defined. Use one of %s or one of the'
+ . ' Logger\'s constants.',
+ $level,
+ implode(', ', array_keys(static::$levels))
+ );
+ }
+ $this->level = static::$levels[(int) $level];
+ } else {
+ $level = strtoupper($level);
+ $levels = array_flip(static::$levels);
+ if (! isset($levels[$level])) {
+ throw new ConfigurationError(
+ 'Can\'t set logging level "%s". Logging level is not defined. Use one of %s.',
+ $level,
+ implode(', ', array_keys($levels))
+ );
+ }
+ $this->level = $levels[$level];
+ }
+ } else {
+ $this->level = static::ERROR;
+ }
+
+ if (strtolower($config->get('log', 'syslog')) !== 'none') {
$this->writer = $this->createWriter($config);
}
}
@@ -67,14 +118,17 @@ class Logger
* Create a new logger object
*
* @param Zend_Config $config
+ *
+ * @return static
*/
public static function create(Zend_Config $config)
{
static::$instance = new static($config);
+ return static::$instance;
}
/**
- * Return a log writer
+ * Create a log writer
*
* @param Zend_Config $config The configuration to initialize the writer with
*
@@ -83,28 +137,26 @@ class Logger
*/
protected function createWriter(Zend_Config $config)
{
- $class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->type)) . 'Writer';
- if (!class_exists($class)) {
+ $class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->log)) . 'Writer';
+ if (! class_exists($class)) {
throw new ConfigurationError(
'Cannot find log writer of type "%s"',
- $config->type
+ $config->log
);
}
- $this->type = $config->type;
-
return new $class($config);
}
/**
- * Write a message to the log
+ * Log a message
*
- * @param string $message The message to write
- * @param int $severity The severity to use
+ * @param int $level The logging level
+ * @param string $message The log message
*/
- public function log($message, $severity)
+ public function log($level, $message)
{
- if ($this->writer !== null && $this->verbosity >= $severity) {
- $this->writer->log($severity, $message);
+ if ($this->writer !== null && $this->level >= $level) {
+ $this->writer->log($level, $message);
}
}
@@ -162,7 +214,7 @@ class Logger
public static function error()
{
if (static::$instance !== null && func_num_args() > 0) {
- static::$instance->log(static::formatMessage(func_get_args()), static::$ERROR);
+ static::$instance->log(static::ERROR, static::formatMessage(func_get_args()));
}
}
@@ -174,7 +226,7 @@ class Logger
public static function warning()
{
if (static::$instance !== null && func_num_args() > 0) {
- static::$instance->log(static::formatMessage(func_get_args()), static::$WARNING);
+ static::$instance->log(static::WARNING, static::formatMessage(func_get_args()));
}
}
@@ -186,7 +238,7 @@ class Logger
public static function info()
{
if (static::$instance !== null && func_num_args() > 0) {
- static::$instance->log(static::formatMessage(func_get_args()), static::$INFO);
+ static::$instance->log(static::INFO, static::formatMessage(func_get_args()));
}
}
@@ -198,7 +250,7 @@ class Logger
public static function debug()
{
if (static::$instance !== null && func_num_args() > 0) {
- static::$instance->log(static::formatMessage(func_get_args()), static::$DEBUG);
+ static::$instance->log(static::DEBUG, static::formatMessage(func_get_args()));
}
}
@@ -212,20 +264,30 @@ class Logger
return $this->writer;
}
+ /**
+ * Is the logger writing to Syslog?
+ *
+ * @return bool
+ */
public static function writesToSyslog()
{
- return static::$instance && static::$instance->type === 'syslog';
+ return static::$instance && static::$instance instanceof SyslogWriter;
}
+ /**
+ * Is the logger writing to a file?
+ *
+ * @return bool
+ */
public static function writesToFile()
{
- return static::$instance && static::$instance->type === 'file';
+ return static::$instance && static::$instance instanceof FileWriter;
}
/**
* Get this' instance
*
- * @return Logger
+ * @return static
*/
public static function getInstance()
{
diff --git a/library/Icinga/Logger/Writer/FileWriter.php b/library/Icinga/Logger/Writer/FileWriter.php
index 4ab04d85b..527358e1c 100644
--- a/library/Icinga/Logger/Writer/FileWriter.php
+++ b/library/Icinga/Logger/Writer/FileWriter.php
@@ -5,38 +5,43 @@
namespace Icinga\Logger\Writer;
use Exception;
-use Icinga\Exception\IcingaException;
use Zend_Config;
-use Icinga\Util\File;
+use Icinga\Exception\ConfigurationError;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
-use Icinga\Exception\ConfigurationError;
+use Icinga\Util\File;
/**
- * Class to write log messages to a file
+ * Log to a file
*/
class FileWriter extends LogWriter
{
/**
- * The path to the file
+ * Path to the file
*
* @var string
*/
- protected $path;
+ protected $file;
/**
- * Create a new log writer initialized with the given configuration
+ * Create a new file log writer
*
- * @throws ConfigurationError In case the log path does not exist
+ * @param Zend_Config $config
+ *
+ * @throws ConfigurationError If the configuration directive 'file' is missing or if the path to 'file' does
+ * not exist or if writing to 'file' is not possible
*/
public function __construct(Zend_Config $config)
{
- $this->path = $config->target;
+ if ($config->file === null) {
+ throw new ConfigurationError('Required logging configuration directive \'file\' missing');
+ }
+ $this->file = $config->file;
- if (substr($this->path, 0, 6) !== 'php://' && false === file_exists(dirname($this->path))) {
+ if (substr($this->file, 0, 6) !== 'php://' && ! file_exists(dirname($this->file))) {
throw new ConfigurationError(
'Log path "%s" does not exist',
- dirname($this->path)
+ dirname($this->file)
);
}
@@ -45,70 +50,32 @@ class FileWriter extends LogWriter
} catch (Exception $e) {
throw new ConfigurationError(
'Cannot write to log file "%s" (%s)',
- $this->path,
+ $this->file,
$e->getMessage()
);
}
}
/**
- * Log a message with the given severity
+ * Log a message
*
- * @param int $severity The severity to use
- * @param string $message The message to log
+ * @param int $level The logging level
+ * @param string $message The log message
*/
- public function log($severity, $message)
+ public function log($level, $message)
{
- $this->write(date('c') . ' ' . $this->getSeverityString($severity) . ' ' . $message . PHP_EOL);
+ $this->write(date('c') . ' - ' . Logger::$levels[$level] . ' - ' . $message . PHP_EOL);
}
/**
- * Return a string representation for the given severity
+ * Write a message to the log
*
- * @param string $severity The severity to use
- *
- * @return string The string representation of the severity
- *
- * @throws IcingaException In case the given severity is unknown
+ * @param string $message
*/
- protected function getSeverityString($severity)
+ protected function write($message)
{
- switch ($severity) {
- case Logger::$ERROR:
- return '- ERROR -';
- case Logger::$WARNING:
- return '- WARNING -';
- case Logger::$INFO:
- return '- INFO -';
- case Logger::$DEBUG:
- return '- DEBUG -';
- default:
- throw new IcingaException(
- 'Unknown severity "%s"',
- $severity
- );
- }
- }
-
- /**
- * Write a message to the path
- *
- * @param string $text The message to write
- *
- * @throws Exception In case write acess to the path failed
- */
- protected function write($text)
- {
- $file = new File($this->path, 'a');
- $file->fwrite($text);
+ $file = new File($this->file, 'a');
+ $file->fwrite($message);
$file->fflush();
}
-
- /**
- * @return string
- */
- public function getPath()
- {
- return $this->path;
- }
}
diff --git a/library/Icinga/Logger/Writer/SyslogWriter.php b/library/Icinga/Logger/Writer/SyslogWriter.php
index d176e1cbb..f67c618d2 100644
--- a/library/Icinga/Logger/Writer/SyslogWriter.php
+++ b/library/Icinga/Logger/Writer/SyslogWriter.php
@@ -4,27 +4,24 @@
namespace Icinga\Logger\Writer;
-use Exception;
use Zend_Config;
use Icinga\Logger\Logger;
use Icinga\Logger\LogWriter;
-use Icinga\Exception\ConfigurationError;
-use Icinga\Exception\IcingaException;
/**
- * Class to write messages to syslog
+ * Log to the syslog service
*/
class SyslogWriter extends LogWriter
{
/**
- * The facility where to write messages to
+ * Syslog facility
*
- * @var string
+ * @var int
*/
protected $facility;
/**
- * The prefix to prepend to each message
+ * Prefix to prepend to each message
*
* @var string
*/
@@ -35,79 +32,42 @@ class SyslogWriter extends LogWriter
*
* @var array
*/
- protected $facilities = array(
- 'LOG_USER' => LOG_USER
+ public static $facilities = array(
+ 'user' => LOG_USER
);
/**
- * Create a new log writer initialized with the given configuration
+ * Log level to syslog severity map
+ *
+ * @var array
+ */
+ public static $severityMap = array(
+ Logger::ERROR => LOG_ERR,
+ Logger::WARNING => LOG_WARNING,
+ Logger::INFO => LOG_INFO,
+ Logger::DEBUG => LOG_DEBUG
+ );
+
+ /**
+ * Create a new syslog log writer
+ *
+ * @param Zend_Config $config
*/
public function __construct(Zend_Config $config)
{
- if (!array_key_exists($config->facility, $this->facilities)) {
- throw new ConfigurationError(
- 'Cannot create syslog writer with unknown facility "%s"',
- $config->facility
- );
- }
-
- $this->ident = $config->application;
- $this->facility = $this->facilities[$config->facility];
+ $this->ident = $config->get('application', 'icingaweb');
+ $this->facility = static::$facilities['user'];
}
/**
- * Log a message with the given severity
+ * Log a message
*
- * @param int $severity The severity to use
- * @param string $message The message to log
- *
- * @throws Exception In case the given severity cannot be mapped to a valid syslog priority
+ * @param int $level The logging level
+ * @param string $message The log message
*/
- public function log($severity, $message)
+ public function log($level, $message)
{
- $priorities = array(
- Logger::$ERROR => LOG_ERR,
- Logger::$WARNING => LOG_WARNING,
- Logger::$INFO => LOG_INFO,
- Logger::$DEBUG => LOG_DEBUG
- );
-
- if (!array_key_exists($severity, $priorities)) {
- throw new IcingaException(
- 'Severity "%s" cannot be mapped to a valid syslog priority',
- $severity
- );
- }
-
- $this->open();
- $this->write($priorities[$severity], $message);
- $this->close();
- }
-
- /**
- * Open a new syslog connection
- */
- protected function open()
- {
- openlog($this->ident, 0, $this->facility);
- }
-
- /**
- * Write a message to the syslog connection
- *
- * @param int $priority The priority to use
- * @param string $message The message to write
- */
- protected function write($priority, $message)
- {
- syslog($priority, $message);
- }
-
- /**
- * Close the syslog connection
- */
- protected function close()
- {
- closelog();
+ openlog($this->ident, LOG_PID, $this->facility);
+ syslog(static::$severityMap[$level], $message);
}
}
diff --git a/library/Icinga/Protocol/Commandpipe/Command.php b/library/Icinga/Protocol/Commandpipe/Command.php
deleted file mode 100644
index e89169f35..000000000
--- a/library/Icinga/Protocol/Commandpipe/Command.php
+++ /dev/null
@@ -1,177 +0,0 @@
-withoutHosts = (bool) $state;
- return $this;
- }
-
- /**
- * Set whether this command should only affect the hosts of a host- or servicegroup
- *
- * @param bool $state
- * @return self
- */
- public function excludeServices($state = true)
- {
- $this->withoutServices = (bool) $state;
- return $this;
- }
-
- /**
- * Set whether this command should also affect all children hosts of a host
- *
- * @param bool $state
- * @return self
- */
- public function includeChildren($state = true)
- {
- $this->withChildren = (bool) $state;
- return $this;
- }
-
- /**
- * Set whether this command only affects the services associated with a particular host
- *
- * @param bool $state
- * @return self
- */
- public function excludeHost($state = true)
- {
- $this->onlyServices = (bool) $state;
- return $this;
- }
-
- /**
- * Getter for flag whether a command is global
- * @return bool
- */
- public function provideGlobalCommand()
- {
- return (bool) $this->globalCommand;
- }
-
- /**
- * Return this command's arguments in the order expected by the actual command definition
- *
- * @return array
- */
- abstract public function getArguments();
-
- /**
- * Return the command as a string with the given host being inserted
- *
- * @param string $hostname The name of the host to insert
- *
- * @return string The string representation of the command
- */
- abstract public function getHostCommand($hostname);
-
- /**
- * Return the command as a string with the given host and service being inserted
- *
- * @param string $hostname The name of the host to insert
- * @param string $servicename The name of the service to insert
- *
- * @return string The string representation of the command
- */
- abstract public function getServiceCommand($hostname, $servicename);
-
- /**
- * Return the command as a string with the given hostgroup being inserted
- *
- * @param string $hostgroup The name of the hostgroup to insert
- *
- * @return string The string representation of the command
- */
- public function getHostgroupCommand($hostgroup)
- {
- throw new ProgrammingError(
- '%s does not provide a hostgroup command',
- get_class($this)
- );
- }
-
- /**
- * Return the command as a string with the given servicegroup being inserted
- *
- * @param string $servicegroup The name of the servicegroup to insert
- *
- * @return string The string representation of the command
- */
- public function getServicegroupCommand($servicegroup)
- {
- throw new ProgrammingError(
- '%s does not provide a servicegroup command',
- get_class($this)
- );
- }
-
- /**
- * Return the command as a string for the whole instance
- *
- * @param string $instance
- *
- * @return string
- * @throws ProgrammingError
- */
- public function getGlobalCommand($instance = null)
- {
- throw new ProgrammingError(
- '%s does not provide a global command',
- getclass($this)
- );
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/CommandPipe.php b/library/Icinga/Protocol/Commandpipe/CommandPipe.php
deleted file mode 100644
index 8d149dd20..000000000
--- a/library/Icinga/Protocol/Commandpipe/CommandPipe.php
+++ /dev/null
@@ -1,603 +0,0 @@
-getTransportForConfiguration($config);
- $this->name = $config->name;
- }
-
- /**
- * Setup the @see Icinga\Protocol\Commandpipe\Transport.php class that will be used for accessing the command pipe
- *
- * Currently this method uses SecureShell when a host is given, otherwise it assumes the pipe is accessible
- * via the machines filesystem
- *
- * @param \Zend_Config $config The configuration as defined in the instances.ini
- */
- private function getTransportForConfiguration(\Zend_Config $config)
- {
- if (isset($config->host)) {
- $this->transport = new SecureShell();
- $this->transport->setEndpoint($config);
- } else {
- $this->transport = new LocalPipe();
- $this->transport->setEndpoint($config);
- }
- }
-
- /**
- * Send the command string $command to the icinga pipe
- *
- * This method just delegates the send command to the underlying transport
- *
- * @param String $command The command string to send, without the timestamp
- */
- public function send($command)
- {
- $this->transport->send($this->escape($command));
- }
-
- /**
- * Return the given command string with escaped newlines
- *
- * @param string $command The command string to escape
- *
- * @return string The escaped command string
- */
- public function escape($command)
- {
- return str_replace(array("\r", "\n"), array('\r', '\n'), $command);
- }
-
- /**
- * Send a command to the icinga pipe
- *
- * @param Command $command
- * @param array $objects
- */
- public function sendCommand(Command $command, array $objects = array())
- {
- if ($command->provideGlobalCommand() === true) {
- $this->send($command->getGlobalCommand());
- } else {
- foreach ($objects as $object) {
- $objectType = $this->getObjectType($object);
- if ($objectType === self::TYPE_SERVICE) {
- $this->send($command->getServiceCommand($object->host_name, $object->service_description));
- } else {
- $this->send($command->getHostCommand($object->host_name));
- }
- }
- }
- }
-
- /**
- * Remove the acknowledgements of the provided objects
- *
- * @param array $objects An array of mixed service and host objects whose acknowledgments will be removed
- */
- public function removeAcknowledge($objects)
- {
- foreach ($objects as $object) {
- if (isset($object->service_description)) {
- $this->send("REMOVE_SVC_ACKNOWLEDGEMENT;$object->host_name;$object->service_description");
- } else {
- $this->send("REMOVE_HOST_ACKNOWLEDGEMENT;$object->host_name");
- }
- }
- }
-
- /**
- * Removes the submitted comments
- *
- * @param array $objectsOrComments An array of hosts and services (to remove all their comments)
- * or single comment objects to remove
- */
- public function removeComment($objectsOrComments)
- {
- foreach ($objectsOrComments as $object) {
- if (isset($object->comment_id)) {
- if (isset($object->service_description)) {
- $type = "SERVICE_COMMENT";
- } else {
- $type = "HOST_COMMENT";
- }
- $this->send("DEL_{$type};" . intval($object->comment_id));
- } else {
- if (isset($object->service_description)) {
- $type = "SERVICE_COMMENT";
- } else {
- $type = "HOST_COMMENT";
- }
- $cmd = "DEL_ALL_{$type}S;" . $object->host_name;
- if ($type == "SERVICE_COMMENT") {
- $cmd .= ";" . $object->service_description;
- }
- $this->send($cmd);
- }
- }
- }
-
- /**
- * Globally enable notifications for this instance
- *
- */
- public function enableGlobalNotifications()
- {
- $this->send("ENABLE_NOTIFICATIONS");
- }
-
- /**
- * Globally disable notifications for this instance
- *
- */
- public function disableGlobalNotifications()
- {
- $this->send("DISABLE_NOTIFICATIONS");
- }
-
- /**
- * Return the object type of the provided object (TYPE_SERVICE or TYPE_HOST)
- *
- * @param $object The object to identify
- * @return string TYPE_SERVICE or TYPE_HOST
- */
- private function getObjectType($object)
- {
- //@TODO: This must be refactored once more commands are supported
- if (isset($object->service_description)) {
- return self::TYPE_SERVICE;
- }
- return self::TYPE_HOST;
- }
-
- /**
- * Remove downtimes for objects
- *
- * @param array $objects An array containing hosts, service or downtime objects
- * @param int $starttime An optional starttime to use for the DEL_DOWNTIME_BY_HOST_NAME command
- */
- public function removeDowntime($objects, $starttime = 0)
- {
- foreach ($objects as $object) {
- $type = $this->getObjectType($object);
- if (isset($object->downtime_id)) {
- $this->send("DEL_" . $type . "_DOWNTIME;" . $object->downtime_id);
- continue;
- }
- $cmd = "DEL_DOWNTIME_BY_HOST_NAME;" . $object->host_name;
- if ($type == self::TYPE_SERVICE) {
- $cmd .= ";" . $object->service_description;
- }
- if ($starttime != 0) {
- $cmd .= ";" . $starttime;
- }
- $this->send($cmd);
- }
- }
-
- /**
- * Restart the icinga instance
- *
- */
- public function restartIcinga()
- {
- $this->send("RESTART_PROCESS");
- }
-
- /**
- * Modify monitoring flags for the provided objects
- *
- * @param array $objects An arry of service and/or host objects to modify
- * @param PropertyModifier $flags The Monitoring attributes to modify
- */
- public function setMonitoringProperties($objects, PropertyModifier $flags)
- {
- foreach ($objects as $object) {
- $type = $this->getObjectType($object);
- $formatArray = $flags->getFormatString($type);
- foreach ($formatArray as $format) {
- $format .= ";"
- . $object->host_name
- . ($type == self::TYPE_SERVICE ? ";" . $object->service_description : "");
- $this->send($format);
- }
- }
- }
-
- /**
- * Enable active checks for all provided objects
- *
- * @param array $objects An array containing services and hosts to enable active checks for
- */
- public function enableActiveChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::ACTIVE => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable active checks for all provided objects
- *
- * @param array $objects An array containing services and hosts to disable active checks
- */
- public function disableActiveChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::ACTIVE => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable passive checks for all provided objects
- *
- * @param array $objects An array containing services and hosts to enable passive checks for
- */
- public function enablePassiveChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::PASSIVE => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Enable passive checks for all provided objects
- *
- * @param array $objects An array containing services and hosts to enable passive checks for
- */
- public function disablePassiveChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::PASSIVE => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable flap detection for all provided objects
- *
- * @param array $objects An array containing services and hosts to enable flap detection
- *
- */
- public function enableFlappingDetection($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::FLAPPING => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable flap detection for all provided objects
- *
- * @param array $objects An array containing services and hosts to disable flap detection
- *
- */
- public function disableFlappingDetection($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::FLAPPING => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable notifications for all provided objects
- *
- * @param array $objects An array containing services and hosts to enable notification
- *
- */
- public function enableNotifications($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable flap detection for all provided objects
- *
- * @param array $objects An array containing services and hosts to disable notifications
- *
- */
- public function disableNotifications($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::NOTIFICATIONS => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable freshness checks for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function enableFreshnessChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::FRESHNESS => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable freshness checks for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function disableFreshnessChecks($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::FRESHNESS => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable event handler for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function enableEventHandler($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable event handler for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function disableEventHandler($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::EVENTHANDLER => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Enable performance data parsing for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function enablePerfdata($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::PERFDATA => PropertyModifier::STATE_ENABLE
- )
- )
- );
- }
-
- /**
- * Disable performance data parsing for all provided objects
- *
- * @param array $objects An array of hosts and/or services
- */
- public function disablePerfdata($objects)
- {
- $this->setMonitoringProperties(
- $objects,
- new PropertyModifier(
- array(
- PropertyModifier::PERFDATA => PropertyModifier::STATE_DISABLE
- )
- )
- );
- }
-
- /**
- * Disable notifications for all services of the provided hosts
- *
- * @param array $objects An array of hosts
- */
- public function disableNotificationsForServices($objects)
- {
- foreach ($objects as $host) {
- $msg = 'DISABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name;
- $this->send($msg);
- }
- }
-
- /**
- * Enable notifications for all services of the provided hosts
- *
- * @param array $objects An array of hosts
- */
- public function enableNotificationsForServices($objects)
- {
- foreach ($objects as $host) {
- $msg = 'ENABLE_HOST_SVC_NOTIFICATIONS;'.$host->host_name;
- $this->send($msg);
- }
- }
-
- /**
- * Disable active checks for all services of the provided hosts
- *
- * @param array $objects An array of hosts
- */
- public function disableActiveChecksWithChildren($objects)
- {
- foreach ($objects as $host) {
- $msg = 'DISABLE_HOST_SVC_CHECKS;'.$host->host_name;
- $this->send($msg);
- }
- }
-
- /**
- * Enable active checks for all services of the provided hosts
- *
- * @param array $objects An array of hosts
- */
- public function enableActiveChecksWithChildren($objects)
- {
- foreach ($objects as $host) {
- $msg = 'ENABLE_HOST_SVC_CHECKS;'.$host->host_name;
- $this->send($msg);
- }
- }
-
- /**
- * Reset modified attributes for all provided objects
- *
- * @param array $objects An array of hosts and services
- */
- public function resetAttributes($objects)
- {
- foreach ($objects as $object) {
- $type = $this->getObjectType($object);
- if ($type === self::TYPE_SERVICE) {
- $this->send('CHANGE_SVC_MODATTR;'.$object->host_name.';'.$object->service_description.';0');
- } else {
- $this->send('CHANGE_HOST_MODATTR;'.$object->host_name.';0');
- }
- }
- }
-
- /**
- * Return the transport handler that handles actual sending of commands
- *
- * @return Transport
- */
- public function getTransport()
- {
- return $this->transport;
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/Comment.php b/library/Icinga/Protocol/Commandpipe/Comment.php
deleted file mode 100644
index 2b8caf52b..000000000
--- a/library/Icinga/Protocol/Commandpipe/Comment.php
+++ /dev/null
@@ -1,61 +0,0 @@
-author = $author;
- $this->content = $content;
- $this->persistent = $persistent;
- }
-
- /**
- * Return this comment's properties as list of command parameters
- *
- * @param bool $ignorePersistentFlag Whether the persistent flag should be included or not
- * @return array
- */
- public function getArguments($ignorePersistentFlag = false)
- {
- if ($ignorePersistentFlag) {
- return array($this->author, $this->content);
- } else {
- return array($this->persistent ? '1' : '0', $this->author, $this->content);
- }
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php b/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php
deleted file mode 100644
index 662a156e9..000000000
--- a/library/Icinga/Protocol/Commandpipe/Exception/InvalidCommandException.php
+++ /dev/null
@@ -1,12 +0,0 @@
- self::STATE_KEEP,
- self::ACTIVE => self::STATE_KEEP,
- self::PASSIVE => self::STATE_KEEP,
- self::NOTIFICATIONS => self::STATE_KEEP,
- self::FRESHNESS => self::STATE_KEEP,
- self::EVENTHANDLER => self::STATE_KEEP
- );
-
- /**
- * Create a new PropertyModified object using the given flags
- *
- * @param array $flags Flags to enable/disable/keep different monitoring attributes
- */
- public function __construct(array $flags)
- {
- foreach ($flags as $type => $value) {
- if (isset($this->flags[$type])) {
- $this->flags[$type] = $value;
- }
- }
- }
-
- /**
- * Return this object as a template for the given object type
- *
- * @param $type Either CommandPipe::TYPE_HOST or CommandPipe::TYPE_SERVICE
- * @return array An array of external command templates for the given type representing the containers state
- */
- public function getFormatString($type)
- {
- $cmd = array();
- foreach ($this->flags as $cmdTemplate => $setting) {
- if ($setting == self::STATE_KEEP) {
- continue;
- }
- $commandString = ($setting == self::STATE_ENABLE ? "ENABLE_" : "DISABLE_");
- $targetString = $type;
- if ($type == CommandPipe::TYPE_SERVICE && $cmdTemplate == self::FRESHNESS) {
- // the external command definition is inconsistent here..
- $targetString = "SERVICE";
- }
- $commandString .= sprintf($cmdTemplate, $targetString);
- $cmd[] = $commandString;
- }
- return $cmd;
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php b/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php
deleted file mode 100644
index 32f745565..000000000
--- a/library/Icinga/Protocol/Commandpipe/Transport/LocalPipe.php
+++ /dev/null
@@ -1,70 +0,0 @@
-path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd';
- }
-
- /**
- * @see Transport::send()
- */
- public function send($message)
- {
- Logger::debug('Attempting to send external icinga command %s to local command file ', $message, $this->path);
-
- try {
- $file = new File($this->path, $this->openMode);
- $file->fwrite('[' . time() . '] ' . $message . PHP_EOL);
- $file->fflush();
- } catch (Exception $e) {
- throw new ConfigurationError(
- 'Could not open icinga command pipe at "%s" (%s)',
- $this->path,
- $e->getMessage()
- );
- }
-
- Logger::debug('Command sent: [' . time() . '] ' . $message . PHP_EOL);
- }
-
- /**
- * Overwrite the open mode (useful for testing)
- *
- * @param string $mode The mode to use to access the pipe
- */
- public function setOpenMode($mode)
- {
- $this->openMode = $mode;
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php
deleted file mode 100644
index f1505c2ef..000000000
--- a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php
+++ /dev/null
@@ -1,102 +0,0 @@
-host = isset($config->host) ? $config->host : 'localhost';
- $this->port = isset($config->port) ? $config->port : 22;
- $this->user = isset($config->user) ? $config->user : null;
- $this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd';
- }
-
- /**
- * Write the given external command to the command pipe
- *
- * @param string $command
- *
- * @throws RuntimeException When the command could not be sent to the remote Icinga host
- * @see Transport::send()
- */
- public function send($command)
- {
- $retCode = 0;
- $output = array();
- Logger::debug(
- 'Icinga instance is on different host, attempting to send command %s via ssh to %s:%s/%s',
- $command,
- $this->host,
- $this->port,
- $this->path
- );
- $hostConnector = $this->user ? $this->user . "@" . $this->host : $this->host;
- $command = escapeshellarg('['. time() .'] ' . $command);
- $sshCommand = sprintf(
- 'ssh -o BatchMode=yes -o KbdInteractiveAuthentication=no %s -p %d'
- . ' "echo %s > %s" 2>&1',
- $hostConnector,
- $this->port,
- $command,
- $this->path
- );
-
- exec($sshCommand, $output, $retCode);
- Logger::debug("Command '%s' exited with %d: %s", $sshCommand, $retCode, $output);
-
- if ($retCode != 0) {
- $msg = 'Could not send command to remote Icinga host: '
- . implode(PHP_EOL, $output)
- . " (returncode $retCode)";
- Logger::error($msg);
- throw new RuntimeException($msg);
- }
- }
-}
diff --git a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php
deleted file mode 100644
index 59119cf13..000000000
--- a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php
+++ /dev/null
@@ -1,27 +0,0 @@
-filename, $this->fields)
- );
+ return new FileIterator($this->filename, $this->fields);
}
/**
diff --git a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php b/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php
deleted file mode 100644
index 0947e65bc..000000000
--- a/library/Icinga/Protocol/Statusdat/Exception/ParsingException.php
+++ /dev/null
@@ -1,13 +0,0 @@
-ref = & $obj;
- $this->reader = & $reader;
- }
-
- /**
- * @param $attribute
- * @return \stdClass
- */
- public function __get($attribute)
- {
- $exploded = explode(".", $attribute);
- $result = $this->ref;
-
- foreach ($exploded as $elem) {
- if (isset($result->$elem)) {
- $result = $result->$elem;
- } else {
- return null;
- }
- }
- return $result;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/Parser.php b/library/Icinga/Protocol/Statusdat/Parser.php
deleted file mode 100644
index 5b3694518..000000000
--- a/library/Icinga/Protocol/Statusdat/Parser.php
+++ /dev/null
@@ -1,422 +0,0 @@
-file = $file;
- $this->icingaState = $baseState;
- }
-
- /**
- * Parse the given file handle as an objects file and read object information
- */
- public function parseObjectsFile()
- {
- $DEFINE = strlen('define ');
- $this->icingaState = array();
- foreach ($this->file as $line) {
- $line = trim($line);
- $this->lineCtr++;
- if ($line === '' || $line[0] === '#') {
- continue;
- }
- $this->currentObjectType = trim(substr($line, $DEFINE, -1));
- if (!isset($this->icingaState[$this->currentObjectType])) {
- $this->icingaState[$this->currentObjectType] = array();
- }
- $this->readCurrentObject();
- }
- $this->processDeferred();
- }
-
- /**
- * Parse the given file as an status.dat file and read runtime information
- *
- * @param File $file The file to parse or null to parse the one passed to the constructor
- */
- public function parseRuntimeState(File $file = null)
- {
- if ($file != null) {
- $this->file = $file;
- } else {
- $file = $this->file;
- }
-
- if (!$this->icingaState) {
- throw new ProgrammingError('Tried to read runtime state without existing objects data');
- }
- $this->overwrites = array();
- foreach ($file as $line) {
- $line = trim($line);
- $this->lineCtr++;
- if ($line === '' || $line[0] === '#') {
- continue;
- }
- $this->currentStateType = trim(substr($line, 0, -1));
- $this->readCurrentState();
- }
- }
-
- /**
- * Read the next object from the object.cache file handle
- *
- * @throws ParsingException
- */
- private function readCurrentObject()
- {
- $monitoringObject = new PrintableObject();
- foreach ($this->file as $line) {
- $line = explode("\t", trim($line), 2);
- $this->lineCtr++;
- if (!$line) {
- continue;
- }
-
- // End of object
- if ($line[0] === '}') {
- $this->registerObject($monitoringObject);
- return;
- }
- if (!isset($line[1])) {
- $line[1] = '';
- }
- $monitoringObject->{$line[0]} = trim($line[1]);
- }
- throw new ParsingException('Unexpected EOF in objects.cache, line ' . $this->lineCtr);
- }
-
- /**
- * Read the next state from the status.dat file handler
- *
- * @throws Exception\ParsingException
- */
- private function readCurrentState()
- {
- $statusdatObject = new RuntimeStateContainer();
-
- $objectType = $this->getObjectTypeForState();
-
- if ($objectType != 'host' && $objectType != 'service') {
- $this->skipObject(); // ignore unknown objects
- return;
- }
- if (!isset($this->icingaState[$this->currentObjectType])) {
- throw new ParsingException("No $this->currentObjectType objects registered in objects.cache");
- }
- $base = & $this->icingaState[$this->currentObjectType];
- $state = $this->skipObject(true);
- $statusdatObject->runtimeState = & $state;
- $name = $this->getObjectIdentifier($statusdatObject);
-
- if (!isset($base[$name])) {
- throw new ParsingException(
- "Unknown object $name " . $this->currentObjectType . ' - '
- . print_r(
- $statusdatObject,
- true
- )
- . "\n" . print_r($base, true)
- );
- }
- $type = substr($this->currentStateType, strlen($objectType));
-
- if ($type == 'status') {
- // directly set the status to the status field of the given object
- $base[$name]->status = & $statusdatObject;
- } else {
- if (!isset($base[$name]->$type) || !in_array($base[$name]->$type, $this->overwrites)) {
- $base[$name]->$type = array();
- $this->overwrites[] = & $base[$name]->$type;
- }
- array_push($base[$name]->$type, $statusdatObject);
- $this->currentObjectType = $type;
- if (!isset($this->icingaState[$type])) {
- $this->icingaState[$type] = array();
- }
- $this->icingaState[$type][] = &$statusdatObject;
- $id = $this->getObjectIdentifier($statusdatObject);
- if ($id !== false && isset($this->icingaState[$objectType][$id])) {
- $statusdatObject->$objectType = $this->icingaState[$objectType][$id];
- }
- }
-
- return;
-
- }
-
- /**
- * Get the corresponding object type name for the given state
- *
- * @return string
- */
- private function getObjectTypeForState()
- {
- $pos = strpos($this->currentStateType, 'service');
-
- if ($pos === false) {
- $pos = strpos($this->currentStateType, 'host');
- } else {
- $this->currentObjectType = 'service';
- return 'service';
- }
-
- if ($pos === false) {
- return $this->currentStateType;
- } else {
- $this->currentObjectType = 'host';
- return 'host';
- }
-
- return $this->currentObjectType;
- }
-
- /**
- * Skip the current object definition
- *
- * @param bool $returnString If true, the object string will be returned
- * @return string The skipped object if $returnString is true
- */
- protected function skipObject($returnString = false)
- {
- if (!$returnString) {
- while (trim($this->file->fgets()) !== '}') {
- }
- return null;
- } else {
- $str = '';
- while (($val = trim($this->file->fgets())) !== '}') {
- $str .= $val . "\n";
- }
- return $str;
- }
- }
-
- /**
- * Register the given object in the icinga state
- *
- * @param object $object The monitoring object to register
- */
- protected function registerObject(&$object)
- {
-
- $name = $this->getObjectIdentifier($object);
- if ($name !== false) {
- $this->icingaState[$this->currentObjectType][$name] = &$object;
- }
- $this->registerObjectAsProperty($object);
- }
-
- /**
- * Register the given object as a property in related objects
- *
- * This registers for example hosts underneath their hostgroup and vice cersa
- *
- * @param object $object The object to register as a property
- */
- protected function registerObjectAsProperty(&$object)
- {
- if ($this->currentObjectType == 'service'
- || $this->currentObjectType == 'host'
- || $this->currentObjectType == 'contact') {
- return null;
- }
- $isService = strpos($this->currentObjectType, 'service') !== false;
- $isHost = strpos($this->currentObjectType, 'host') !== false;
- $isContact = strpos($this->currentObjectType, 'contact') !== false;
- $name = $this->getObjectIdentifier($object);
-
- if ($isService === false && $isHost === false && $isContact === false) {
- // this would be error in the parser implementation
- return null;
- }
- $property = $this->currentObjectType;
- if ($isService) {
- $this->currentObjectType = 'service';
- $property = substr($property, strlen('service'));
- } elseif ($isHost) {
- $this->currentObjectType = 'host';
- $property = substr($property, strlen('host'));
- } elseif ($isContact) {
- $this->currentObjectType = 'contact';
- $property = substr($property, strlen('contact'));
- }
-
- if (!isset($this->icingaState[$this->currentObjectType])) {
- return $this->deferRegistration($object, $this->currentObjectType . $property);
- }
-
- // @TODO: Clean up, this differates between 1:n and 1:1 references
- if (strpos($property, 'group') !== false) {
- $sourceIdentifier = $this->getMembers($object);
- foreach ($sourceIdentifier as $id) {
- $source = $this->icingaState[$this->currentObjectType][$id];
- if (!isset($source->$property)) {
- $source->$property = array();
- }
- $type = $this->currentObjectType;
- if (!isset($object->$type)) {
- $object->$type = array();
- }
- // add the member to the group object
- array_push($object->$type, $source);
- // add the group to the member object
- array_push($source->$property, $name);
- }
- } else {
- $source = $this->icingaState[$this->currentObjectType][$this->getObjectIdentifier($object)];
- if (!isset($source->$property)) {
- $source->$property = array();
- }
-
- array_push($source->$property, $object);
- }
-
- return null;
- }
-
- /**
- * Defer registration of the given object
- *
- * @param object $object The object to defer
- * @param String $objType The name of the object type
- */
- protected function deferRegistration($object, $objType)
- {
- $this->deferred[] = array($object, $objType);
- }
-
- /**
- * Process deferred objects
- */
- protected function processDeferred()
- {
- foreach ($this->deferred as $obj) {
- $this->currentObjectType = $obj[1];
- $this->registerObjectAsProperty($obj[0]);
- }
- }
-
- /**
- * Return the resolved members directive of an object
- *
- * @param object $object The object to get the members from
- * @return array An array of member names
- */
- protected function getMembers(&$object)
- {
- if (!isset($object->members)) {
- return array();
- }
-
- $members = explode(',', $object->members);
-
- if ($this->currentObjectType == 'service') {
- $res = array();
- for ($i = 0; $i < count($members); $i += 2) {
- $res[] = $members[$i] . ';' . $members[$i + 1];
- }
- return $res;
- } else {
- return $members;
- }
-
- }
-
- /**
- * Return the unique name of the given object
- *
- * @param object $object The object to retrieve the name from
- * @return string The name of the object or null if no name can be retrieved
- */
- protected function getObjectIdentifier(&$object)
- {
- if ($this->currentObjectType == 'contact') {
- return $object->contact_name;
- }
-
- if ($this->currentObjectType == 'service') {
- return $object->host_name . ';' . $object->service_description;
- }
- $name = $this->currentObjectType . '_name';
- if (isset($object->{$name})) {
- return $object->{$name};
- }
- if (isset($object->service_description)) {
- return $object->host_name . ';' . $object->service_description;
- } elseif (isset($object->host_name)) {
- return $object->host_name;
- }
- return null;
-
- }
-
- /**
- * Return the internal state of the parser
- *
- * @return null
- */
- public function getRuntimeState()
- {
- return $this->icingaState;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/PrintableObject.php b/library/Icinga/Protocol/Statusdat/PrintableObject.php
deleted file mode 100644
index 840a7aa2a..000000000
--- a/library/Icinga/Protocol/Statusdat/PrintableObject.php
+++ /dev/null
@@ -1,20 +0,0 @@
-contact_name)) {
- return $this->contact_name;
- } elseif (isset($this->service_description)) {
- return $this->service_description;
- } elseif (isset($this->host_name)) {
- return $this->host_name;
- }
- return '';
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/Query.php b/library/Icinga/Protocol/Statusdat/Query.php
deleted file mode 100644
index f3b5ea85e..000000000
--- a/library/Icinga/Protocol/Statusdat/Query.php
+++ /dev/null
@@ -1,460 +0,0 @@
- array('host'),
- 'services' => array('service'),
- 'downtimes' => array('downtime'),
- 'groups' => array('hostgroup', 'servicegroup'),
- 'hostgroups' => array('hostgroup'),
- 'servicegroups' => array('servicegroup'),
- 'comments' => array('comment'),
- 'contacts' => array('contact'),
- 'contactgroups' => array('contactgroup')
- );
-
- /**
- * The current StatusDat query that will be applied upon calling fetchAll
- *
- * @var IQueryPart
- */
- private $queryFilter = null;
-
- /**
- * The current query source being used
- *
- * @var string
- */
- private $source = '';
-
- /**
- * An array containing all columns used for sorting
- *
- * @var array
- */
- protected $orderColumns = array();
-
- /**
- * An array containig all columns used for (simple) grouping
- *
- * @var array
- */
- private $groupColumns = array();
-
- /**
- * An optional function callback to use for more specific grouping
- *
- * @var array
- */
- private $groupByFn = null;
-
- /**
- * The scope index for the callback function
- */
- const FN_SCOPE = 0;
-
- /**
- * The name index for the callback function
- */
- const FN_NAME = 1;
-
- /**
- * Return true if columns are set for this query
- *
- * @return bool
- */
- public function hasColumns()
- {
- $columns = $this->getColumns();
- return !empty($columns);
- }
-
- /**
- * Set the status.dat specific IQueryPart filter to use
- *
- * @param IQueryPart $filter
- */
- public function setQueryFilter($filter)
- {
- $this->queryFilter = $filter;
- }
-
- /**
- * Order the query result by the given columns
- *
- * @param String|array $columns An array of columns to order by
- * @param String $dir The direction (asc or desc) in string form
- *
- * @return $this Fluent interface
- */
- public function order($columns, $dir = null, $isFunction = false)
- {
- if ($dir && strtolower($dir) == 'desc') {
- $dir = self::SORT_DESC;
- } else {
- $dir = self::SORT_ASC;
- }
- if (!is_array($columns)) {
- $columns = array($columns);
- }
-
- foreach ($columns as $col) {
- if (($pos = strpos($col, ' ')) !== false) {
- $dir = strtoupper(substr($col, $pos + 1));
- if ($dir === 'DESC') {
- $dir = self::SORT_DESC;
- } else {
- $dir = self::SORT_ASC;
- }
- $col = substr($col, 0, $pos);
- } else {
- $col = $col;
- }
-
- $this->orderColumns[] = array($col, $dir);
- }
- return $this;
- }
-
- /**
- * Order the query result using the callback to retrieve values for items
- *
- * @param array $columns A scope, function array to use for retrieving the values when ordering
- * @param String $dir The direction (asc or desc) in string form
- *
- * @return $this Fluent interface
- */
- public function orderByFn(array $callBack, $dir = null)
- {
- if ($dir && strtolower($dir) == 'desc') {
- $dir = self::SORT_DESC;
- } else {
- $dir = self::SORT_ASC;
- }
- $this->orderColumns[] = array($callBack, $dir);
- }
-
-
-
- /**
- * Set the query target
- *
- * @param String $table The table/target to select the query from
- * @param array $columns An array of attributes to use (required for fetchPairs())
- *
- * @return $this Fluent interface
- * @throws IcingaException If the target is unknonw
- */
- public function from($table, array $attributes = null)
- {
- if (!$this->getColumns() && $attributes) {
- $this->setColumns($attributes);
- }
- if (isset(self::$VALID_TARGETS[$table])) {
- $this->source = $table;
- } else {
- throw new IcingaException(
- 'Unknown from target for status.dat :%s',
- $table
- );
- }
- return $this;
- }
-
- /**
- * Return an index of all objects matching the filter of this query
- *
- * This index will be used for ordering, grouping and limiting
- */
- private function getFilteredIndices($classType = '\Icinga\Protocol\Statusdat\Query\Group')
- {
- $baseGroup = $this->queryFilter;
- $state = $this->ds->getState();
- $result = array();
- $source = self::$VALID_TARGETS[$this->source];
-
- foreach ($source as $target) {
-
- if (! isset($state[$target])) {
- continue;
- }
-
- $indexes = array_keys($state[$target]);
- if ($baseGroup) {
- $baseGroup->setQuery($this);
- $idx = array_keys($state[$target]);
- $indexes = $baseGroup->filter($state[$target], $idx);
- }
- if (!isset($result[$target])) {
- $result[$target] = $indexes;
- } else {
- array_merge($result[$target], $indexes);
- }
- }
- return $result;
- }
-
- /**
- * Order the given result set
- *
- * @param array $indices The result set of the query that should be ordered
- */
- private function orderIndices(array &$indices)
- {
- if (!empty($this->orderColumns)) {
- foreach ($indices as $type => &$subindices) {
- $this->currentType = $type;
- usort($subindices, array($this, 'orderResult'));
- }
- }
- }
-
- /**
- * Start a query
- *
- * This is just a dummy function to allow a more convenient syntax
- *
- * @return self Fluent interface
- */
- public function select()
- {
- return $this;
- }
-
- /**
- * Order implementation called by usort
- *
- * @param String $a The left object index
- * @param Strinv $b The right object index
- * @return int 0, 1 or -1, see usort for detail
- */
- private function orderResult($a, $b)
- {
- $o1 = $this->ds->getObjectByName($this->currentType, $a);
- $o2 = $this->ds->getObjectByName($this->currentType, $b);
- $result = 0;
-
- foreach ($this->orderColumns as &$col) {
- if (is_array($col[0])) {
- // sort by function
- $result += $col[1] * strnatcasecmp(
- $col[0][0]->$col[0][1]($o1),
- $col[0][0]->$col[0][1]($o2)
- );
- } else {
- $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]});
- }
- }
- return $result;
- }
-
- /**
- * Limit the given resultset
- *
- * @param array $indices The filtered, ordered indices
- */
- private function limitIndices(array &$indices)
- {
- foreach ($indices as $type => $subindices) {
- $indices[$type] = array_slice($subindices, $this->getOffset(), $this->getLimit());
- }
- }
-
- /**
- * Register the given function for grouping the result
- *
- * @param String $fn The function to use for grouping
- * @param Object $scope An optional scope to use instead of $this
- *
- * @return self Fluent interface
- */
- public function groupByFunction($fn, $scope = null)
- {
- $this->groupByFn = array($scope ? $scope : $this, $fn);
- return $this;
- }
-
- /**
- * Group by the given column
- *
- * @param array|string $columns The columns to use for grouping
- * @return self Fluent interface
- * @see Query::columnGroupFn() The implementation used for grouping
- */
- public function groupByColumns($columns)
- {
- if (!is_array($columns)) {
- $columns = array($columns);
- }
- $this->groupColumns = $columns;
- $this->groupByFn = array($this, 'columnGroupFn');
- return $this;
- }
-
- /**
- * The internal handler function used by the group function
- *
- * @param array $indices The indices to group
- * @return array The grouped result set
- */
- private function columnGroupFn(array &$indices)
- {
- $cols = $this->groupColumns;
- $result = array();
- foreach ($indices as $type => $subindices) {
- foreach ($subindices as $objectIndex) {
- $r = $this->ds->getObjectByName($type, $objectIndex);
- $hash = '';
- $cols = array();
- foreach ($this->groupColumns as $col) {
- $hash = md5($hash . $r->$col);
- $cols[$col] = $r->$col;
- }
- if (!isset($result[$hash])) {
- $result[$hash] = (object)array(
- 'columns' => (object)$cols,
- 'count' => 0
- );
- }
- $result[$hash]->count++;
- }
- }
- return array_values($result);
- }
-
- /**
- * Query Filter, Order, Group, Limit and return the result set
- *
- * @return array The resultset matching this query
- */
- public function getResult()
- {
- $indices = $this->getFilteredIndices();
- $this->orderIndices($indices);
- if ($this->groupByFn) {
- $scope = $this->groupByFn[self::FN_SCOPE];
- $fn = $this->groupByFn[self::FN_NAME];
-
- return $scope->$fn($indices);
- }
-
- $this->limitIndices($indices);
-
- $result = array();
- $state = $this->ds->getState();
-
- foreach ($indices as $type => $subindices) {
- foreach ($subindices as $index) {
- $result[] = & $state[$type][$index];
- }
- }
- return $result;
- }
-
-
- /**
- * Apply all filters of this filterable on the datasource
- */
- public function applyFilter()
- {
- $parser = new TreeToStatusdatQueryParser();
- if ($this->getFilter()) {
- $query = $parser->treeToQuery($this->getFilter(), $this);
- $this->setQueryFilter($query);
- }
-
- }
-
- /**
- * Return only the first row fetched from the result set
- *
- * @return MonitoringObjectList The monitoring object matching this query
- */
- public function fetchRow()
- {
- $rs = $this->fetchAll();
- $rs->rewind();
- return $rs->current();
- }
-
- /**
- * Fetch the result as an associative array using the first column as the key and the second as the value
- *
- * @return array An associative array with the result
- * @throws IcingaException If no attributes are defined
- */
- public function fetchPairs()
- {
- $result = array();
- if (count($this->getColumns()) < 2) {
- throw new IcingaException(
- 'Status.dat "fetchPairs()" query expects at least columns to be set in the query expression'
- );
- }
- $attributes = $this->getColumns();
-
- $param1 = $attributes[0];
- $param2 = $attributes[1];
- foreach ($this->fetchAll() as $resultList) {
- $result[$resultList->$param1] = $resultList->$param2;
- }
-
- return $result;
- }
-
- /**
- * Fetch all results
- *
- * @return MonitoringObjectList An MonitoringObjectList wrapping the given resultset
- */
- public function fetchAll()
- {
- $this->applyFilter();
- if (!isset($this->cursor)) {
- $result = $this->getResult();
- $this->cursor = new MonitoringObjectList($result, $this);
- }
- return $this->cursor;
- }
-
- /**
- * Return the value of the first column for the first row fetched from the result set
- */
- public function fetchOne()
- {
- throw new ProgrammingError('Statusdat/Query::fetchOne() is not implemented yet');
- }
-
- /**
- * Count the number of results
- *
- * @return int
- */
- public function count()
- {
- $q = clone $this;
- $q->limit(null, null);
- return count($q->fetchAll());
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/Query/Expression.php b/library/Icinga/Protocol/Statusdat/Query/Expression.php
deleted file mode 100644
index 1db48df33..000000000
--- a/library/Icinga/Protocol/Statusdat/Query/Expression.php
+++ /dev/null
@@ -1,415 +0,0 @@
-":
- $this->CB = "isGreater";
- break;
- case "<":
- $this->CB = "isLess";
- break;
- case ">=":
- $this->CB = "isGreaterEq";
- break;
- case "<=":
- $this->CB = "isLessEq";
- break;
- case "=":
- $this->CB = "isEqual";
- break;
- case "LIKE":
- $this->CB = "isLike";
- break;
- case "NOT_LIKE":
- $this->CB = "isNotLike";
- break;
- case "!=":
- $this->CB = "isNotEqual";
- break;
- case "IN":
- $this->CB = "isIn";
- break;
- case "NOT_IN":
- $this->CB = "isNotIn";
- break;
- default:
- throw new IcingaException(
- 'Unknown operator %s in expression %s !',
- $token,
- $this->expression
- );
- }
- }
-
- /**
- * @param $tokens
- * @return mixed
- */
- private function extractAggregationFunction(&$tokens)
- {
- $token = $tokens[0];
- $value = array();
- if (preg_match("/COUNT\{(.*)\}/", $token, $value) == false) {
- return $token;
- }
- $this->function = "count";
- $tokens[0] = $value[1];
-
- return null;
- }
-
- /**
- * @param $values
- */
- private function parseExpression(&$values)
- {
- $tokenized = preg_split("/ +/", trim($this->expression), 3);
- $this->extractAggregationFunction($tokenized);
- if (count($tokenized) != 3) {
- echo(
- "Currently statusdat query expressions must be in "
- . "the format FIELD OPERATOR ? or FIELD OPERATOR :value_name"
- );
- }
-
- $this->fields = explode(".", trim($tokenized[0]));
- $this->field = $this->fields[count($this->fields) - 1];
-
- $this->getOperatorType(trim($tokenized[1]));
- $tokenized[2] = trim($tokenized[2]);
-
- if ($tokenized[2][0] === ":") {
- $this->name = substr($tokenized, 1);
- $this->value = $values[$this->name];
- } else {
- if ($tokenized[2] === "?") {
- $this->value = array_shift($values);
- } else {
- $this->value = trim($tokenized[2]);
- }
- }
-
- }
-
- /**
- * @param $expression
- * @param $values
- * @return $this
- */
- public function fromString($expression, &$values)
- {
- $this->expression = $expression;
- $this->parseExpression($values);
- return $this;
- }
-
- /**
- * @param null $expression
- * @param array $values
- */
- public function __construct($expression = null, &$values = array())
- {
- if ($expression) {
- if (!is_array($values)) {
- $values = array($values);
- }
- $this->fromString($expression, $values);
- }
-
- }
-
- /**
- * @param array $base
- * @param array $idx
- * @return array|mixed
- */
- public function filter(array &$base, &$idx = array())
- {
- if (!$idx) {
- $idx = array_keys($base);
- }
- $this->basedata = $base;
- return array_filter($idx, array($this, "filterFn"));
- }
-
- /**
- * @return string
- */
- public function getValue()
- {
- return $this->value;
- }
-
- /**
- * @return null
- */
- public function getField()
- {
- return $this->field;
- }
-
- /**
- * @param $idx
- * @return bool
- */
- protected function filterFn($idx)
- {
- $values = $this->getFieldValues($idx);
-
- if ($values === false) {
- return false;
- }
-
- if ($this->CB == "isIn" || $this->CB == "isNotIn") {
- $cmpValues = is_array($this->value) ? $this->value : array($this->value);
- foreach ($cmpValues as $cmpValue) {
- $this->value = $cmpValue;
- foreach ($values as $value) {
- if ($this->CB == "isIn" && $this->isLike($value)) {
- $this->value = $cmpValues;
- return true;
- } elseif ($this->CB == "isNotIn" && $this->isNotLike($value)) {
- $this->value = $cmpValues;
- return true;
- }
- }
- }
- $this->value = $cmpValues;
- return false;
- }
-
- if ($this->function) {
- $values = call_user_func($this->function, $values);
- if (!is_array($values)) {
- $values = array($values);
- }
- }
- foreach ($values as $val) {
-
- if (!is_string($val) && !is_numeric($val) && is_object($val)) {
- if (isset($val->service_description)) {
- $val = $val->service_description;
- } elseif (isset($val->host_name)) {
- $val = $val->host_name;
- } else {
- return false;
- }
- }
- if ($this->{$this->CB}($val)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * @param $idx
- * @return array
- */
- private function getFieldValues($idx)
- {
- $res = $this->basedata[$idx];
-
- foreach ($this->fields as $field) {
- if (!is_array($res)) {
- if ($this->query) {
- $res = $this->query->get($res, $field);
- continue;
- }
-
- if (!isset($res->$field)) {
- $res = array();
- break;
- }
- $res = $res->$field;
- continue;
- }
-
- // it can be that an element contains more than one value, like it
- // happens when using comments, in this case we have to create a new
- // array that contains the values/objects we're searching
- $swap = array();
- foreach ($res as $sub) {
- if ($this->query) {
- $swap[] = $this->query->get($sub, $field);
- continue;
- }
- if (!isset($sub->$field)) {
- continue;
- }
- if (!is_array($sub->$field)) {
- $swap[] = $sub->$field;
- } else {
- $swap = array_merge($swap, $sub->$field);
- }
- }
- $res = $swap;
- }
- if (!is_array($res)) {
- return array($res);
- }
-
- return $res;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isGreater($value)
- {
- return $value > $this->value;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isLess($value)
- {
- return $value < $this->value;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isLike($value)
- {
- return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isNotLike($value)
- {
- return !preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isEqual($value)
- {
- if (!is_numeric($value)) {
- return strtolower($value) == strtolower($this->value);
- }
- return $value == $this->value;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isNotEqual($value)
- {
- return $value != $this->value;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isGreaterEq($value)
- {
- return $value >= $this->value;
- }
-
- /**
- * @param $value
- * @return bool
- */
- public function isLessEq($value)
- {
- return $value <= $this->value;
- }
-
- /**
- * Add additional information about the query this filter belongs to
- *
- * @param $query
- * @return mixed
- */
- public function setQuery($query)
- {
- $this->query = $query;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/Query/Group.php b/library/Icinga/Protocol/Statusdat/Query/Group.php
deleted file mode 100644
index 2e110843f..000000000
--- a/library/Icinga/Protocol/Statusdat/Query/Group.php
+++ /dev/null
@@ -1,397 +0,0 @@
-value = $value;
- }
-
- /**
- * @return array
- */
- public function getItems()
- {
- return $this->items;
- }
-
- /**
- * @return string
- */
- public function getType()
- {
- return $this->type ? $this->type : self::TYPE_AND;
- }
-
- /**
- * @param $type
- */
- public function setType($type)
- {
- $this->type = $type;
- }
-
- /**
- * @throws IcingaException
- */
- private function tokenize()
- {
- $token = 0;
- $subgroupCount = 0;
- while ($token != self::EOF) {
-
- $token = $this->getNextToken();
-
- if ($token === self::GROUP_BEGIN) {
-
- /**
- * check if this is a nested group, if so then it's
- * considered part of the subexpression
- */
- if ($subgroupCount == 0) {
- $this->startNewSubExpression();
- }
- $subgroupCount++;
- continue;
- }
- if ($token === self::GROUP_END) {
- if ($subgroupCount < 1) {
- throw new IcingaException(
- 'Invalid Query: unexpected \')\' at pos %s',
- $this->parsePos
- );
- }
- $subgroupCount--;
- /*
- * check if this is a nested group, if so then it's
- * considered part of the subexpression
- */
- if ($subgroupCount == 0) {
- $this->addSubgroupFromExpression();
- }
- continue;
- }
-
- if ($token === self::CONJUNCTION_AND && $subgroupCount == 0) {
- $this->startNewSubExpression();
- if ($this->type != self::TYPE_AND && $this->type != "") {
- $this->createImplicitGroup(self::TYPE_AND);
- break;
- } else {
- $this->type = self::TYPE_AND;
- }
- continue;
- }
- if ($token === self::CONJUNCTION_OR && $subgroupCount == 0) {
- $this->startNewSubExpression();
- if ($this->type != self::TYPE_OR && $this->type != "") {
- $this->createImplicitGroup(self::TYPE_OR);
- break;
- } else {
-
- $this->type = self::TYPE_OR;
- }
- continue;
- }
-
- $this->subExpressionLength = $this->parsePos - $this->subExpressionStart;
- }
- if ($subgroupCount > 0) {
- throw new IcingaException('Unexpected end of query, are you missing a parenthesis?');
- }
-
- $this->startNewSubExpression();
- }
-
- /**
- * @param $type
- */
- private function createImplicitGroup($type)
- {
- $group = new Group();
- $group->setType($type);
- $group->addItem(array_pop($this->items));
-
- $group->fromString(substr($this->expression, $this->parsePos), $this->value, $this->expressionClass);
- $this->items[] = $group;
- $this->parsePos = strlen($this->expression);
-
- }
-
- /**
- *
- */
- private function startNewSubExpression()
- {
- if ($this->getCurrentSubExpression() != "") {
- if (!$this->expressionClass) {
- $this->items[] = new Expression($this->getCurrentSubExpression(), $this->value);
- } else {
- $this->items[] = new $this->expressionClass($this->getCurrentSubExpression(), $this->value);
- }
- }
-
- $this->subExpressionStart = $this->parsePos;
- $this->subExpressionLength = 0;
- }
-
- /**
- * @return string
- */
- private function getCurrentSubExpression()
- {
-
- return substr($this->expression, $this->subExpressionStart, $this->subExpressionLength);
- }
-
- /**
- *
- */
- private function addSubgroupFromExpression()
- {
-
- if (!$this->expressionClass) {
- $this->items[] = new Group($this->getCurrentSubExpression(), $this->value);
- } else {
- $group = new Group();
- $group->fromString($this->getCurrentSubExpression(), $this->value, $this->expressionClass);
- $this->items[] = $group;
- }
- $this->subExpressionStart = $this->parsePos;
- $this->subExpressionLength = 0;
- }
-
- /**
- * @return bool
- */
- private function isEOF()
- {
- if ($this->parsePos >= strlen($this->expression)) {
- return true;
- }
- return false;
- }
-
- /**
- * @return int|string
- */
- private function getNextToken()
- {
- if ($this->isEOF()) {
- return self::EOF;
- }
-
- // skip whitespaces
- while ($this->expression[$this->parsePos] == " ") {
- $this->parsePos++;
- if ($this->isEOF()) {
- return self::EOF;
- }
- }
- if ($this->expression[$this->parsePos] == self::GROUP_BEGIN) {
- $this->parsePos++;
- return self::GROUP_BEGIN;
- }
- if ($this->expression[$this->parsePos] == self::GROUP_END) {
- $this->parsePos++;
- return self::GROUP_END;
- }
- if (substr_compare(
- $this->expression,
- self::CONJUNCTION_AND,
- $this->parsePos,
- strlen(self::CONJUNCTION_AND),
- true
- ) === 0) {
- $this->parsePos += strlen(self::CONJUNCTION_AND);
- return self::CONJUNCTION_AND;
- }
- if (substr_compare(
- $this->expression,
- self::CONJUNCTION_OR,
- $this->parsePos,
- strlen(self::CONJUNCTION_OR),
- true
- ) === 0) {
- $this->parsePos += strlen(self::CONJUNCTION_OR);
- return self::CONJUNCTION_OR;
- }
- $this->parsePos++;
- return self::EXPRESSION;
- }
-
- /**
- * @param $ex
- * @return $this
- */
- public function addItem($ex)
- {
- $this->items[] = $ex;
- return $this;
- }
-
- /**
- * @param $expression
- * @param array $value
- * @param null $expressionClass
- * @return $this
- */
- public function fromString($expression, &$value = array(), $expressionClass = null)
- {
- $this->expression = $expression;
- $this->value = & $value;
- $this->expressionClass = $expressionClass;
-
- $this->tokenize();
- return $this;
- }
-
- /**
- * @param null $expression
- * @param array $value
- */
- public function __construct($expression = null, &$value = array())
- {
- if ($expression) {
- $this->fromString($expression, $value);
- }
- }
-
- /**
- * @param array $base
- * @param null $idx
- * @return array|null
- */
- public function filter(array &$base, &$idx = null)
- {
- if ($this->type == self::TYPE_OR) {
- $idx = array();
- foreach ($this->items as &$subFilter) {
- $baseKeys = array_keys($base);
- $subFilter->setQuery($this->query);
- $idx += $subFilter->filter($base, $baseKeys);
- }
- } else {
- if (!$idx) {
- $idx = array_keys($base);
- }
- foreach ($this->items as $subFilter) {
- $subFilter->setQuery($this->query);
- $idx = array_intersect($idx, $subFilter->filter($base, $idx));
- }
- }
-
- return $idx;
- }
-
- /**
- * Add additional information about the query this filter belongs to
- *
- * @param $query
- * @return mixed
- */
- public function setQuery($query)
- {
- $this->query = $query;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php b/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php
deleted file mode 100644
index 1dcd434df..000000000
--- a/library/Icinga/Protocol/Statusdat/Query/IQueryPart.php
+++ /dev/null
@@ -1,36 +0,0 @@
-noCache = $noCache;
- if (isset($config->no_cache)) {
- $this->noCache = $config->no_cache;
- }
- $this->config = $config;
- $this->parser = $parser;
-
- if (!$this->noCache) {
- $this->cache = $this->initializeCaches($config);
- if ($this->fromCache()) {
- $this->createHostServiceConnections();
- return;
- }
- }
-
- if (!$this->lastState) {
- $this->parseObjectsCacheFile();
- }
- if (!$this->hasRuntimeState) {
-
- }
- $this->parseStatusDatFile();
- if (!$noCache && $this->newState) {
- $this->statusCache->save($this->parser->getRuntimeState(), 'object' . md5($this->config->object_file));
- }
- $this->createHostServiceConnections();
-
- }
-
- /**
- * Initialize the internal caches if enabled
- *
- * @throws ConfigurationError
- */
- private function initializeCaches()
- {
- $defaultCachePath = self::STATUSDAT_DEFAULT_CACHE_PATH;
- $cachePath = $this->config->get('cache_path', $defaultCachePath);
- $maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME));
- $cachingEnabled = true;
- if (!is_writeable($cachePath)) {
- Logger::warning(
- 'Can\'t cache Status.dat backend; make sure cachepath %s is writable by the web user. '
- . 'Caching is now disabled',
- $cachePath
- );
- $cachePath = null;
- }
- $backendOptions = array(
- 'cache_dir' => $cachePath
- );
- // the object cache might exist for months and is still valid
- $this->objectCache = $this->initCache($this->config->object_file, $backendOptions, null, $cachingEnabled);
- $this->statusCache = $this->initCache(
- $this->config->status_file,
- $backendOptions,
- $maxCacheLifetime,
- $cachingEnabled
- );
- }
-
- /**
- * Init the Cache backend in Zend
- *
- * @param String $file The file to use as the cache master file
- * @param Zend_Config $backend The backend configuration to use
- * @param integer $lifetime The lifetime of the cache
- *
- * @return \Zend_Cache_Core|\Zend_Cache_Frontend
- */
- private function initCache($file, $backendConfig, $lifetime)
- {
- $frontendOptions = array(
- 'lifetime' => $lifetime,
- 'automatic_serialization' => true,
- 'master_files' => array($file)
- );
- return \Zend_Cache::factory('Core', 'File', $frontendOptions, $backendConfig);
- }
-
- /**
- * Read the current cache state
- *
- * @return bool True if the state is the same as the icinga state
- */
- private function fromCache()
- {
- if (!$this->readObjectsCache()) {
- $this->newState = true;
- return false;
- }
- if (!$this->readStatusCache()) {
- $this->newState = true;
- return false;
- }
- return true;
- }
-
- /**
- * Read the object.cache file from the Zend_Cache backend
- *
- * @return bool True if the file could be loaded from cache
- */
- private function readObjectsCache()
- {
- $this->lastState = $this->objectCache->load('object' . md5($this->config->object_file));
- if ($this->lastState == false) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Read the status.dat file from the Zend_Cache backend
- *
- * @return bool True if the file could be loaded from cache
- */
- private function readStatusCache()
- {
- if (!isset($this->stateCache)) {
- return true;
- }
- $statusInfo = $this->stateCache->load('state' . md5($this->config->status_file));
- if ($statusInfo == false) {
- return false;
- }
-
- $this->hasRuntimeState = true;
- return true;
- }
-
- /**
- * Take the status.dat and objects.cache and connect all services to hosts
- *
- */
- private function createHostServiceConnections()
- {
- if (!isset($this->lastState["service"])) {
- return;
- }
- foreach ($this->lastState["host"] as &$host) {
- $host->host = $host;
- }
- foreach ($this->lastState["service"] as &$service) {
- $service->service = &$service; // allow easier querying
- $host = &$this->lastState["host"][$service->host_name];
- if (!isset($host->services)) {
- $host->services = array();
- }
- $host->services[$service->service_description] = & $service;
- $service->host = & $host;
- }
- }
-
- /**
- * Parse the object.cache file and update the current state
- *
- * @throws ConfigurationError If the object.cache couldn't be read
- */
- private function parseObjectsCacheFile()
- {
- if (!is_readable($this->config->object_file)) {
- throw new ConfigurationError(
- 'Can\'t read object-file "%s", check your configuration',
- $this->config->object_file
- );
- }
- if (!$this->parser) {
- $this->parser = new Parser(new File($this->config->object_file, 'r'));
- }
- $this->parser->parseObjectsFile();
- $this->lastState = $this->parser->getRuntimeState();
- }
-
- /**
- * Parse the status.dat file and update the current state
- *
- * @throws ConfigurationError If the status.dat couldn't be read
- */
- private function parseStatusDatFile()
- {
- if (!is_readable($this->config->status_file)) {
- throw new ConfigurationError(
- 'Can\'t read status-file %s, check your configuration',
- $this->config->status_file
- );
- }
- if (!$this->parser) {
- $this->parser = new Parser(new File($this->config->status_file, 'r'), $this->lastState);
- }
- $this->parser->parseRuntimeState(new File($this->config->status_file, 'r'));
- $this->lastState = $this->parser->getRuntimeState();
- if (!$this->noCache) {
- $this->statusCache->save(array("true" => true), "state" . md5($this->config->object_file));
- }
- }
-
- /**
- * Create a new Query
- *
- * @return Query The query to operate on
- */
- public function select()
- {
- return new Query($this);
- }
-
- /**
- * Return the internal state of the status.dat
- *
- * @return mixed The internal status.dat representation
- */
- public function getState()
- {
- return $this->lastState;
- }
-
-
- /**
- * Return the object with the given name and type
- *
- * @param String $type The type of the object to return (service, host, servicegroup...)
- * @param String $name The name of the object
- *
- * @return ObjectContainer An object container wrapping the result or null if the object doesn't exist
- */
- public function getObjectByName($type, $name)
- {
- if (isset($this->lastState[$type]) && isset($this->lastState[$type][$name])) {
- return new ObjectContainer($this->lastState[$type][$name], $this);
- }
- return null;
- }
-
- /**
- * Get an array containing all names of monitoring objects with the given type
- *
- * @param String $type The type of object to get the names for
- * @return array An array of names or null if the type does not exist
- */
- public function getObjectNames($type)
- {
- return isset($this->lastState[$type]) ? array_keys($this->lastState[$type]) : null;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php b/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php
deleted file mode 100644
index b15dfe4bb..000000000
--- a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php
+++ /dev/null
@@ -1,72 +0,0 @@
-runtimeState = $str;
- }
-
- /**
- * Return true if the argument exists
- *
- * @param String $attr The argument to retrieve
- * @return bool True if it exists, otherwise false
- */
- public function __isset($attr)
- {
- try {
- $this->__get($attr);
- return true;
- } catch (\InvalidArgumentException $e) {
- return false;
- }
- }
-
- /**
- * Return the given attribute
- *
- * If the container string is not yet parsed, this will happen here
- *
- * @param String $attr The attribute to retrieve
- * @return mixed The value of the attribute
- * @throws \InvalidArgumentException When the attribute does not exist
- */
- public function __get($attr)
- {
- $start = strpos($this->runtimeState, $attr . "=");
- if ($start === false) {
- throw new \InvalidArgumentException("Unknown property $attr");
- }
-
- $start += strlen($attr . "=");
- $len = strpos($this->runtimeState, "\n", $start) - $start;
- $this->$attr = trim(substr($this->runtimeState, $start, $len));
-
- return $this->$attr;
- }
-}
diff --git a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php b/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php
deleted file mode 100644
index 4e4bc76a1..000000000
--- a/library/Icinga/Protocol/Statusdat/View/AccessorStrategy.php
+++ /dev/null
@@ -1,42 +0,0 @@
-dataSet = $dataset;
- $this->position = 0;
- $this->dataView = $dataView;
- }
-
- public function count()
- {
- return count($this->dataSet);
- }
-
- public function setPosition($pos)
- {
- $this->position = $pos;
- }
-
- /**
- * (PHP 5 >= 5.0.0)
- * Return the current element
- * @link http://php.net/manual/en/iterator.current.php
- * @return mixed Can return any type.
- */
- public function current()
- {
- if ($this->dataView) {
- return $this;
- }
-
- return $this->dataSet[$this->position];
- }
-
- /**
- * (PHP 5 >= 5.0.0)
- * Move forward to next element
- * @link http://php.net/manual/en/iterator.next.php
- * @return void Any returned value is ignored.
- */
- public function next()
- {
- $this->position++;
- }
-
- /**
- * (PHP 5 >= 5.0.0)
- * Return the key of the current element
- * @link http://php.net/manual/en/iterator.key.php
- * @return mixed scalar on success, or null on failure.
- */
- public function key()
- {
- return $this->position;
- }
-
- /**
- * (PHP 5 >= 5.0.0)
- * Checks if current position is valid
- * @link http://php.net/manual/en/iterator.valid.php
- * @return boolean The return value will be casted to boolean and then evaluated.
- * Returns true on success or false on failure.
- */
- public function valid()
- {
- return $this->position < count($this->dataSet);
- }
-
- /**
- * (PHP 5 >= 5.0.0)
- * Rewind the Iterator to the first element
- * @link http://php.net/manual/en/iterator.rewind.php
- * @return void Any returned value is ignored.
- */
- public function rewind()
- {
- $this->position = 0;
- }
-
- public function __isset($name)
- {
- return $this->dataView->exists($this->dataSet[$this->position], $name);
- }
-
- public function __get($name)
- {
- return $this->dataView->get($this->dataSet[$this->position], $name);
- }
-
- public function __set($name, $value)
- {
- throw new IcingaException('Setting is currently not available for objects');
- }
-
- public function offsetExists($offset)
- {
- return count($this->dataSet) < $offset;
- }
-
- public function offsetGet($offset)
- {
- $res = new MonitoringObjectList($this->dataSet, $this->dataView);
- $res->position = $offset;
- return $res;
- }
-
- public function offsetSet($offset, $value)
- {
- // non mutable
- }
-
- public function offsetUnset($offset)
- {
- // non mutable
- }
-}
diff --git a/library/Icinga/Test/BaseTestCase.php b/library/Icinga/Test/BaseTestCase.php
index b72bdee62..a735550cb 100644
--- a/library/Icinga/Test/BaseTestCase.php
+++ b/library/Icinga/Test/BaseTestCase.php
@@ -25,20 +25,16 @@ namespace Icinga\Test {
use RuntimeException;
use Mockery;
use Zend_Config;
- use Zend_Controller_Request_Abstract;
- use Zend_Controller_Request_HttpTestCase;
use PHPUnit_Framework_TestCase;
use Icinga\Application\Icinga;
use Icinga\Util\DateTimeFactory;
use Icinga\Data\ResourceFactory;
use Icinga\Data\Db\DbConnection;
- use Icinga\User\Preferences;
- use Icinga\Web\Form;
/**
* Class BaseTestCase
*/
- class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest, FormTest
+ class BaseTestCase extends PHPUnit_Framework_TestCase implements DbTest
{
/**
* Path to application/
@@ -82,13 +78,6 @@ namespace Icinga\Test {
*/
public static $moduleDir;
- /**
- * Store request for form tests
- *
- * @var Zend_Controller_Request_HttpTestCase
- */
- private $request;
-
/**
* Resource configuration for different database types
*
@@ -151,28 +140,54 @@ namespace Icinga\Test {
public function setUp()
{
parent::setUp();
-
- $requestMock = Mockery::mock('Icinga\Web\Request');
- $requestMock->shouldReceive('getPathInfo')->andReturn('')
- ->shouldReceive('getBaseUrl')->andReturn('/')
- ->shouldReceive('getQuery')->andReturn(array());
- $this->setupIcingaMock($requestMock);
+ $this->setupIcingaMock();
}
/**
* Setup mock object for the application's bootstrap
*
- * @param Zend_Controller_Request_Abstract $request The request to be returned by
- * Icinga::app()->getFrontController()->getRequest()
+ * @return Mockery\Mock
*/
- protected function setupIcingaMock(Zend_Controller_Request_Abstract $request)
+ protected function setupIcingaMock()
{
+ $requestMock = Mockery::mock('Icinga\Web\Request')->shouldDeferMissing();
+ $requestMock->shouldReceive('getPathInfo')->andReturn('')->byDefault()
+ ->shouldReceive('getBaseUrl')->andReturn('/')->byDefault()
+ ->shouldReceive('getQuery')->andReturn(array())->byDefault()
+ ->shouldReceive('getParam')->with(Mockery::type('string'), Mockery::type('string'))
+ ->andReturnUsing(function ($name, $default) { return $default; })->byDefault();
+
+ $responseMock = Mockery::mock('Icinga\Web\Response')->shouldDeferMissing();
+
+ // Can't express this as demeter chains. See: https://github.com/padraic/mockery/issues/59
$bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing();
- $bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing(
- function () use ($request) { return $request; }
- )->shouldReceive('getApplicationDir')->andReturn(self::$appDir);
+ $bootstrapMock->shouldReceive('getFrontController')->andReturn($bootstrapMock)
+ ->shouldReceive('getApplicationDir')->andReturn(self::$appDir)
+ ->shouldReceive('getRequest')->andReturn($requestMock)
+ ->shouldReceive('getResponse')->andReturn($responseMock);
Icinga::setApp($bootstrapMock, true);
+ return $bootstrapMock;
+ }
+
+ /**
+ * Return the currently active request mock object
+ *
+ * @return Icinga\Web\Request
+ */
+ public function getRequestMock()
+ {
+ return Icinga::app()->getFrontController()->getRequest();
+ }
+
+ /**
+ * Return the currently active response mock object
+ *
+ * @return Icinga\Web\Response
+ */
+ public function getResponseMock()
+ {
+ return Icinga::app()->getFrontController()->getResponse();
}
/**
@@ -298,58 +313,6 @@ namespace Icinga\Test {
$adapter->exec('DROP TABLE ' . $table . ';');
}
}
-
- /**
- * Instantiate a form
- *
- * If the form has CSRF protection enabled, creates the form's token element and adds the generated token to the
- * request data
- *
- * @param string $formClass Qualified class name of the form to create. Note that the class has to be
- * defined as no attempt is made to require the class before instantiating.
- * @param array $requestData Request data for the form
- *
- * @return Form
- * @throws RuntimeException
- */
- public function createForm($formClass, array $requestData = array())
- {
- $form = new $formClass;
- // If the form has CSRF protection enabled, add the token to the request data, else all calls to
- // isSubmittedAndValid will fail
- $form->setSessionId('1234');
- $form->initCsrfToken();
- $token = $form->getValue($form->getTokenElementName());
- if ($token !== null) {
- $requestData[$form->getTokenElementName()] = $token;
- }
- $request = $this->getRequest();
- $request->setMethod('POST');
- $request->setPost($requestData);
- $form->setRequest($request);
- $form->setUserPreferences(
- new Preferences(
- array()
- )
- );
- return $form;
- }
-
- /**
- * Retrieve test case request object
- *
- * This is a mock methods borrowed from Zend Controller Test Case to handle form tests properly (#6106)
- *
- * @return Zend_Controller_Request_HttpTestCase
- */
- public function getRequest()
- {
- if (null === $this->request) {
- require_once 'Zend/Controller/Request/HttpTestCase.php';
- $this->request = new Zend_Controller_Request_HttpTestCase;
- }
- return $this->request;
- }
}
BaseTestCase::setupTimezone();
diff --git a/library/Icinga/Test/FormTest.php b/library/Icinga/Test/FormTest.php
deleted file mode 100644
index e8b070483..000000000
--- a/library/Icinga/Test/FormTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-permissions = $permissions;
+ natcasesort($permissions);
+ if (! empty($permissions)) {
+ $this->permissions = array_combine($permissions, $permissions);
+ }
+ return $this;
}
/**
@@ -382,38 +380,6 @@ class User
return new DateTimeZone($tz);
}
- /**
- * Add a message that can be accessed from future requests, to this user.
- *
- * This function does NOT automatically write to the session, messages will not be persisted until you do.
- *
- * @param Message $msg The message
- */
- public function addMessage(Message $msg)
- {
- $this->messages[] = $msg;
- }
-
- /**
- * Get all currently pending messages
- *
- * @return array The messages
- */
- public function getMessages()
- {
- return isset($this->messages) ? $this->messages : array();
- }
-
- /**
- * Remove all messages from this user
- *
- * This function does NOT automatically write the session, messages will not be persisted until you do.
- */
- public function clearMessages()
- {
- $this->messages = null;
- }
-
/**
* Set additional remote user information
*
@@ -442,6 +408,33 @@ class User
*/
public function isRemoteUser()
{
- return (count($this->remoteUserInformation)) ? true : false;
+ return ! empty($this->remoteUserInformation);
+ }
+
+ /**
+ * Whether the user has a given permission
+ *
+ * @param string $permission
+ *
+ * @return bool
+ */
+ public function can($permission)
+ {
+ if (isset($this->permissions['*']) || isset($this->permissions[$permission])) {
+ return true;
+ }
+ foreach ($this->permissions as $permitted) {
+ $wildcard = strpos($permitted, '*');
+ if ($wildcard !== false) {
+ if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) {
+ return true;
+ } else {
+ if ($permission === $permitted) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
}
diff --git a/library/Icinga/User/Message.php b/library/Icinga/User/Message.php
deleted file mode 100644
index 83d780563..000000000
--- a/library/Icinga/User/Message.php
+++ /dev/null
@@ -1,59 +0,0 @@
-message = $message;
- $this->level = $level;
- }
-
- /**
- * @return string
- */
- public function getMessage()
- {
- return $this->message;
- }
-
- /**
- * @return The
- */
- public function getLevel()
- {
- return $this->level;
- }
-}
diff --git a/library/Icinga/User/Preferences.php b/library/Icinga/User/Preferences.php
index b1dae867a..364112a9e 100644
--- a/library/Icinga/User/Preferences.php
+++ b/library/Icinga/User/Preferences.php
@@ -79,19 +79,38 @@ class Preferences implements Countable
}
/**
- * Retrieve a preference and return $default if the preference is not set
+ * Retrieve a preference section
*
* @param string $name
- * @param mixed $default
*
- * @return mixed
+ * @return array|null
*/
- public function get($name, $default = null)
+ public function get($name)
{
if (array_key_exists($name, $this->preferences)) {
return $this->preferences[$name];
}
+ return null;
+ }
+
+ /**
+ * Retrieve a value from a specific section
+ *
+ * @param string $section
+ * @param string $name
+ * @param null $default
+ *
+ * @return array|null
+ */
+ public function getValue($section, $name, $default = null)
+ {
+ if (array_key_exists($section, $this->preferences)
+ && array_key_exists($name, $this->preferences[$section])
+ ) {
+ return $this->preferences[$section][$name];
+ }
+
return $default;
}
diff --git a/library/Icinga/Util/DateTimeFactory.php b/library/Icinga/Util/DateTimeFactory.php
index 724b54770..4a482dc0a 100644
--- a/library/Icinga/Util/DateTimeFactory.php
+++ b/library/Icinga/Util/DateTimeFactory.php
@@ -76,4 +76,18 @@ class DateTimeFactory implements ConfigAwareFactory
{
return new DateTime($time, $timeZone !== null ? $timeZone : self::$timeZone);
}
+
+ /**
+ * Check whether a variable is a Unix timestamp
+ *
+ * @param mixed $timestamp
+ *
+ * @return bool
+ */
+ public static function isUnixTimestamp($timestamp)
+ {
+ return (is_int($timestamp) || ctype_digit($timestamp))
+ && ($timestamp <= PHP_INT_MAX)
+ && ($timestamp >= ~PHP_INT_MAX);
+ }
}
diff --git a/library/Icinga/Util/Enumerate.php b/library/Icinga/Util/Enumerate.php
deleted file mode 100644
index 0861f7f65..000000000
--- a/library/Icinga/Util/Enumerate.php
+++ /dev/null
@@ -1,62 +0,0 @@
-iterator = $iterator;
- }
-
- public function rewind()
- {
- $this->iterator->rewind();
- $this->key = 0;
- }
-
- public function next()
- {
- $this->iterator->next();
- ++$this->key;
- }
-
- public function valid()
- {
- return $this->iterator->valid();
- }
-
- public function current()
- {
- return $this->iterator->current();
- }
-
- public function key()
- {
- return $this->key;
- }
-}
diff --git a/library/Icinga/Util/EnumeratingFilterIterator.php b/library/Icinga/Util/EnumeratingFilterIterator.php
new file mode 100644
index 000000000..44ef9b0c8
--- /dev/null
+++ b/library/Icinga/Util/EnumeratingFilterIterator.php
@@ -0,0 +1,37 @@
+index = 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function key()
+ {
+ return $this->index++;
+ }
+}
diff --git a/library/Icinga/Util/File.php b/library/Icinga/Util/File.php
index b7aff9743..9ce1ec893 100644
--- a/library/Icinga/Util/File.php
+++ b/library/Icinga/Util/File.php
@@ -131,7 +131,7 @@ class File extends SplFileObject
set_error_handler(
function ($errno, $errstr, $errfile, $errline) {
restore_error_handler();
- throw new ErrorException($errno, $errstr, $errfile, $errline);
+ throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
},
E_WARNING
);
diff --git a/library/Icinga/Util/String.php b/library/Icinga/Util/String.php
index b64d9e524..0bebb06e7 100644
--- a/library/Icinga/Util/String.php
+++ b/library/Icinga/Util/String.php
@@ -21,4 +21,18 @@ class String
{
return array_map('trim', explode($delimiter, $value));
}
+
+ /**
+ * Uppercase the first character of each word in a string assuming and removing the underscore as word separator
+ *
+ * Converts 'first_name' to 'firstName' for example.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public static function cname($name)
+ {
+ return str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name))));
+ }
}
diff --git a/library/Icinga/Util/Translator.php b/library/Icinga/Util/Translator.php
index b6cf533d9..755203474 100644
--- a/library/Icinga/Util/Translator.php
+++ b/library/Icinga/Util/Translator.php
@@ -34,13 +34,22 @@ class Translator
*
* Falls back to the default domain in case the string cannot be translated using the given domain
*
- * @param string $text The string to translate
- * @param string $domain The primary domain to use
+ * @param string $text The string to translate
+ * @param string $domain The primary domain to use
+ * @param string|null $context Optional parameter for context based translation
*
- * @return string The translated string
+ * @return string The translated string
*/
- public static function translate($text, $domain)
+ public static function translate($text, $domain, $context = null)
{
+ if ($context !== null) {
+ $res = self::pgettext($text, $domain, $context);
+ if ($res === $text && $domain !== self::DEFAULT_DOMAIN) {
+ $res = self::pgettext($text, self::DEFAULT_DOMAIN, $context);
+ }
+ return $res;
+ }
+
$res = dgettext($domain, $text);
if ($res === $text && $domain !== self::DEFAULT_DOMAIN) {
return dgettext(self::DEFAULT_DOMAIN, $text);
@@ -48,6 +57,86 @@ class Translator
return $res;
}
+ /**
+ * Translate a plural string
+ *
+ * Falls back to the default domain in case the string cannot be translated using the given domain
+ *
+ * @param string $textSingular The string in singular form to translate
+ * @param string $textPlural The string in plural form to translate
+ * @param integer $number The number to get the plural or singular string
+ * @param string $domain The primary domain to use
+ * @param string|null $context Optional parameter for context based translation
+ *
+ * @return string The translated string
+ */
+ public static function translatePlural($textSingular, $textPlural, $number, $domain, $context = null)
+ {
+ if ($context !== null) {
+ $res = self::pngettext($textSingular, $textPlural, $number, $domain, $context);
+ if (($res === $textSingular || $res === $textPlural) && $domain !== self::DEFAULT_DOMAIN) {
+ $res = self::pngettext($textSingular, $textPlural, $number, self::DEFAULT_DOMAIN, $context);
+ }
+ return $res;
+ }
+
+ $res = dngettext($domain, $textSingular, $textPlural, $number);
+ if (($res === $textSingular || $res === $textPlural) && $domain !== self::DEFAULT_DOMAIN) {
+ $res = dngettext(self::DEFAULT_DOMAIN, $textSingular, $textPlural, $number);
+ }
+ return $res;
+ }
+
+ /**
+ * Emulated pgettext()
+ *
+ * @link http://php.net/manual/de/book.gettext.php#89975
+ *
+ * @param $text
+ * @param $domain
+ * @param $context
+ *
+ * @return string
+ */
+ public static function pgettext($text, $domain, $context)
+ {
+ $contextString = "{$context}\004{$text}";
+
+ $translation = dcgettext($domain, $contextString, LC_MESSAGES);
+
+ if ($translation == $contextString) {
+ return $text;
+ } else {
+ return $translation;
+ }
+ }
+
+ /**
+ * Emulated pngettext()
+ *
+ * @link http://php.net/manual/de/book.gettext.php#89975
+ *
+ * @param $textSingular
+ * @param $textPlural
+ * @param $number
+ * @param $domain
+ * @param $context
+ *
+ * @return string
+ */
+ public static function pngettext($textSingular, $textPlural, $number, $domain, $context)
+ {
+ $contextString = "{$context}\004{$textSingular}";
+
+ $translation = dcngettext($domain, $contextString, $textPlural, $number, LC_MESSAGES);
+
+ if ($translation == $contextString || $translation == $textPlural) {
+ return ($number == 1 ? $textSingular : $textPlural);
+ } else {
+ return $translation;
+ }
+ }
+
/**
* Register a new gettext domain
*
diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php
index aead4043a..315209232 100644
--- a/library/Icinga/Web/Controller/ActionController.php
+++ b/library/Icinga/Web/Controller/ActionController.php
@@ -39,10 +39,6 @@ class ActionController extends Zend_Controller_Action
*/
protected $requiresAuthentication = true;
- private $config;
-
- private $configs = array();
-
private $autorefreshInterval;
private $reloadCss = false;
@@ -91,23 +87,29 @@ class ActionController extends Zend_Controller_Action
}
$this->view->tabs = new Tabs();
+ $this->prepareInit();
$this->init();
}
+ /**
+ * Prepare controller initialization
+ *
+ * As it should not be required for controllers to call the parent's init() method, base controllers should use
+ * prepareInit() in order to prepare the controller initialization.
+ *
+ * @see \Zend_Controller_Action::init() For the controller initialization method.
+ */
+ protected function prepareInit()
+ {
+ }
+
public function Config($file = null)
{
if ($file === null) {
- if ($this->config === null) {
- $this->config = Config::app();
- }
- return $this->config;
+ return Config::app();
} else {
- if (! array_key_exists($file, $this->configs)) {
- $this->configs[$file] = Config::module($module, $file);
- }
- return $this->configs[$file];
+ return Config::app($file);
}
- return $this->config;
}
public function Auth()
@@ -216,13 +218,29 @@ class ActionController extends Zend_Controller_Action
*
* Autoselects the module domain, if any, and falls back to the global one if no translation could be found.
*
- * @param string $text The string to translate
+ * @param string $text The string to translate
+ * @param string|null $context Optional parameter for context based translation
*
- * @return string The translated string
+ * @return string The translated string
*/
- public function translate($text)
+ public function translate($text, $context = null)
{
- return Translator::translate($text, $this->view->translationDomain);
+ return Translator::translate($text, $this->view->translationDomain, $context);
+ }
+
+ /**
+ * Translate a plural string
+ *
+ * @param string $textSingular The string in singular form to translate
+ * @param string $textPlural The string in plural form to translate
+ * @param string $number The number to get the plural or singular string
+ * @param string|null $context Optional parameter for context based translation
+ *
+ * @return string The translated string
+ */
+ public function translatePlural($textSingular, $textPlural, $number, $context = null)
+ {
+ return Translator::translatePlural($textSingular, $textPlural, $number, $this->view->translationDomain, $context);
}
protected function ignoreXhrBody()
@@ -347,7 +365,7 @@ class ActionController extends Zend_Controller_Action
if ($user = $req->getUser()) {
// Cast preference app.show_benchmark to bool because preferences loaded from a preferences storage are
// always strings
- if ((bool) $user->getPreferences()->get('app.show_benchmark', false) === true) {
+ if ((bool) $user->getPreferences()->getValue('icingaweb', 'show_benchmark', false) === true) {
if (!$this->_helper->viewRenderer->getNoRender()) {
$layout->benchmark = $this->renderBenchmark();
}
diff --git a/library/Icinga/Web/Controller/BaseConfigController.php b/library/Icinga/Web/Controller/BaseConfigController.php
deleted file mode 100644
index 98941686e..000000000
--- a/library/Icinga/Web/Controller/BaseConfigController.php
+++ /dev/null
@@ -1,61 +0,0 @@
-getUser()->addMessage(
- new Message($msg, Zend_Log::INFO)
- );
- Session::getSession()->write();
- }
-
- /**
- * Send a message with the logging level Zend_Log::ERR to the current user and
- * commit the changes to the underlying session.
- *
- * @param $msg The message content
- */
- protected function addErrorMessage($msg)
- {
- AuthenticationManager::getInstance()->getUser()->addMessage(
- new Message($msg, Zend_Log::ERR)
- );
- Session::getSession()->write();
- }
-
- /*
- * Return an array of tabs provided by this configuration controller.
- *
- * Those tabs will automatically be added to the application's configuration dialog
- *
- * @return array
- */
- public static function createProvidedTabs()
- {
- return array();
- }
-}
diff --git a/library/Icinga/Web/Controller/BasePreferenceController.php b/library/Icinga/Web/Controller/BasePreferenceController.php
index a83b8e6c0..f7d45aa5f 100644
--- a/library/Icinga/Web/Controller/BasePreferenceController.php
+++ b/library/Icinga/Web/Controller/BasePreferenceController.php
@@ -4,11 +4,6 @@
namespace Icinga\Web\Controller;
-use Icinga\Application\Config as IcingaConfig;
-use Icinga\Exception\ConfigurationError;
-use Icinga\Web\Session;
-use Icinga\User\Preferences\PreferencesStore;
-
/**
* Base class for Preference Controllers
*
@@ -42,27 +37,4 @@ class BasePreferenceController extends ActionController
parent::init();
$this->view->tabs = ControllerTabCollector::collectControllerTabs('PreferenceController');
}
-
- protected function savePreferences(array $preferences)
- {
- $session = Session::getSession();
- $currentPreferences = $session->user->getPreferences();
- foreach ($preferences as $key => $value) {
- if ($value === null) {
- $currentPreferences->remove($key);
- } else {
- $currentPreferences->{$key} = $value;
- }
- }
- $session->write();
-
- if (($preferencesConfig = IcingaConfig::app()->preferences) === null) {
- throw new ConfigurationError(
- 'Cannot save preferences changes since you\'ve not configured a preferences backend'
- );
- }
- $store = PreferencesStore::create($preferencesConfig, $session->user);
- $store->load(); // Necessary for patching existing preferences
- $store->save($currentPreferences);
- }
}
diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php
index 55663123b..0f540c2a0 100644
--- a/library/Icinga/Web/Controller/ModuleActionController.php
+++ b/library/Icinga/Web/Controller/ModuleActionController.php
@@ -6,9 +6,10 @@ namespace Icinga\Web\Controller;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
-use Zend_Controller_Request_Abstract as Request;
-use Zend_Controller_Response_Abstract as Response;
+/**
+ * Base class for module action controllers
+ */
class ModuleActionController extends ActionController
{
private $config;
@@ -17,34 +18,42 @@ class ModuleActionController extends ActionController
private $module;
+ /**
+ * Module name
+ *
+ * @var string
+ */
protected $moduleName;
- public function __construct(
- Request $request,
- Response $response,
- array $invokeArgs = array()
- ) {
- parent::__construct($request, $response, $invokeArgs);
- $this->moduleName = $request->getModuleName();
+ /**
+ * (non-PHPDoc)
+ * @see \Icinga\Web\Controller\ActionController For the method documentation.
+ */
+ protected function prepareInit()
+ {
+ $this->moduleName = $this->_request->getModuleName();
$this->_helper->layout()->moduleName = $this->moduleName;
$this->view->translationDomain = $this->moduleName;
$this->moduleInit();
}
+ /**
+ * Prepare module action controller initialization
+ */
+ protected function moduleInit()
+ {
+ }
+
public function Config($file = null)
{
- $module = $this->getRequest()->getModuleName();
-
- $this->moduleName = $module;
-
if ($file === null) {
if ($this->config === null) {
- $this->config = Config::module($module);
+ $this->config = Config::module($this->moduleName);
}
return $this->config;
} else {
if (! array_key_exists($file, $this->configs)) {
- $this->configs[$file] = Config::module($module, $file);
+ $this->configs[$file] = Config::module($this->moduleName, $file);
}
return $this->configs[$file];
}
@@ -58,19 +67,13 @@ class ModuleActionController extends ActionController
return $this->module;
}
- public function postDispatch()
- {
- $req = $this->getRequest();
- $resp = $this->getResponse();
-
- if ($this->isXhr()) {
- $resp->setHeader('X-Icinga-Module', $this->moduleName);
- }
-
- parent::postDispatch();
- }
-
- protected function moduleInit()
+ /**
+ * (non-PHPDoc)
+ * @see \Icinga\Web\Controller\ActionController::postDispatchXhr() For the method documentation.
+ */
+ public function postDispatchXhr()
{
+ parent::postDispatchXhr();
+ $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName);
}
}
diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php
index 65dc7c8d1..631b2eb07 100644
--- a/library/Icinga/Web/Form.php
+++ b/library/Icinga/Web/Form.php
@@ -4,47 +4,65 @@
namespace Icinga\Web;
-use Zend_Controller_Request_Abstract;
-use Zend_Form;
+use LogicException;
use Zend_Config;
-use Zend_Form_Element_Submit;
-use Zend_Form_Element_Reset;
+use Zend_Form;
use Zend_View_Interface;
-use Icinga\Web\Session;
-use Icinga\Web\Form\Element\Note;
-use Icinga\Exception\ProgrammingError;
-use Icinga\Web\Form\Decorator\HelpText;
-use Icinga\Web\Form\Decorator\BootstrapForm;
-use Icinga\Web\Form\InvalidCSRFTokenException;
-use Icinga\Application\Config as IcingaConfig;
+use Icinga\Application\Icinga;
+use Icinga\Web\Form\Decorator\NoScriptApply;
+use Icinga\Web\Form\Element\CsrfCounterMeasure;
/**
* Base class for forms providing CSRF protection, confirmation logic and auto submission
+ *
+ * @method $this setDefaults(array $defaults) {
+ * Use `Form::populate()' for setting default values for elements instead because `Form::setDefaults()' does not
+ * create the form via `Form::create()'.
+ *
+ * Due to a BC introduced with https://github.com/mhujer/zf1/commit/244e3d3f88a363ee0ca49cf63eee31f925f515cd
+ * we cannot override this function without running into a strict standards violation on Zend version 1.12.7.
+ *
+ * @param array $defaults
+ *
+ * @return $this
+ * }
*/
class Form extends Zend_Form
{
/**
- * The form's request object
+ * Whether this form has been created
*
- * @var Zend_Controller_Request_Abstract
+ * @var bool
*/
- protected $request;
+ protected $created = false;
/**
- * Main configuration
+ * The callback to call instead of Form::onSuccess()
*
- * Used as fallback if user preferences are not available.
- *
- * @var IcingaConfig
+ * @var Callback
*/
- protected $config;
+ protected $onSuccess;
/**
- * The preference object to use instead of the one from the user (used for testing)
+ * Label to use for the standard submit button
*
- * @var Zend_Config
+ * @var string
*/
- protected $preferences;
+ protected $submitLabel;
+
+ /**
+ * The url to redirect to upon success
+ *
+ * @var string|Url
+ */
+ protected $redirectUrl;
+
+ /**
+ * The view script to use when rendering this form
+ *
+ * @var string
+ */
+ protected $viewScript;
/**
* Whether this form should NOT add random generated "challenge" tokens that are associated with the user's current
@@ -56,83 +74,184 @@ class Form extends Zend_Form
protected $tokenDisabled = false;
/**
- * Name of the CSRF token element (used to create non-colliding hashes)
+ * Name of the CSRF token element
*
* @var string
*/
protected $tokenElementName = 'CSRFToken';
/**
- * Flag to indicate that form is already build
+ * Whether this form should add a UID element being used to distinct different forms posting to the same action
*
* @var bool
*/
- protected $created = false;
+ protected $uidDisabled = false;
/**
- * Session id used for CSRF token generation
+ * Name of the form identification element
*
* @var string
*/
- protected $sessionId;
+ protected $uidElementName = 'formUID';
/**
- * Label for submit button
+ * Default element decorators
*
- * If omitted, no button will be shown
- *
- * @var string
+ * @var array
*/
- protected $submitLabel;
+ public static $defaultElementDecorators = array(
+ 'ViewHelper',
+ 'Errors',
+ array('Description', array('tag' => 'span', 'class' => 'description')),
+ 'Label',
+ array('HtmlTag', array('tag' => 'div'))
+ );
/**
- * Label for cancel button
+ * Create a new form
*
- * If omitted, no button will be shown
+ * Accepts an additional option `onSuccess' which is a callback that is called instead of this
+ * form's method. It is called using the following signature: (Request $request, Form $form).
*
- * @var string
+ * @see Zend_Form::__construct()
+ *
+ * @throws LogicException In case `onSuccess' is not callable
*/
- protected $cancelLabel;
+ public function __construct($options = null)
+ {
+ if (is_array($options) && isset($options['onSuccess'])) {
+ $this->onSuccess = $options['onSuccess'];
+ unset($options['onSuccess']);
+ } elseif (isset($options->onSuccess)) {
+ $this->onSuccess = $options->onSuccess;
+ unset($options->onSuccess);
+ }
+
+ if ($this->onSuccess !== null && false === is_callable($this->onSuccess)) {
+ throw new LogicException('The option `onSuccess\' is not callable');
+ }
+
+ parent::__construct($options);
+ }
/**
- * Last used note-id
+ * Set the label to use for the standard submit button
*
- * Helper to generate unique names for note elements
+ * @param string $label The label to use for the submit button
*
- * @var int
+ * @return self
*/
- protected $last_note_id = 0;
+ public function setSubmitLabel($label)
+ {
+ $this->submitLabel = $label;
+ return $this;
+ }
/**
- * Getter for the session ID
- *
- * If the ID has never been set, the ID from session_id() is returned
+ * Return the label being used for the standard submit button
*
* @return string
*/
- public function getSessionId()
+ public function getSubmitLabel()
{
- if (!$this->sessionId) {
- $this->sessionId = Session::getSession()->getId();
+ return $this->submitLabel;
+ }
+
+ /**
+ * Set the url to redirect to upon success
+ *
+ * @param string|Url $url The url to redirect to
+ *
+ * @return self
+ */
+ public function setRedirectUrl($url)
+ {
+ $this->redirectUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Return the url to redirect to upon success
+ *
+ * @return string|Url
+ */
+ public function getRedirectUrl()
+ {
+ if ($this->redirectUrl === null) {
+ $url = Url::fromRequest(array(), $this->getRequest());
+ // Be sure to remove all form dependent params because we do not want to submit it again
+ $this->redirectUrl = $url->without(array_keys($this->getElements()));
}
- return $this->sessionId;
+ return $this->redirectUrl;
}
/**
- * Setter for the session ID
+ * Set the view script to use when rendering this form
*
- * This method should be used for testing purposes only
+ * @param string $viewScript The view script to use
*
- * @param string $sessionId
+ * @return self
*/
- public function setSessionId($sessionId)
+ public function setViewScript($viewScript)
{
- $this->sessionId = $sessionId;
+ $this->viewScript = $viewScript;
+ return $this;
}
/**
- * Return the HTML element name of the CSRF token field
+ * Return the view script being used when rendering this form
+ *
+ * @return string
+ */
+ public function getViewScript()
+ {
+ return $this->viewScript;
+ }
+
+ /**
+ * Disable CSRF counter measure and remove its field if already added
+ *
+ * @param bool $disabled Set true in order to disable CSRF protection for this form, otherwise false
+ *
+ * @return self
+ */
+ public function setTokenDisabled($disabled = true)
+ {
+ $this->tokenDisabled = (bool) $disabled;
+
+ if ($disabled && $this->getElement($this->tokenElementName) !== null) {
+ $this->removeElement($this->tokenElementName);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return whether CSRF counter measures are disabled for this form
+ *
+ * @return bool
+ */
+ public function getTokenDisabled()
+ {
+ return $this->tokenDisabled;
+ }
+
+ /**
+ * Set the name to use for the CSRF element
+ *
+ * @param string $name The name to set
+ *
+ * @return self
+ */
+ public function setTokenElementName($name)
+ {
+ $this->tokenElementName = $name;
+ return $this;
+ }
+
+ /**
+ * Return the name of the CSRF element
*
* @return string
*/
@@ -142,427 +261,383 @@ class Form extends Zend_Form
}
/**
- * Render the form to HTML
+ * Disable form identification and remove its field if already added
*
- * @param Zend_View_Interface $view
- *
- * @return string
- */
- public function render(Zend_View_Interface $view = null)
- {
- // Elements must be there to render the form
- $this->buildForm();
- return parent::render($view);
- }
-
- /**
- * Add elements to this form (used by extending classes)
- */
- protected function create()
- {
-
- }
-
- /**
- * Method called before validation
- */
- protected function preValidation(array $data)
- {
-
- }
-
- /**
- * Setter for the request
- *
- * @param Zend_Controller_Request_Abstract $request
- */
- public function setRequest(Zend_Controller_Request_Abstract $request)
- {
- $this->request = $request;
- }
-
- /**
- * Getter for the request
- *
- * @return Zend_Controller_Request_Abstract
- */
- public function getRequest()
- {
- return $this->request;
- }
-
- /**
- * Set the configuration to be used for this form when no preferences are set yet
- *
- * @param IcingaConfig $cfg
+ * @param bool $disabled Set true in order to disable identification for this form, otherwise false
*
* @return self
*/
- public function setConfiguration($cfg)
+ public function setUidDisabled($disabled = true)
{
- $this->config = $cfg;
+ $this->uidDisabled = (bool) $disabled;
+
+ if ($disabled && $this->getElement($this->uidElementName) !== null) {
+ $this->removeElement($this->uidElementName);
+ }
+
return $this;
}
/**
- * Get the main configuration
+ * Return whether identification is disabled for this form
*
- * Returns the set configuration or an empty default one.
- *
- * @return Zend_Config
+ * @return bool
*/
- public function getConfiguration()
+ public function getUidDisabled()
{
- if ($this->config === null) {
- $this->config = new Zend_Config(array(), true);
- }
-
- return $this->config;
+ return $this->uidDisabled;
}
/**
- * Set preferences to be used instead of the one from the user object (used for testing)
+ * Set the name to use for the form identification element
*
- * @param Zend_Config $prefs
+ * @param string $name The name to set
+ *
+ * @return self
*/
- public function setUserPreferences($prefs)
+ public function setUidElementName($name)
{
- $this->preferences = $prefs;
+ $this->uidElementName = $name;
+ return $this;
}
/**
- * Return the preferences of the user or the overwritten ones
+ * Return the name of the form identification element
*
- * @return Zend_Config
+ * @return string
*/
- public function getUserPreferences()
+ public function getUidElementName()
{
- if ($this->preferences) {
- return $this->preferences;
- }
-
- return $this->getRequest()->getUser()->getPreferences();
+ return $this->uidElementName;
}
/**
- * Create the form if not done already
+ * Create this form
*
- * Adds all elements to the form
+ * @param array $formData The data sent by the user
+ *
+ * @return self
*/
- public function buildForm()
+ public function create(array $formData = array())
{
- if ($this->created === false) {
- $this->initCsrfToken();
- $this->create();
+ if (false === $this->created) {
+ $this->createElements($formData);
+ $this->addFormIdentification()
+ ->addCsrfCounterMeasure()
+ ->addSubmitButton();
- if ($this->submitLabel) {
- $this->addSubmitButton();
- }
-
- if ($this->cancelLabel) {
- $this->addCancelButton();
- }
-
- // Empty action if not safe
- if (!$this->getAction() && $this->getRequest()) {
- $this->setAction($this->getRequest()->getRequestUri());
+ if ($this->getAction() === '') {
+ // We MUST set an action as JS gets confused otherwise, if
+ // this form is being displayed in an additional column
+ $this->setAction(Url::fromRequest()->without(array_keys($this->getElements())));
}
$this->created = true;
}
+
+ return $this;
}
/**
- * Setter for the cancel label
+ * Create and add elements to this form
*
- * @param string $cancelLabel
+ * Intended to be implemented by concrete form classes.
+ *
+ * @param array $formData The data sent by the user
*/
- public function setCancelLabel($cancelLabel)
+ public function createElements(array $formData)
{
- $this->cancelLabel = $cancelLabel;
+
}
/**
- * Add cancel button to form
+ * Perform actions after this form was submitted using a valid request
+ *
+ * Intended to be implemented by concrete form classes. The base implementation returns always FALSE.
+ *
+ * @param Request $request The valid request used to process this form
+ *
+ * @return null|bool Return FALSE in case no redirect should take place
*/
- protected function addCancelButton()
+ public function onSuccess(Request $request)
{
- $this->addElement(
- new Zend_Form_Element_Reset(
- array(
- 'name' => 'btn_reset',
- 'label' => $this->cancelLabel,
- 'class' => 'btn pull-right'
- )
- )
- );
+ return false;
}
/**
- * Setter for the submit label
+ * Perform actions when no form dependent data was sent
*
- * @param string $submitLabel
+ * Intended to be implemented by concrete form classes.
+ *
+ * @param Request $request The current request
*/
- public function setSubmitLabel($submitLabel)
+ public function onRequest(Request $request)
{
- $this->submitLabel = $submitLabel;
+
}
/**
- * Add submit button to form
+ * Add a submit button to this form
+ *
+ * Uses the label previously set with Form::setSubmitLabel(). Overwrite this
+ * method in order to add multiple submit buttons or one with a custom name.
+ *
+ * @return self
*/
- protected function addSubmitButton()
+ public function addSubmitButton()
{
- $this->addElement(
- new Zend_Form_Element_Submit(
- array(
- 'name' => 'btn_submit',
- 'label' => $this->submitLabel
- )
- )
- );
- }
-
- /**
- * Add message to form
- *
- * @param string $message The message to be displayed
- * @param int $headingType Whether it should be displayed as heading (1-6) or not (null)
- */
- public function addNote($message, $headingType = null)
- {
- $this->addElement(
- new Note(
- array(
- 'escape' => $headingType === null ? false : true,
- 'name' => sprintf('note_%s', $this->last_note_id++),
- 'value' => $headingType === null ? $message : sprintf(
- '%2$s',
- $headingType,
- $message
- )
- )
- )
- );
- }
-
- /**
- * Enable automatic form submission on the given elements
- *
- * Enables automatic submission of this form once the user edits specific elements
- *
- * @param array $triggerElements The element names which should auto-submit the form
- *
- * @throws ProgrammingError When an element is found which does not yet exist
- */
- public function enableAutoSubmit($triggerElements)
- {
- foreach ($triggerElements as $elementName) {
- $element = $this->getElement($elementName);
- if ($element !== null) {
- $class = $element->getAttrib('class');
- if ($class === null) {
- $class = 'autosubmit';
- } else {
- $class .= ' autosubmit';
- }
- $element->setAttrib('class', $class);
- } else {
- throw new ProgrammingError(
- 'You need to add the element "%s" to the form before automatic submission can be enabled!',
- $elementName
- );
- }
- }
- }
-
- /**
- * Check whether the form was submitted with a valid request
- *
- * Ensures that the current request method is POST, that the form was manually submitted and that the data provided
- * in the request is valid and gets repopulated in case its invalid.
- *
- * @return bool True when the form is submitted and valid, otherwise false
- */
- public function isSubmittedAndValid()
- {
- if ($this->getRequest()->isPost() === false) {
- return false;
- }
-
- $this->buildForm();
- $checkData = $this->getRequest()->getParams();
- $this->assertValidCsrfToken($checkData);
-
- if ($this->isSubmitted()) {
- // perform full validation if submitted
- $this->preValidation($checkData);
- return $this->isValid($checkData);
- } else {
- // only populate if not submitted
- $this->populate($checkData);
- $this->setAttrib('data-icinga-form-modified', 'true');
- return false;
- }
- }
-
- /**
- * Check whether this form has been submitted
- *
- * Per default, this checks whether the button set with the 'setSubmitLabel' method
- * is being submitted. For custom submission logic, this method must be overwritten
- *
- * @return bool True when the form is marked as submitted, otherwise false
- */
- public function isSubmitted()
- {
- // TODO: There are some missunderstandings and missconceptions to be
- // found in this class. If populate() etc would have been used as
- // designed this function would read as simple as:
- // return $this->getElement('btn_submit')->isChecked();
-
- if ($this->submitLabel) {
- $checkData = $this->getRequest()->getParams();
- return isset($checkData['btn_submit']) && $checkData['btn_submit'];
- }
- return true;
- }
-
- /**
- * Disable CSRF counter measure and remove its field if already added
- *
- * This method should be used for testing purposes only
- *
- * @param bool $disabled Set true in order to disable CSRF tokens in
- * this form (default: true), otherwise false
- */
- public function setTokenDisabled($disabled = true)
- {
- $this->tokenDisabled = (boolean) $disabled;
-
- if ($disabled === true) {
- $this->removeElement($this->tokenElementName);
- }
- }
-
- /**
- * Add CSRF counter measure field to form
- */
- public function initCsrfToken()
- {
- if (!$this->tokenDisabled && $this->getElement($this->tokenElementName) === null) {
+ $submitLabel = $this->getSubmitLabel();
+ if ($submitLabel) {
$this->addElement(
- 'hidden',
- $this->tokenElementName,
+ 'submit',
+ 'btn_submit',
array(
- 'value' => $this->generateCsrfTokenAsString()
+ 'ignore' => true,
+ 'label' => $submitLabel,
+ 'decorators' => array(
+ 'ViewHelper',
+ array('HtmlTag', array('tag' => 'div'))
+ )
)
);
}
+
+ return $this;
}
/**
- * Test the submitted data for a correct CSRF token
+ * Add a subform
*
- * @param array $checkData The POST data send by the user
+ * @param Zend_Form $form The subform to add
+ * @param string $name The name of the subform or null to use the name of $form
+ * @param int $order The location where to insert the form
*
- * @throws InvalidCSRFTokenException When CSRF Validation fails
+ * @return Zend_Form
*/
- public function assertValidCsrfToken(array $checkData)
+ public function addSubForm(Zend_Form $form, $name = null, $order = null)
{
- if (!$this->tokenDisabled) {
- if (!isset($checkData[$this->tokenElementName])
- || !$this->hasValidCsrfToken($checkData[$this->tokenElementName])
- ) {
- throw new InvalidCSRFTokenException();
+ if ($form instanceof self) {
+ $form->removeDecorator('Form');
+ $form->setSubmitLabel('');
+ $form->setTokenDisabled();
+ $form->setUidDisabled();
+ }
+
+ if ($name === null) {
+ $name = $form->getName();
+ }
+
+ return parent::addSubForm($form, $name, $order);
+ }
+
+ /**
+ * Create a new element
+ *
+ * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set the
+ * `disableLoadDefaultDecorators' option to any other value than `true'. For loading custom element decorators use
+ * the 'decorators' option.
+ *
+ * @param string $type String element type
+ * @param string $name The name of the element to add
+ * @param mixed $options The options for the element
+ *
+ * @return Zend_Form_Element
+ *
+ * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators.
+ */
+ public function createElement($type, $name, $options = null)
+ {
+ if ($options !== null) {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
}
- }
- }
-
- /**
- * Check whether the form's CSRF token-field has a valid value
- *
- * @param string $elementValue Value from the form element
- *
- * @return bool
- */
- protected function hasValidCsrfToken($elementValue)
- {
- if ($this->getElement($this->tokenElementName) === null || strpos($elementValue, '|') === false) {
- return false;
+ if (! isset($options['decorators'])
+ && ! array_key_exists('disabledLoadDefaultDecorators', $options)
+ ) {
+ $options['decorators'] = static::$defaultElementDecorators;
+ }
+ } else {
+ $options = array('decorators' => static::$defaultElementDecorators);
}
- list($seed, $token) = explode('|', $elementValue);
+ $el = parent::createElement($type, $name, $options);
- if (!is_numeric($seed)) {
- return false;
+ if ($el && $el->getAttrib('autosubmit')) {
+ $el->addDecorator(new NoScriptApply()); // Non-JS environments
+ $class = $el->getAttrib('class');
+ if (is_array($class)) {
+ $class[] = 'autosubmit';
+ } elseif ($class === null) {
+ $class = 'autosubmit';
+ } else {
+ $class .= ' autosubmit';
+ }
+ $el->setAttrib('class', $class); // JS environments
+ unset($el->autosubmit);
}
- return $token === hash('sha256', $this->getSessionId() . $seed);
+ return $el;
}
/**
- * Generate a new (seed, token) pair
- *
- * @return array
- */
- public function generateCsrfToken()
- {
- $seed = mt_rand();
- $hash = hash('sha256', $this->getSessionId() . $seed);
-
- return array($seed, $hash);
- }
-
- /**
- * Return the string representation of the CSRF seed/token pair
- *
- * @return string
- */
- public function generateCsrfTokenAsString()
- {
- list ($seed, $token) = $this->generateCsrfToken($this->getSessionId());
- return sprintf('%s|%s', $seed, $token);
- }
-
- /**
- * Add a new element
- *
- * Additionally, all DtDd tags will be removed and the Bootstrap compatible
- * BootstrapForm decorator will be added to the elements
- *
- * @param string|Zend_Form_Element $element String element type, or an object of type Zend_Form_Element
- * @param string $name The name of the element to add if $element is a string
- * @param array $options The settings for the element if $element is a string
+ * Add a field with a unique and form specific ID
*
* @return self
- * @see Zend_Form::addElement()
*/
- public function addElement($element, $name = null, $options = null)
+ public function addFormIdentification()
{
- parent::addElement($element, $name, $options);
- $el = $name !== null ? $this->getElement($name) : $element;
-
- if ($el) {
- if (strpos(strtolower(get_class($el)), 'hidden') !== false) {
- // Do not add structural elements to invisible elements which produces ugly views
- $el->setDecorators(array('ViewHelper'));
- } else {
- $el->removeDecorator('HtmlTag');
- $el->removeDecorator('Label');
- $el->removeDecorator('DtDdWrapper');
- $el->addDecorator(new BootstrapForm());
- $el->addDecorator(new HelpText());
- }
+ if (false === $this->uidDisabled && $this->getElement($this->uidElementName) === null) {
+ $this->addElement(
+ 'hidden',
+ $this->uidElementName,
+ array(
+ 'ignore' => true,
+ 'value' => $this->getName(),
+ 'decorators' => array('ViewHelper')
+ )
+ );
}
return $this;
}
+ /**
+ * Add CSRF counter measure field to this form
+ *
+ * @return self
+ */
+ public function addCsrfCounterMeasure()
+ {
+ if (false === $this->tokenDisabled && $this->getElement($this->tokenElementName) === null) {
+ $this->addElement(new CsrfCounterMeasure($this->tokenElementName));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Populate the elements with the given values
+ *
+ * @param array $defaults The values to populate the elements with
+ */
+ public function populate(array $defaults)
+ {
+ $this->create($defaults);
+ return parent::populate($defaults);
+ }
+
+ /**
+ * Process the given request using this form
+ *
+ * Redirects to the url set with setRedirectUrl() upon success. See onSuccess()
+ * and onRequest() wherewith you can customize the processing logic.
+ *
+ * @param Request $request The request to be processed
+ *
+ * @return Request The request supposed to be processed
+ */
+ public function handleRequest(Request $request = null)
+ {
+ if ($request === null) {
+ $request = $this->getRequest();
+ }
+
+ $formData = $this->getRequestData($request);
+ if ($this->getUidDisabled() || $this->wasSent($formData)) {
+ $this->populate($formData); // Necessary to get isSubmitted() to work
+ if (! $this->getSubmitLabel() || $this->isSubmitted()) {
+ if ($this->isValid($formData)
+ && (($this->onSuccess !== null && false !== call_user_func($this->onSuccess, $request, $this))
+ || ($this->onSuccess === null && false !== $this->onSuccess($request)))) {
+ $this->getResponse()->redirectAndExit($this->getRedirectUrl());
+ }
+ } else {
+ // The form can't be processed but we want to show validation errors though
+ $this->isValidPartial($formData);
+ }
+ } else {
+ $this->onRequest($request);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Return whether the submit button of this form was pressed
+ *
+ * When overwriting Form::addSubmitButton() be sure to overwrite this method as well.
+ *
+ * @return bool True in case it was pressed, False otherwise or no submit label was set
+ */
+ public function isSubmitted()
+ {
+ if ($this->getSubmitLabel()) {
+ return $this->getElement('btn_submit')->isChecked();
+ }
+
+ return false;
+ }
+
+ /**
+ * Return whether the data sent by the user refers to this form
+ *
+ * Ensures that the correct form gets processed in case there are multiple forms
+ * with equal submit button names being posted against the same route.
+ *
+ * @param array $formData The data sent by the user
+ *
+ * @return bool Whether the given data refers to this form
+ */
+ public function wasSent(array $formData)
+ {
+ return isset($formData[$this->uidElementName]) && $formData[$this->uidElementName] === $this->getName();
+ }
+
+ /**
+ * Return whether the given values (possibly incomplete) are valid
+ *
+ * Unlike Zend_Form::isValid() this will not set NULL as value for
+ * an element that is not present in the given data.
+ *
+ * @param array $formData The data to validate
+ *
+ * @return bool
+ */
+ public function isValidPartial(array $formData)
+ {
+ $this->create($formData);
+ return parent::isValidPartial($formData);
+ }
+
+ /**
+ * Return whether the given values are valid
+ *
+ * @param array $formData The data to validate
+ *
+ * @return bool
+ */
+ public function isValid($formData)
+ {
+ $this->create($formData);
+ return parent::isValid($formData);
+ }
+
+ /**
+ * Remove all elements of this form
+ *
+ * @return self
+ */
+ public function clearElements()
+ {
+ $this->created = false;
+ return parent::clearElements();
+ }
+
/**
* Load the default decorators
*
- * Overwrites Zend_Form::loadDefaultDecorators to avoid having the HtmlTag-Decorator added
+ * Overwrites Zend_Form::loadDefaultDecorators to avoid having
+ * the HtmlTag-Decorator added and to provide viewscript usage
*
* @return self
*/
@@ -574,11 +649,84 @@ class Form extends Zend_Form
$decorators = $this->getDecorators();
if (empty($decorators)) {
- $this->addDecorator('FormElements')
- //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form'))
- ->addDecorator('Form');
+ if ($this->viewScript) {
+ $this->addDecorator('ViewScript', array(
+ 'viewScript' => $this->viewScript,
+ 'form' => $this
+ ));
+ } else {
+ $this->addDecorator('FormErrors', array('onlyCustomFormErrors' => true))
+ ->addDecorator('FormElements')
+ //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form'))
+ ->addDecorator('Form');
+ }
}
return $this;
}
+
+ /**
+ * Return the name of this form
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ $name = parent::getName();
+ if (! $name) {
+ $name = get_class($this);
+ $this->setName($name);
+ $name = parent::getName();
+ }
+ return $name;
+ }
+
+ /**
+ * Return the request data based on this form's request method
+ *
+ * @param Request $request The request to fetch the data from
+ *
+ * @return array
+ */
+ public function getRequestData(Request $request)
+ {
+ if (strtolower($request->getMethod()) === $this->getMethod()) {
+ return $request->{'get' . ($request->isPost() ? 'Post' : 'Query')}();
+ }
+
+ return array();
+ }
+
+ /**
+ * Return the current request
+ *
+ * @return Request
+ */
+ public function getRequest()
+ {
+ return Icinga::app()->getFrontController()->getRequest();
+ }
+
+ /**
+ * Return the current Response
+ *
+ * @return Response
+ */
+ public function getResponse()
+ {
+ return Icinga::app()->getFrontController()->getResponse();
+ }
+
+ /**
+ * Render this form
+ *
+ * @param Zend_View_Interface $view The view context to use
+ *
+ * @return string
+ */
+ public function render(Zend_View_Interface $view = null)
+ {
+ $this->create();
+ return parent::render($view);
+ }
}
diff --git a/library/Icinga/Web/Form/Decorator/BootstrapForm.php b/library/Icinga/Web/Form/Decorator/BootstrapForm.php
deleted file mode 100644
index dfb60de2c..000000000
--- a/library/Icinga/Web/Form/Decorator/BootstrapForm.php
+++ /dev/null
@@ -1,80 +0,0 @@
- dom added per default
- *
- * @var array
- */
- private static $noLabel = array(
- 'Zend_Form_Element_Hidden',
- 'Zend_Form_Element_Button',
- 'Zend_Form_Element_Submit'
- );
-
- /**
- * Return the DOM for the element label
- *
- * @param String $elementName The name of the element
- *
- * @return String The DOM for the form element's label
- */
- public function getLabel($elementName)
- {
- $label = $this->getElement()->getLabel();
- if (!$label) {
- $label = ' ';
- }
- if (in_array($this->getElement()->getType(), self::$noLabel)
- && !$this->getElement()->getAttrib('addLabelPlaceholder', false)) {
- $label = '';
- } else {
- if (in_array($this->getElement()->getType(), self::$noLabel)) {
- $label = ' ';
- }
- $label = '';
- }
- return $label;
- }
-
- /**
- * Render this element
- *
- * Renders as the following:
- *
- * $dtLabel
- * $dtElement
- *
- *
- * @param String $content The content of the form element
- *
- * @return String The decorated form element
- */
- public function render($content)
- {
- $el = $this->getElement();
- $elementName = $el->getName();
- $label = $this->getLabel($elementName);
- return '
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/createbackend.phtml b/modules/monitoring/application/views/scripts/config/createbackend.phtml
new file mode 100644
index 000000000..0ef51cd6a
--- /dev/null
+++ b/modules/monitoring/application/views/scripts/config/createbackend.phtml
@@ -0,0 +1,2 @@
+
= $this->translate('Add New Backend'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/createinstance.phtml b/modules/monitoring/application/views/scripts/config/createinstance.phtml
new file mode 100644
index 000000000..49c8b5ec5
--- /dev/null
+++ b/modules/monitoring/application/views/scripts/config/createinstance.phtml
@@ -0,0 +1,2 @@
+
= $this->translate('Add New Instance'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/editbackend.phtml b/modules/monitoring/application/views/scripts/config/editbackend.phtml
index 6f2f19cbb..58b6c18f1 100644
--- a/modules/monitoring/application/views/scripts/config/editbackend.phtml
+++ b/modules/monitoring/application/views/scripts/config/editbackend.phtml
@@ -1,13 +1,2 @@
-name): ?>
-
Edit Backend "= $this->escape($this->name) ?>"
-
-
Create New Backend
-
-
-error): ?>
-
- = $this->escape($this->error); ?>
-
-
-
-= $this->form; ?>
\ No newline at end of file
+
= $this->translate('Edit Existing Backend'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/editinstance.phtml b/modules/monitoring/application/views/scripts/config/editinstance.phtml
index 1a2f5fe97..b12f262c3 100644
--- a/modules/monitoring/application/views/scripts/config/editinstance.phtml
+++ b/modules/monitoring/application/views/scripts/config/editinstance.phtml
@@ -1,14 +1,2 @@
-
-name)): ?>
-
} Edit Instance Configuration for "= $this->escape($this->name) ?>"
-
-
Configure New Icinga Instance
-
-
-error): ?>
-
- = $this->escape($this->error); ?>
-
-
-
-= $this->form; ?>
\ No newline at end of file
+
= $this->translate('Edit Existing Instance'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml
index 41d2748d5..34f47a0b4 100644
--- a/modules/monitoring/application/views/scripts/config/index.phtml
+++ b/modules/monitoring/application/views/scripts/config/index.phtml
@@ -1,99 +1,70 @@
-{$element}) &&
- get_class($this->{$element}) === 'Icinga\Exception\NotReadableError') {
- $fileNotReadable[$element] = $this->{$element}->getMessage();
- } else {
- $fileNotReadable[$element] = false;
- }
-}
-?>
-
+
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/removebackend.phtml b/modules/monitoring/application/views/scripts/config/removebackend.phtml
index bd4a95f3b..fc7da17e3 100644
--- a/modules/monitoring/application/views/scripts/config/removebackend.phtml
+++ b/modules/monitoring/application/views/scripts/config/removebackend.phtml
@@ -1,8 +1,2 @@
-
-
Remove Backend "= $this->escape($this->name) ?>"
-
-
- Are you sure you want to remove the backend = $this->escape($this->name) ?>?
-
-
-= $this->form; ?>
+
= $this->translate('Remove Existing Backend'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/config/removeinstance.phtml b/modules/monitoring/application/views/scripts/config/removeinstance.phtml
index a278cf9b1..306d41815 100644
--- a/modules/monitoring/application/views/scripts/config/removeinstance.phtml
+++ b/modules/monitoring/application/views/scripts/config/removeinstance.phtml
@@ -1,13 +1,4 @@
-
-
Remove Instance "= $this->escape($this->name) ?>"
-
-
- Are you sure you want to remove the instance = $this->escape($this->name) ?>?
-
-
-
- If you have still any environments or views refering to this instance, you won't be able to send commands anymore
- after deletion.
-
= $this->translate('Are you sure you want to remove this instance?'); ?>
+
= $this->translate('If you have still any environments or views referring to this instance, you won\'t be able to send commands anymore after deletion.'); ?>
+= $form; ?>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/host/show.phtml b/modules/monitoring/application/views/scripts/host/show.phtml
new file mode 100644
index 000000000..8023c1821
--- /dev/null
+++ b/modules/monitoring/application/views/scripts/host/show.phtml
@@ -0,0 +1,27 @@
+
diff --git a/modules/monitoring/application/views/scripts/process/disable-notifications.phtml b/modules/monitoring/application/views/scripts/process/disable-notifications.phtml
new file mode 100644
index 000000000..547b3ac37
--- /dev/null
+++ b/modules/monitoring/application/views/scripts/process/disable-notifications.phtml
@@ -0,0 +1,18 @@
+
+ = $this->tabs->showOnlyCloseButton() ?>
+
+
+
= $title ?>
+ notifications_enabled === false): ?>
+
+ = $this->translate('Host and service notifications are already disabled.') ?>
+ programStatus->disable_notif_expire_time): ?>
+ = sprintf(
+ $this->translate('Notifications will be re-enabled in %s.'),
+ $this->timeUntil($this->programStatus->disable_notif_expire_time)); ?>
+
+
+ = sprintf(
+ $this->translate('%s has been up and running with PID %d since %s'),
+ $this->backendName,
+ $this->programStatus->process_id,
+ $this->timeSince($this->programStatus->program_start_time)) ?>
+
+
+
+ = sprintf($this->translate('%s is not running'), $this->backendName) ?>
+