mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-04-08 17:15:08 +02:00
Merge branch 'master' into bugfix/unnecessary-joins-8614
This commit is contained in:
commit
2286ab1846
5
AUTHORS
5
AUTHORS
@ -1,5 +1,6 @@
|
||||
Alexander Fuhr <alexander.fuhr@netways.de>
|
||||
Alexander Klimov <alexander.klimov@netways.de>
|
||||
ayoubabid <ayoubabid@users.noreply.github.com>
|
||||
baufrecht <baufrecht@users.noreply.github.com>
|
||||
Bernd Erk <bernd.erk@icinga.org>
|
||||
Boden Garman <boden.garman@spintel.net.au>
|
||||
@ -11,8 +12,9 @@ Goran Rakic <grakic@devbase.net>
|
||||
Gunnar Beutner <gunnar.beutner@netways.de>
|
||||
Jannis Moßhammer <jannis.mosshammer@netways.de>
|
||||
Johannes Meyer <johannes.meyer@netways.de>
|
||||
Marius Hein <marius.hein@netways.de>
|
||||
Louis Sautier <sautier.louis@gmail.com>
|
||||
Marcus Cobden <marcus@marcuscobden.co.uk>
|
||||
Marius Hein <marius.hein@netways.de>
|
||||
Markus Frosch <markus@lazyfrosch.de>
|
||||
Matthias Jentsch <matthias.jentsch@netways.de>
|
||||
Michael Friedrich <michael.friedrich@netways.de>
|
||||
@ -22,4 +24,3 @@ Sylph Lin <sylph.lin@gmail.com>
|
||||
Thomas Gelf <thomas.gelf@netways.de>
|
||||
Tom Ford <exptom@users.noreply.github.com>
|
||||
Ulf Lange <mopp@gmx.net>
|
||||
ayoubabid <ayoubabid@users.noreply.github.com>
|
||||
|
@ -135,22 +135,23 @@ class ConfigController extends ActionController
|
||||
|
||||
public function moduleAction()
|
||||
{
|
||||
$name = $this->getParam('name');
|
||||
$app = Icinga::app();
|
||||
$manager = $app->getModuleManager();
|
||||
$name = $this->getParam('name');
|
||||
if ($manager->hasInstalled($name)) {
|
||||
$this->view->moduleData = Icinga::app()
|
||||
->getModuleManager()
|
||||
->select()
|
||||
->from('modules')
|
||||
->where('name', $name)
|
||||
->fetchRow();
|
||||
$module = new Module($app, $name, $manager->getModuleDir($name));
|
||||
$this->view->moduleData = $manager->select()->from('modules')->where('name', $name)->fetchRow();
|
||||
if ($manager->hasLoaded($name)) {
|
||||
$module = $manager->getModule($name);
|
||||
} else {
|
||||
$module = new Module($app, $name, $manager->getModuleDir($name));
|
||||
}
|
||||
|
||||
$this->view->module = $module;
|
||||
$this->view->tabs = $module->getConfigTabs()->activate('info');
|
||||
} else {
|
||||
$this->view->module = false;
|
||||
$this->view->tabs = null;
|
||||
}
|
||||
$this->view->tabs = $module->getConfigTabs()->activate('info');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,7 +164,6 @@ class ConfigController extends ActionController
|
||||
$manager = Icinga::app()->getModuleManager();
|
||||
try {
|
||||
$manager->enableModule($module);
|
||||
$manager->loadModule($module);
|
||||
Notification::success(sprintf($this->translate('Module "%s" enabled'), $module));
|
||||
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||
} catch (Exception $e) {
|
||||
|
@ -53,7 +53,7 @@ class ExternalBackendForm extends Form
|
||||
return @preg_match($value, '') !== false;
|
||||
});
|
||||
$callbackValidator->setMessage(
|
||||
$this->translate('"%value%" is not a valid regular expression'),
|
||||
$this->translate('"%value%" is not a valid regular expression.'),
|
||||
Zend_Validate_Callback::INVALID_VALUE
|
||||
);
|
||||
$this->addElement(
|
||||
@ -62,9 +62,10 @@ class ExternalBackendForm extends Form
|
||||
array(
|
||||
'label' => $this->translate('Filter Pattern'),
|
||||
'description' => $this->translate(
|
||||
'The regular expression to use to strip specific parts off from usernames.'
|
||||
. ' Leave empty if you do not want to strip off anything'
|
||||
'The filter to use to strip specific parts off from usernames.'
|
||||
. ' Leave empty if you do not want to strip off anything.'
|
||||
),
|
||||
'requirement' => $this->translate('The filter pattern must be a valid regular expression.'),
|
||||
'validators' => array($callbackValidator)
|
||||
)
|
||||
);
|
||||
|
@ -55,7 +55,7 @@ class LdapBackendForm extends Form
|
||||
'required' => true,
|
||||
'label' => $this->translate('Backend Name'),
|
||||
'description' => $this->translate(
|
||||
'The name of this authentication provider that is used to differentiate it from others'
|
||||
'The name of this authentication provider that is used to differentiate it from others.'
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -64,8 +64,10 @@ class LdapBackendForm extends Form
|
||||
'resource',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('LDAP Resource'),
|
||||
'description' => $this->translate('The resource to use for authenticating with this provider'),
|
||||
'label' => $this->translate('LDAP Connection'),
|
||||
'description' => $this->translate(
|
||||
'The LDAP connection to use for authenticating with this provider.'
|
||||
),
|
||||
'multiOptions' => false === empty($this->resources)
|
||||
? array_combine($this->resources, $this->resources)
|
||||
: array()
|
||||
@ -77,10 +79,40 @@ class LdapBackendForm extends Form
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('LDAP User Object Class'),
|
||||
'description' => $this->translate('The object class used for storing users on the ldap server'),
|
||||
'description' => $this->translate('The object class used for storing users on the LDAP server.'),
|
||||
'value' => 'inetOrgPerson'
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'filter',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'label' => $this->translate('LDAP Filter'),
|
||||
'description' => $this->translate(
|
||||
'An additional filter to use when looking up users using the specified connection. '
|
||||
. 'Leave empty to not to use any additional filter rules.'
|
||||
),
|
||||
'requirement' => $this->translate(
|
||||
'The filter needs to be expressed as standard LDAP expression, without'
|
||||
. ' outer parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)'
|
||||
),
|
||||
'validators' => array(
|
||||
array(
|
||||
'Callback',
|
||||
false,
|
||||
array(
|
||||
'callback' => function ($v) {
|
||||
return strpos($v, '(') !== 0;
|
||||
},
|
||||
'messages' => array(
|
||||
'callbackValue' => $this->translate('The filter must not be wrapped in parantheses.')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'user_name_attribute',
|
||||
@ -88,7 +120,7 @@ class LdapBackendForm extends Form
|
||||
'required' => true,
|
||||
'label' => $this->translate('LDAP User Name Attribute'),
|
||||
'description' => $this->translate(
|
||||
'The attribute name used for storing the user name on the ldap server'
|
||||
'The attribute name used for storing the user name on the LDAP server.'
|
||||
),
|
||||
'value' => 'uid'
|
||||
)
|
||||
@ -106,10 +138,10 @@ class LdapBackendForm extends Form
|
||||
'base_dn',
|
||||
array(
|
||||
'required' => false,
|
||||
'label' => $this->translate('Base DN'),
|
||||
'label' => $this->translate('LDAP Base DN'),
|
||||
'description' => $this->translate(
|
||||
'The path where users can be found on the ldap server. Leave ' .
|
||||
'empty to select all users available on the specified resource.'
|
||||
'The path where users can be found on the LDAP server. Leave ' .
|
||||
'empty to select all users available using the specified connection.'
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -142,11 +174,17 @@ class LdapBackendForm extends Form
|
||||
ResourceFactory::createResource($form->getResourceConfig()),
|
||||
$form->getElement('user_class')->getValue(),
|
||||
$form->getElement('user_name_attribute')->getValue(),
|
||||
$form->getElement('base_dn')->getValue()
|
||||
$form->getElement('base_dn')->getValue(),
|
||||
$form->getElement('filter')->getValue()
|
||||
);
|
||||
$ldapUserBackend->assertAuthenticationPossible();
|
||||
} catch (AuthenticationException $e) {
|
||||
$form->addError($e->getMessage());
|
||||
if (($previous = $e->getPrevious()) !== null) {
|
||||
$form->addError($previous->getMessage());
|
||||
} else {
|
||||
$form->addError($e->getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$form->addError(sprintf($form->translate('Unable to validate authentication: %s'), $e->getMessage()));
|
||||
|
@ -66,6 +66,7 @@ class LoggingConfigForm extends Form
|
||||
'description' => $this->translate(
|
||||
'The name of the application by which to prefix syslog messages.'
|
||||
),
|
||||
'requirement' => $this->translate('The application prefix must not contain whitespace.'),
|
||||
'value' => 'icingaweb2',
|
||||
'validators' => array(
|
||||
array(
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
namespace Icinga\Forms\Config\Resource;
|
||||
|
||||
use Zend_Validate_Callback;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
@ -42,13 +43,22 @@ class FileResourceForm extends Form
|
||||
'validators' => array('ReadablePathValidator')
|
||||
)
|
||||
);
|
||||
$callbackValidator = new Zend_Validate_Callback(function ($value) {
|
||||
return @preg_match($value, '') !== false;
|
||||
});
|
||||
$callbackValidator->setMessage(
|
||||
$this->translate('"%value%" is not a valid regular expression.'),
|
||||
Zend_Validate_Callback::INVALID_VALUE
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'fields',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Pattern'),
|
||||
'description' => $this->translate('The regular expression by which to identify columns')
|
||||
'description' => $this->translate('The pattern by which to identify columns.'),
|
||||
'requirement' => $this->translate('The column pattern must be a valid regular expression.'),
|
||||
'validators' => array($callbackValidator)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -29,7 +29,16 @@ if ($notifications->hasMessages()) {
|
||||
}
|
||||
?></ul>
|
||||
<div id="logo" data-base-target="_main">
|
||||
<img aria-hidden="true" src="<?= $this->href('img/logo_icinga-inv.png') ?>" class="logo" alt="<?= t('Dashboard') ?>" />
|
||||
<?= $this->qlink(
|
||||
'',
|
||||
'/dashboard',
|
||||
null,
|
||||
array(
|
||||
'icon' => '../logo_icinga-inv.png',
|
||||
'aria-hidden' => 'true',
|
||||
'tabindex' => -1
|
||||
)
|
||||
); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2,9 +2,6 @@
|
||||
<?= $this->tabs ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1 tabindex="-1">
|
||||
<?= $this->escape($module->getTitle()) ?>
|
||||
</h1>
|
||||
<?php if (! $module): ?>
|
||||
<?= $this->translate('There is no such module installed.') ?>
|
||||
<?php return; endif ?>
|
||||
@ -14,6 +11,9 @@
|
||||
$permissions = $module->getProvidedPermissions();
|
||||
$state = $moduleData->enabled ? ($moduleData->loaded ? 'enabled' : 'failed') : 'disabled'
|
||||
?>
|
||||
<h1 tabindex="-1">
|
||||
<?= $this->escape($module->getTitle()) ?>
|
||||
</h1>
|
||||
<table class="avp">
|
||||
<tr>
|
||||
<th><?= $this->escape($this->translate('Name')) ?></th>
|
||||
|
@ -252,7 +252,7 @@ class Manager
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the given module and remove it's enabled state
|
||||
* Disable the given module and remove its enabled state
|
||||
*
|
||||
* @param string $name The name of the module to disable
|
||||
*
|
||||
|
@ -113,6 +113,13 @@ class Module
|
||||
*/
|
||||
private $triedToLaunchConfigScript = false;
|
||||
|
||||
/**
|
||||
* Whether this module has been registered
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $registered = false;
|
||||
|
||||
/**
|
||||
* Provided permissions
|
||||
*
|
||||
@ -279,6 +286,10 @@ class Module
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if ($this->registered) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->registerAutoloader();
|
||||
try {
|
||||
$this->launchRunScript();
|
||||
@ -291,10 +302,22 @@ class Module
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->registerWebIntegration();
|
||||
$this->registered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this module has been registered
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRegistered()
|
||||
{
|
||||
return $this->registered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for an enabled module by name
|
||||
*
|
||||
@ -913,7 +936,7 @@ class Module
|
||||
*/
|
||||
protected function launchConfigScript()
|
||||
{
|
||||
if ($this->triedToLaunchConfigScript) {
|
||||
if ($this->triedToLaunchConfigScript || !$this->registered) {
|
||||
return;
|
||||
}
|
||||
$this->triedToLaunchConfigScript = true;
|
||||
|
@ -182,19 +182,24 @@ class Platform
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given Zend framework class exists
|
||||
* Return whether the given class exists
|
||||
*
|
||||
* @param string $name The name of the class to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function zendClassExists($name)
|
||||
public static function classExists($name)
|
||||
{
|
||||
if (@class_exists($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (@include str_replace('_', '/', $name) . '.php') !== false;
|
||||
if (strpos($name, '_') !== false) {
|
||||
// Assume it's a Zend-Framework class
|
||||
return (@include str_replace('_', '/', $name) . '.php') !== false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,7 +211,7 @@ class Platform
|
||||
*/
|
||||
public static function hasMysqlSupport()
|
||||
{
|
||||
return static::extensionLoaded('mysql') && static::zendClassExists('Zend_Db_Adapter_Pdo_Mysql');
|
||||
return static::extensionLoaded('mysql') && static::classExists('Zend_Db_Adapter_Pdo_Mysql');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,6 +223,6 @@ class Platform
|
||||
*/
|
||||
public static function hasPostgresqlSupport()
|
||||
{
|
||||
return static::extensionLoaded('pgsql') && static::zendClassExists('Zend_Db_Adapter_Pdo_Pgsql');
|
||||
return static::extensionLoaded('pgsql') && static::classExists('Zend_Db_Adapter_Pdo_Pgsql');
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +96,15 @@ class DbUserBackend extends UserBackend
|
||||
'SELECT password_hash FROM icingaweb_user WHERE name = :name AND active = 1'
|
||||
);
|
||||
}
|
||||
|
||||
$stmt->execute(array(':name' => $username));
|
||||
$stmt->bindColumn(1, $lob, PDO::PARAM_LOB);
|
||||
$stmt->fetch(PDO::FETCH_BOUND);
|
||||
return is_resource($lob) ? stream_get_contents($lob) : $lob;
|
||||
if (is_resource($lob)) {
|
||||
$lob = stream_get_contents($lob);
|
||||
}
|
||||
|
||||
return $this->conn->getDbType() === 'pgsql' ? pg_unescape_bytea($lob) : $lob;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@ use Icinga\Protocol\Ldap\Query;
|
||||
use Icinga\Protocol\Ldap\Connection;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Protocol\Ldap\Exception as LdapException;
|
||||
use Icinga\Protocol\Ldap\Expression;
|
||||
|
||||
class LdapUserBackend extends UserBackend
|
||||
{
|
||||
@ -25,14 +26,23 @@ class LdapUserBackend extends UserBackend
|
||||
|
||||
protected $userNameAttribute;
|
||||
|
||||
protected $customFilter;
|
||||
|
||||
protected $groupOptions;
|
||||
|
||||
public function __construct(Connection $conn, $userClass, $userNameAttribute, $baseDn, $groupOptions = null)
|
||||
{
|
||||
public function __construct(
|
||||
Connection $conn,
|
||||
$userClass,
|
||||
$userNameAttribute,
|
||||
$baseDn,
|
||||
$cutomFilter,
|
||||
$groupOptions = null
|
||||
) {
|
||||
$this->conn = $conn;
|
||||
$this->baseDn = trim($baseDn) !== '' ? $baseDn : $conn->getDN();
|
||||
$this->baseDn = trim($baseDn) ?: $conn->getDN();
|
||||
$this->userClass = $userClass;
|
||||
$this->userNameAttribute = $userNameAttribute;
|
||||
$this->customFilter = trim($cutomFilter);
|
||||
$this->groupOptions = $groupOptions;
|
||||
}
|
||||
|
||||
@ -43,12 +53,18 @@ class LdapUserBackend extends UserBackend
|
||||
*/
|
||||
protected function selectUsers()
|
||||
{
|
||||
return $this->conn->select()->setBase($this->baseDn)->from(
|
||||
$query = $this->conn->select()->setBase($this->baseDn)->from(
|
||||
$this->userClass,
|
||||
array(
|
||||
$this->userNameAttribute
|
||||
)
|
||||
);
|
||||
|
||||
if ($this->customFilter) {
|
||||
$query->addFilter(new Expression($this->customFilter));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,9 +104,10 @@ class LdapUserBackend extends UserBackend
|
||||
|
||||
if ($result === null) {
|
||||
throw new AuthenticationException(
|
||||
'No objects with objectClass="%s" in DN="%s" found.',
|
||||
'No objects with objectClass="%s" in DN="%s" found. (Filter: %s)',
|
||||
$this->userClass,
|
||||
$this->baseDn
|
||||
$this->baseDn,
|
||||
$this->customFilter ?: 'None'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@ abstract class UserBackend implements Countable
|
||||
$backendConfig->get('user_class', 'user'),
|
||||
$backendConfig->get('user_name_attribute', 'sAMAccountName'),
|
||||
$backendConfig->get('base_dn', $resource->getDN()),
|
||||
$backendConfig->get('filter'),
|
||||
$groupOptions
|
||||
);
|
||||
break;
|
||||
@ -130,6 +131,7 @@ abstract class UserBackend implements Countable
|
||||
$backendConfig->user_class,
|
||||
$backendConfig->user_name_attribute,
|
||||
$backendConfig->get('base_dn', $resource->getDN()),
|
||||
$backendConfig->get('filter'),
|
||||
$groupOptions
|
||||
);
|
||||
break;
|
||||
|
@ -16,7 +16,7 @@ use Icinga\Chart\Unit\LinearUnit;
|
||||
/**
|
||||
* Axis class for the GridChart class.
|
||||
*
|
||||
* Implements drawing functions for the axis and it's labels but delegates tick and label calculations
|
||||
* Implements drawing functions for the axis and its labels but delegates tick and label calculations
|
||||
* to the AxisUnit implementations
|
||||
*
|
||||
* @see GridChart
|
||||
|
30
library/Icinga/Protocol/Ldap/Expression.php
Normal file
30
library/Icinga/Protocol/Ldap/Expression.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Protocol\Ldap;
|
||||
|
||||
class Expression
|
||||
{
|
||||
protected $value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getValue();
|
||||
}
|
||||
}
|
@ -306,6 +306,19 @@ class Query
|
||||
return $paginator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter expression to this query
|
||||
*
|
||||
* @param Expression $expression
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function addFilter(Expression $expression)
|
||||
{
|
||||
$this->filters[] = $expression;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP filter that will be applied
|
||||
*
|
||||
@ -318,11 +331,15 @@ class Query
|
||||
throw new Exception('Object class is mandatory');
|
||||
}
|
||||
foreach ($this->filters as $key => $value) {
|
||||
$parts[] = sprintf(
|
||||
'%s=%s',
|
||||
LdapUtils::quoteForSearch($key),
|
||||
LdapUtils::quoteForSearch($value, true)
|
||||
);
|
||||
if ($value instanceof Expression) {
|
||||
$parts[] = (string) $value;
|
||||
} else {
|
||||
$parts[] = sprintf(
|
||||
'%s=%s',
|
||||
LdapUtils::quoteForSearch($key),
|
||||
LdapUtils::quoteForSearch($value, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (count($parts) > 1) {
|
||||
return '(&(' . implode(')(', $parts) . '))';
|
||||
|
@ -203,12 +203,14 @@ class DbStore extends PreferencesStore
|
||||
foreach ($preferences as $key => $value) {
|
||||
$db->update(
|
||||
$this->table,
|
||||
array(self::COLUMN_VALUE => $value),
|
||||
array(
|
||||
self::COLUMN_VALUE => $value,
|
||||
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
||||
),
|
||||
array(
|
||||
self::COLUMN_USERNAME . '=?' => $this->getUser()->getUsername(),
|
||||
$db->quoteIdentifier(self::COLUMN_SECTION) . '=?' => $section,
|
||||
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . '=?' => $key,
|
||||
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
||||
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . '=?' => $key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class BasePreferenceController extends ActionController
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the controller and collect all tabs for it from the application and it's modules
|
||||
* Initialize the controller and collect all tabs for it from the application and its modules
|
||||
*
|
||||
* @see ActionController::init()
|
||||
*/
|
||||
|
@ -157,7 +157,7 @@ class Form extends Zend_Form
|
||||
public static $defaultElementDecorators = array(
|
||||
array('ViewHelper', array('separator' => '')),
|
||||
array('Errors', array('separator' => '')),
|
||||
array('Help'),
|
||||
array('Help', array('placement' => 'PREPEND')),
|
||||
array('Label', array('separator' => '')),
|
||||
array('HtmlTag', array('tag' => 'div', 'class' => 'element'))
|
||||
);
|
||||
@ -622,7 +622,7 @@ class Form extends Zend_Form
|
||||
public function addSubForm(Zend_Form $form, $name = null, $order = null)
|
||||
{
|
||||
if ($form instanceof self) {
|
||||
$form->removeDecorator('Form');
|
||||
$form->setDecorators(array('FormElements')); // TODO: Makes it difficult to customise subform decorators..
|
||||
$form->setSubmitLabel('');
|
||||
$form->setTokenDisabled();
|
||||
$form->setUidDisabled();
|
||||
@ -743,7 +743,7 @@ class Form extends Zend_Form
|
||||
if (($cue = $this->getRequiredCue()) !== null && ($label = $element->getDecorator('label')) !== false) {
|
||||
$element->setLabel($this->getView()->escape($element->getLabel()));
|
||||
$label->setOption('escape', false);
|
||||
$label->setOption('requiredSuffix', sprintf(' <span aria-hidden="true">%s</span>', $cue));
|
||||
$label->setRequiredSuffix(sprintf(' <span aria-hidden="true">%s</span>', $cue));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,29 @@ use Icinga\Web\Form;
|
||||
*/
|
||||
class FormDescriptions extends Zend_Form_Decorator_Abstract
|
||||
{
|
||||
/**
|
||||
* A list of element class names to be ignored when detecting which message to use to describe required elements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $blacklist;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($options = null)
|
||||
{
|
||||
parent::__construct($options);
|
||||
$this->blacklist = array(
|
||||
'Zend_Form_Element_Hidden',
|
||||
'Zend_Form_Element_Submit',
|
||||
'Zend_Form_Element_Button',
|
||||
'Icinga\Web\Form\Element\Note',
|
||||
'Icinga\Web\Form\Element\Button',
|
||||
'Icinga\Web\Form\Element\CsrfCounterMeasure'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render form descriptions
|
||||
*
|
||||
@ -32,9 +55,16 @@ class FormDescriptions extends Zend_Form_Decorator_Abstract
|
||||
return $content;
|
||||
}
|
||||
|
||||
$descriptions = $form->getDescriptions();
|
||||
if (($requiredDesc = $this->getRequiredDescription($form)) !== null) {
|
||||
$descriptions[] = $requiredDesc;
|
||||
$descriptions = $this->recurseForm($form, $entirelyRequired);
|
||||
if ($entirelyRequired) {
|
||||
$descriptions[] = $form->getView()->translate(
|
||||
'All fields are required and must be filled in to complete the form.'
|
||||
);
|
||||
} elseif ($entirelyRequired === false) {
|
||||
$descriptions[] = $form->getView()->translate(sprintf(
|
||||
'Required fields are marked with %s and must be filled in to complete the form.',
|
||||
$form->getRequiredCue()
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($descriptions)) {
|
||||
@ -60,53 +90,57 @@ class FormDescriptions extends Zend_Form_Decorator_Abstract
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the description for the given form's required elements
|
||||
* Recurse the given form and return the descriptions for it and all of its subforms
|
||||
*
|
||||
* @param Form $form
|
||||
* @param Form $form The form to recurse
|
||||
* @param mixed $entirelyRequired Set by reference, true means all elements in the hierarchy are
|
||||
* required, false only a partial subset and null none at all
|
||||
* @param bool $elementsPassed Whether there were any elements passed during the recursion until now
|
||||
*
|
||||
* @return string|null
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequiredDescription(Form $form)
|
||||
protected function recurseForm(Form $form, & $entirelyRequired = null, $elementsPassed = false)
|
||||
{
|
||||
if (($cue = $form->getRequiredCue()) === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requiredLabels = array();
|
||||
$entirelyRequired = true;
|
||||
$partiallyRequired = false;
|
||||
$blacklist = array(
|
||||
'Zend_Form_Element_Hidden',
|
||||
'Zend_Form_Element_Submit',
|
||||
'Zend_Form_Element_Button',
|
||||
'Icinga\Web\Form\Element\Note',
|
||||
'Icinga\Web\Form\Element\Button',
|
||||
'Icinga\Web\Form\Element\CsrfCounterMeasure'
|
||||
);
|
||||
foreach ($form->getElements() as $element) {
|
||||
if (! in_array($element->getType(), $blacklist)) {
|
||||
if (! $element->isRequired()) {
|
||||
$entirelyRequired = false;
|
||||
} else {
|
||||
$partiallyRequired = true;
|
||||
if (($label = $element->getDecorator('label')) !== false) {
|
||||
$requiredLabels[] = $label;
|
||||
if ($form->getRequiredCue() !== null) {
|
||||
$partiallyRequired = $partiallyOptional = false;
|
||||
foreach ($form->getElements() as $element) {
|
||||
if (! in_array($element->getType(), $this->blacklist)) {
|
||||
if (! $element->isRequired()) {
|
||||
$partiallyOptional = true;
|
||||
if ($entirelyRequired) {
|
||||
$entirelyRequired = false;
|
||||
}
|
||||
} else {
|
||||
$partiallyRequired = true;
|
||||
if (($label = $element->getDecorator('label')) !== false) {
|
||||
$requiredLabels[] = $label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $elementsPassed) {
|
||||
$elementsPassed = $partiallyRequired || $partiallyOptional;
|
||||
if ($entirelyRequired === null && $partiallyRequired) {
|
||||
$entirelyRequired = ! $partiallyOptional;
|
||||
}
|
||||
} elseif ($entirelyRequired === null && $partiallyRequired) {
|
||||
$entirelyRequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($entirelyRequired && $partiallyRequired) {
|
||||
$descriptions = array($form->getDescriptions());
|
||||
foreach ($form->getSubForms() as $subForm) {
|
||||
$descriptions[] = $this->recurseForm($subForm, $entirelyRequired, $elementsPassed);
|
||||
}
|
||||
|
||||
if ($entirelyRequired) {
|
||||
foreach ($requiredLabels as $label) {
|
||||
$label->setRequiredSuffix('');
|
||||
}
|
||||
|
||||
return $form->getView()->translate('All fields are required and must be filled in to complete the form.');
|
||||
} elseif ($partiallyRequired) {
|
||||
return $form->getView()->translate(sprintf(
|
||||
'Required fields are marked with %s and must be filled in to complete the form.',
|
||||
$cue
|
||||
));
|
||||
}
|
||||
|
||||
return call_user_func_array('array_merge', $descriptions);
|
||||
}
|
||||
}
|
||||
|
@ -76,18 +76,35 @@ class Help extends Zend_Form_Decorator_Abstract
|
||||
*/
|
||||
public function render($content = '')
|
||||
{
|
||||
if ($content && ($description = $this->getElement()->getDescription()) !== null) {
|
||||
$element = $this->getElement();
|
||||
$description = $element->getDescription();
|
||||
$requirement = $element->getAttrib('requirement');
|
||||
unset($element->requirement);
|
||||
|
||||
$helpContent = '';
|
||||
if ($description || $requirement) {
|
||||
if ($this->accessible) {
|
||||
$content = '<span id="'
|
||||
$helpContent = '<span id="'
|
||||
. $this->getDescriptionId()
|
||||
. '" class="sr-only">'
|
||||
. $description
|
||||
. '</span>' . $content;
|
||||
. ($description && $requirement ? ' ' : '')
|
||||
. $requirement
|
||||
. '</span>';
|
||||
}
|
||||
|
||||
$content = $this->getView()->icon('help', $description, array('aria-hidden' => 'true')) . $content;
|
||||
$helpContent = $this->getView()->icon(
|
||||
'help',
|
||||
$description . ($description && $requirement ? ' ' : '') . $requirement,
|
||||
array('aria-hidden' => $this->accessible ? 'true' : 'false')
|
||||
) . $helpContent;
|
||||
}
|
||||
|
||||
return $content;
|
||||
switch ($this->getPlacement()) {
|
||||
case self::APPEND:
|
||||
return $content . $helpContent;
|
||||
case self::PREPEND:
|
||||
return $helpContent . $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ abstract class AbstractWidget
|
||||
{
|
||||
/**
|
||||
* If you are going to access the current view with the view() function,
|
||||
* it's instance is stored here for performance reasons.
|
||||
* its instance is stored here for performance reasons.
|
||||
*
|
||||
* @var Zend_View_Abstract
|
||||
*/
|
||||
|
@ -268,7 +268,7 @@ class HistoryColorGrid extends AbstractWidget {
|
||||
}
|
||||
$week++;
|
||||
}
|
||||
if ($day > cal_days_in_month(CAL_GREGORIAN, $month, $year)) {
|
||||
if ($day > date('t', mktime(0, 0, 0, $month, 1, $year))) {
|
||||
$month++;
|
||||
if ($month > 12) {
|
||||
$year++;
|
||||
|
@ -18,7 +18,7 @@ use Icinga\Web\Url;
|
||||
*
|
||||
* The terminology is as follows:
|
||||
* - Dashlet: A single view showing a specific url
|
||||
* - Pane: Aggregates one or more dashlets on one page, displays it's title as a tab
|
||||
* - Pane: Aggregates one or more dashlets on one page, displays its title as a tab
|
||||
* - Dashboard: Shows all panes
|
||||
*
|
||||
*/
|
||||
@ -347,7 +347,7 @@ class Dashboard extends AbstractWidget
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the default pane of this dashboard and returns it's name
|
||||
* Activates the default pane of this dashboard and returns its name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
@ -140,6 +140,10 @@ class Tab extends AbstractWidget
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
if (! $this->label) {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
@ -238,7 +242,7 @@ class Tab extends AbstractWidget
|
||||
$classes[] = 'active';
|
||||
}
|
||||
|
||||
$caption = $view->escape($this->label);
|
||||
$caption = $view->escape($this->getLabel());
|
||||
$tagParams = $this->tagParams;
|
||||
|
||||
if ($this->title) {
|
||||
|
@ -207,7 +207,7 @@ EOT;
|
||||
* with tab properties or an instance of an existing Tab
|
||||
*
|
||||
* @param string $name The new tab name
|
||||
* @param array|Tab $tab The tab itself of it's properties
|
||||
* @param array|Tab $tab The tab itself of its properties
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
@ -232,7 +232,7 @@ EOT;
|
||||
* of an existing Tab
|
||||
*
|
||||
* @param string $name The new tab name
|
||||
* @param array|Tab $tab The tab itself of it's properties
|
||||
* @param array|Tab $tab The tab itself of its properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
@ -279,7 +279,7 @@ EOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the dropdown area with it's tabs and return the resulting HTML
|
||||
* Render the dropdown area with its tabs and return the resulting HTML
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
Module: doc
|
||||
Version: 2.0.0
|
||||
Description: Documentation module
|
||||
Extracts, shows and exports documentation for Icinga Web 2 and it's modules.
|
||||
Extracts, shows and exports documentation for Icinga Web 2 and its modules.
|
||||
|
@ -3,13 +3,12 @@
|
||||
|
||||
namespace Icinga\Module\Monitoring;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Wizard;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Module\Setup\Setup;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
use Icinga\Module\Setup\RequirementSet;
|
||||
use Icinga\Module\Setup\Forms\SummaryPage;
|
||||
use Icinga\Module\Monitoring\Forms\Setup\WelcomePage;
|
||||
use Icinga\Module\Monitoring\Forms\Setup\BackendPage;
|
||||
@ -17,6 +16,8 @@ use Icinga\Module\Monitoring\Forms\Setup\InstancePage;
|
||||
use Icinga\Module\Monitoring\Forms\Setup\SecurityPage;
|
||||
use Icinga\Module\Monitoring\Forms\Setup\IdoResourcePage;
|
||||
use Icinga\Module\Monitoring\Forms\Setup\LivestatusResourcePage;
|
||||
use Icinga\Module\Setup\Requirement\ClassRequirement;
|
||||
use Icinga\Module\Setup\Requirement\PhpModuleRequirement;
|
||||
|
||||
/**
|
||||
* Monitoring Module Setup Wizard
|
||||
@ -135,22 +136,62 @@ class MonitoringWizard extends Wizard implements SetupWizard
|
||||
*/
|
||||
public function getRequirements()
|
||||
{
|
||||
$requirements = new Requirements();
|
||||
$set = new RequirementSet();
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_sockets',
|
||||
mt('monitoring', 'PHP Module: Sockets'),
|
||||
mt(
|
||||
// TODO(8254): Add this to the $backendSet
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Sockets',
|
||||
'description' => mt(
|
||||
'monitoring',
|
||||
'In case it\'s desired that a TCP connection is being used by Icinga Web 2 to'
|
||||
. ' access a Livestatus interface, the Sockets module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('sockets'),
|
||||
Platform::extensionLoaded('sockets') ? mt('monitoring', 'The PHP Module sockets is available.') : (
|
||||
mt('monitoring', 'The PHP Module sockets is not available.')
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
return $requirements;
|
||||
$backendSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mysqlSet = new RequirementSet(true);
|
||||
$mysqlSet->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'mysql',
|
||||
'alias' => 'PDO-MySQL',
|
||||
'description' => mt(
|
||||
'monitoring',
|
||||
'To access the IDO stored in a MySQL database the PDO-MySQL module for PHP is required.'
|
||||
)
|
||||
)));
|
||||
$mysqlSet->add(new ClassRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Zend_Db_Adapter_Pdo_Mysql',
|
||||
'alias' => mt('monitoring', 'Zend database adapter for MySQL'),
|
||||
'description' => mt(
|
||||
'monitoring',
|
||||
'The Zend database adapter for MySQL is required to access a MySQL database.'
|
||||
)
|
||||
)));
|
||||
$backendSet->merge($mysqlSet);
|
||||
$pgsqlSet = new RequirementSet(true);
|
||||
$pgsqlSet->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'pgsql',
|
||||
'alias' => 'PDO-PostgreSQL',
|
||||
'description' => mt(
|
||||
'monitoring',
|
||||
'To access the IDO stored in a PostgreSQL database the PDO-PostgreSQL module for PHP is required.'
|
||||
)
|
||||
)));
|
||||
$pgsqlSet->add(new ClassRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Zend_Db_Adapter_Pdo_Pgsql',
|
||||
'alias' => mt('monitoring', 'Zend database adapter for PostgreSQL'),
|
||||
'description' => mt(
|
||||
'monitoring',
|
||||
'The Zend database adapter for PostgreSQL is required to access a PostgreSQL database.'
|
||||
)
|
||||
)));
|
||||
$backendSet->merge($pgsqlSet);
|
||||
$set->merge($backendSet);
|
||||
|
||||
return $set;
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,8 @@ class AdminAccountPage extends Form
|
||||
ResourceFactory::createResource(new ConfigObject($this->resourceConfig)),
|
||||
$this->backendConfig['user_class'],
|
||||
$this->backendConfig['user_name_attribute'],
|
||||
$this->backendConfig['base_dn']
|
||||
$this->backendConfig['base_dn'],
|
||||
$this->backendConfig['filter']
|
||||
);
|
||||
} else {
|
||||
throw new LogicException(
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
namespace Icinga\Module\Setup\Forms;
|
||||
|
||||
use Exception;
|
||||
use Zend_Validate_NotEmpty;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\ErrorLabeller;
|
||||
@ -68,9 +69,12 @@ class LdapDiscoveryPage extends Form
|
||||
}
|
||||
|
||||
if (isset($data['domain']) && $data['domain']) {
|
||||
$this->discovery = Discovery::discoverDomain($data['domain']);
|
||||
if ($this->discovery->isSuccess()) {
|
||||
return true;
|
||||
try {
|
||||
$this->discovery = Discovery::discoverDomain($data['domain']);
|
||||
if ($this->discovery->isSuccess()) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
$this->addError(
|
||||
|
@ -4,7 +4,7 @@
|
||||
namespace Icinga\Module\Setup\Forms;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
|
||||
/**
|
||||
* Wizard page to list setup requirements
|
||||
@ -12,11 +12,11 @@ use Icinga\Module\Setup\Requirements;
|
||||
class RequirementsPage extends Form
|
||||
{
|
||||
/**
|
||||
* The requirements to list
|
||||
* The wizard
|
||||
*
|
||||
* @var Requirements
|
||||
* @var SetupWizard
|
||||
*/
|
||||
protected $requirements;
|
||||
protected $wizard;
|
||||
|
||||
/**
|
||||
* Initialize this page
|
||||
@ -28,30 +28,30 @@ class RequirementsPage extends Form
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the requirements to list
|
||||
* Set the wizard
|
||||
*
|
||||
* @param Requirements $requirements
|
||||
* @param SetupWizard $wizard
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRequirements(Requirements $requirements)
|
||||
public function setWizard(SetupWizard $wizard)
|
||||
{
|
||||
$this->requirements = $requirements;
|
||||
$this->wizard = $wizard;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the requirements to list
|
||||
* Return the wizard
|
||||
*
|
||||
* @return Requirements
|
||||
* @return SetupWizard
|
||||
*/
|
||||
public function getRequirements()
|
||||
public function getWizard()
|
||||
{
|
||||
return $this->requirements;
|
||||
return $this->wizard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given form data and check whether the requirements are fulfilled
|
||||
* Validate the given form data and check whether the wizard's requirements are fulfilled
|
||||
*
|
||||
* @param array $data The data to validate
|
||||
*
|
||||
@ -63,6 +63,6 @@ class RequirementsPage extends Form
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->requirements->fulfilled();
|
||||
return $this->wizard->getRequirements()->fulfilled();
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ $showRadioBoxes = strpos(strtolower(get_class($radioElem)), 'radio') !== false;
|
||||
<?php if (($byNameElem = $form->getElement('by_name')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $byNameElem; ?>
|
||||
<?= $showRadioBoxes ? $byNameElem->setAttrib('data-related-radiobtn', 'by_name') : $byNameElem; ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="by_name"<?= $radioElem->getValue() === 'by_name' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<input type="radio" id="by_name" name="user_type" value="by_name"<?= $radioElem->getValue() === 'by_name' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<?= $radioElem->getMultiOption('by_name'); ?>
|
||||
</label>
|
||||
</div>
|
||||
@ -35,12 +35,12 @@ $showRadioBoxes = strpos(strtolower(get_class($radioElem)), 'radio') !== false;
|
||||
<?php if (($existingUserElem = $form->getElement('existing_user')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $existingUserElem; ?>
|
||||
<?= $showRadioBoxes ? $existingUserElem->setAttrib('data-related-radiobtn', 'existing_user') : $existingUserElem; ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="existing_user"<?= $radioElem->getValue() === 'existing_user' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<input type="radio" id="existing_user" name="user_type" value="existing_user"<?= $radioElem->getValue() === 'existing_user' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<?= $radioElem->getMultiOption('existing_user'); ?>
|
||||
</label>
|
||||
</div>
|
||||
@ -50,14 +50,14 @@ $showRadioBoxes = strpos(strtolower(get_class($radioElem)), 'radio') !== false;
|
||||
<?php if (($newUserElem = $form->getElement('new_user')) !== null): ?>
|
||||
<div>
|
||||
<div class="instructions">
|
||||
<?= $newUserElem; ?>
|
||||
<?= $showRadioBoxes ? $newUserElem->setAttrib('data-related-radiobtn', 'new_user') : $newUserElem; ?>
|
||||
<?= $form->getElement('new_user_password'); ?>
|
||||
<?= $form->getElement('new_user_2ndpass'); ?>
|
||||
</div>
|
||||
<?php if ($showRadioBoxes): ?>
|
||||
<div class="radiobox">
|
||||
<label>
|
||||
<input type="radio" name="user_type" value="new_user"<?= $radioElem->getValue() === 'new_user' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<input type="radio" id="new_user" name="user_type" value="new_user"<?= $radioElem->getValue() === 'new_user' ? ' checked' : ''; ?> class="autosubmit" aria-required="true" required>
|
||||
<?= $radioElem->getMultiOption('new_user'); ?>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -1,64 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Icinga\Web\Wizard;
|
||||
use Icinga\Module\Setup\Requirements;
|
||||
|
||||
$requirements = $form->getRequirements();
|
||||
|
||||
?>
|
||||
<table class="requirements">
|
||||
<tbody>
|
||||
<?php foreach ($requirements as $requirement): ?>
|
||||
<tr>
|
||||
<td><h2><?= $requirement->title; ?></h2></td>
|
||||
<td style="width: 50%">
|
||||
<?php if (is_array($requirement->description)): ?>
|
||||
<ul>
|
||||
<?php foreach ($requirement->description as $desc): ?>
|
||||
<li><?= $desc; ?></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<?= $requirement->description; ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td class="state <?= $requirement->state === Requirements::STATE_OK ? 'fulfilled' : (
|
||||
$requirement->state === Requirements::STATE_OPTIONAL ? 'not-available' : 'missing'
|
||||
); ?>"><?= $requirement->message; ?></td>
|
||||
</tr>
|
||||
<h1>Icinga Web 2</h1>
|
||||
<?= $form->getWizard()->getRequirements(true); ?>
|
||||
<?php foreach ($form->getWizard()->getPage('setup_modules')->getModuleWizards() as $moduleName => $wizard): ?>
|
||||
<h1><?= ucwords($moduleName) . ' ' . $this->translate('Module'); ?></h1>
|
||||
<?= $wizard->getRequirements(); ?>
|
||||
<?php endforeach ?>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="btn-update">
|
||||
<div class="buttons">
|
||||
<?php $title = $this->translate('You may also need to restart the web-server for the changes to take effect!'); ?>
|
||||
<?= $this->qlink(
|
||||
$this->translate('Refresh'),
|
||||
null,
|
||||
null,
|
||||
array(
|
||||
'class' => 'button-like',
|
||||
'title' => $title,
|
||||
'aria-label' => sprintf($this->translate('Refresh the page; %s'), $title)
|
||||
)
|
||||
); ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<form id="<?= $form->getName(); ?>" name="<?= $form->getName(); ?>" enctype="<?= $form->getEncType(); ?>" method="<?= $form->getMethod(); ?>" action="<?= $form->getAction(); ?>">
|
||||
<?= $form->getElement($form->getTokenElementName()); ?>
|
||||
<?= $form->getElement($form->getUidElementName()); ?>
|
||||
<div class="buttons" style="margin: 0 0 1.5em;">
|
||||
<div class="buttons">
|
||||
<?= $form->getElement(Wizard::BTN_PREV); ?>
|
||||
<?php
|
||||
$btn = $form->getElement(Wizard::BTN_NEXT);
|
||||
if (false === $requirements->fulfilled()) {
|
||||
if (! $form->getWizard()->getRequirements()->fulfilled()) {
|
||||
$btn->setAttrib('disabled', 1);
|
||||
}
|
||||
echo $btn;
|
||||
?>
|
||||
<div class="requirements-refresh">
|
||||
<?php $title = $this->translate('You may also need to restart the web-server for the changes to take effect!'); ?>
|
||||
<?= $this->qlink(
|
||||
$this->translate('Refresh'),
|
||||
null,
|
||||
null,
|
||||
array(
|
||||
'class' => 'button-like',
|
||||
'title' => $title,
|
||||
'aria-label' => sprintf($this->translate('Refresh the page; %s'), $title)
|
||||
)
|
||||
); ?>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
279
modules/setup/library/Setup/Requirement.php
Normal file
279
modules/setup/library/Setup/Requirement.php
Normal file
@ -0,0 +1,279 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use LogicException;
|
||||
|
||||
abstract class Requirement
|
||||
{
|
||||
/**
|
||||
* The state of this requirement
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* A descriptive text representing the current state of this requirement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $stateText;
|
||||
|
||||
/**
|
||||
* The descriptions of this requirement
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $descriptions;
|
||||
|
||||
/**
|
||||
* The title of this requirement
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The condition of this requirement
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $condition;
|
||||
|
||||
/**
|
||||
* Whether this requirement is optional
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $optional;
|
||||
|
||||
/**
|
||||
* The alias to display the condition with in a human readable way
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $alias;
|
||||
|
||||
/**
|
||||
* Create a new requirement
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @throws LogicException In case there exists no setter for an option's key
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->optional = false;
|
||||
$this->descriptions = array();
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
$setMethod = 'set' . ucfirst($key);
|
||||
$addMethod = 'add' . ucfirst($key);
|
||||
if (method_exists($this, $setMethod)) {
|
||||
$this->$setMethod($value);
|
||||
} elseif (method_exists($this, $addMethod)) {
|
||||
$this->$addMethod($value);
|
||||
} else {
|
||||
throw LogicException('No setter found for option key: ' . $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of this requirement
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
$this->state = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state of this requirement
|
||||
*
|
||||
* Evaluates the requirement in case there is no state set yet.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
if ($this->state === null) {
|
||||
$this->state = $this->evaluate();
|
||||
}
|
||||
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a descriptive text for this requirement's current state
|
||||
*
|
||||
* @param string $text
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setStateText($text)
|
||||
{
|
||||
$this->stateText = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a descriptive text for this requirement's current state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStateText()
|
||||
{
|
||||
return $this->stateText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a description for this requirement
|
||||
*
|
||||
* @param string $description
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function addDescription($description)
|
||||
{
|
||||
$this->descriptions[] = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the descriptions of this wizard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDescriptions()
|
||||
{
|
||||
return $this->descriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title for this requirement
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title of this requirement
|
||||
*
|
||||
* In case there is no title set the alias is returned instead.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
if ($this->title === null) {
|
||||
return $this->getAlias();
|
||||
}
|
||||
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the condition for this requirement
|
||||
*
|
||||
* @param mixed $condition
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setCondition($condition)
|
||||
{
|
||||
$this->condition = $condition;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the condition of this requirement
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCondition()
|
||||
{
|
||||
return $this->condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this requirement is optional
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setOptional($state = true)
|
||||
{
|
||||
$this->optional = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this requirement is optional
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOptional()
|
||||
{
|
||||
return $this->optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alias to display the condition with in a human readable way
|
||||
*
|
||||
* @param string $alias
|
||||
*
|
||||
* @return Requirement
|
||||
*/
|
||||
public function setAlias($alias)
|
||||
{
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the alias to display the condition with in a human readable way
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate this requirement and return whether it is fulfilled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function evaluate();
|
||||
|
||||
/**
|
||||
* Return whether the given requirement equals this one
|
||||
*
|
||||
* @param Requirement $requirement
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(Requirement $requirement)
|
||||
{
|
||||
if ($requirement instanceof static) {
|
||||
return $this->getCondition() === $requirement->getCondition();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
28
modules/setup/library/Setup/Requirement/ClassRequirement.php
Normal file
28
modules/setup/library/Setup/Requirement/ClassRequirement.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class ClassRequirement extends Requirement
|
||||
{
|
||||
protected function evaluate()
|
||||
{
|
||||
$classNameOrPath = $this->getCondition();
|
||||
if (Platform::classExists($classNameOrPath)) {
|
||||
$this->setStateText(sprintf(
|
||||
mt('setup', 'The %s is available.', 'setup.requirement.class'),
|
||||
$this->getAlias() ?: $classNameOrPath . ' ' . mt('setup', 'class', 'setup.requirement.class')
|
||||
));
|
||||
return true;
|
||||
} else {
|
||||
$this->setStateText(sprintf(
|
||||
mt('setup', 'The %s is missing.', 'setup.requirement.class'),
|
||||
$this->getAlias() ?: $classNameOrPath . ' ' . mt('setup', 'class', 'setup.requirement.class')
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class ConfigDirectoryRequirement extends Requirement
|
||||
{
|
||||
public function getTitle()
|
||||
{
|
||||
$title = parent::getTitle();
|
||||
if ($title === null) {
|
||||
return mt('setup', 'Read- and writable configuration directory');
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
protected function evaluate()
|
||||
{
|
||||
$path = $this->getCondition();
|
||||
if (file_exists($path)) {
|
||||
$readable = is_readable($path);
|
||||
if ($readable && is_writable($path)) {
|
||||
$this->setStateText(sprintf(mt('setup', 'The directory %s is read- and writable.'), $path));
|
||||
return true;
|
||||
} else {
|
||||
$this->setStateText(sprintf(
|
||||
$readable
|
||||
? mt('setup', 'The directory %s is not writable.')
|
||||
: mt('setup', 'The directory %s is not readable.'),
|
||||
$path
|
||||
));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setStateText(sprintf(mt('setup', 'The directory %s does not exist.'), $path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
27
modules/setup/library/Setup/Requirement/OSRequirement.php
Normal file
27
modules/setup/library/Setup/Requirement/OSRequirement.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class OSRequirement extends Requirement
|
||||
{
|
||||
public function getTitle()
|
||||
{
|
||||
$title = parent::getTitle();
|
||||
if ($title === null) {
|
||||
return sprintf(mt('setup', '%s Platform'), ucfirst($this->getCondition()));
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
protected function evaluate()
|
||||
{
|
||||
$phpOS = Platform::getOperatingSystemName();
|
||||
$this->setStateText(sprintf(mt('setup', 'You are running PHP on a %s system.'), ucfirst($phpOS)));
|
||||
return strtolower($phpOS) === strtolower($this->getCondition());
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class PhpConfigRequirement extends Requirement
|
||||
{
|
||||
protected function evaluate()
|
||||
{
|
||||
list($configDirective, $value) = $this->getCondition();
|
||||
$configValue = Platform::getPhpConfig($configDirective);
|
||||
$this->setStateText(
|
||||
$configValue
|
||||
? sprintf(mt('setup', 'The PHP config `%s\' is set to "%s".'), $configDirective, $configValue)
|
||||
: sprintf(mt('setup', 'The PHP config `%s\' is not defined.'), $configDirective)
|
||||
);
|
||||
return is_bool($value) ? $configValue == $value : $configValue === $value;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class PhpModuleRequirement extends Requirement
|
||||
{
|
||||
public function getTitle()
|
||||
{
|
||||
$title = parent::getTitle();
|
||||
if ($title === $this->getAlias()) {
|
||||
if ($title === null) {
|
||||
$title = $this->getCondition();
|
||||
}
|
||||
|
||||
return sprintf(mt('setup', 'PHP Module: %s'), $title);
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
protected function evaluate()
|
||||
{
|
||||
$moduleName = $this->getCondition();
|
||||
if (Platform::extensionLoaded($moduleName)) {
|
||||
$this->setStateText(sprintf(
|
||||
mt('setup', 'The PHP module %s is available.'),
|
||||
$this->getAlias() ?: $moduleName
|
||||
));
|
||||
return true;
|
||||
} else {
|
||||
$this->setStateText(sprintf(
|
||||
mt('setup', 'The PHP module %s is missing.'),
|
||||
$this->getAlias() ?: $moduleName
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup\Requirement;
|
||||
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
|
||||
class PhpVersionRequirement extends Requirement
|
||||
{
|
||||
public function getTitle()
|
||||
{
|
||||
$title = parent::getTitle();
|
||||
if ($title === null) {
|
||||
return mt('setup', 'PHP Version');
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
protected function evaluate()
|
||||
{
|
||||
$phpVersion = Platform::getPhpVersion();
|
||||
$this->setStateText(sprintf(mt('setup', 'You are running PHP version %s.'), $phpVersion));
|
||||
list($operator, $requiredVersion) = $this->getCondition();
|
||||
return version_compare($phpVersion, $requiredVersion, $operator);
|
||||
}
|
||||
}
|
333
modules/setup/library/Setup/RequirementSet.php
Normal file
333
modules/setup/library/Setup/RequirementSet.php
Normal file
@ -0,0 +1,333 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use LogicException;
|
||||
use RecursiveIterator;
|
||||
|
||||
/**
|
||||
* Container to store and handle requirements
|
||||
*/
|
||||
class RequirementSet implements RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* Mode AND (all requirements must be met)
|
||||
*/
|
||||
const MODE_AND = 0;
|
||||
|
||||
/**
|
||||
* Mode OR (at least one requirement must be met)
|
||||
*/
|
||||
const MODE_OR = 1;
|
||||
|
||||
/**
|
||||
* Whether all requirements meet their condition
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Whether this set is optional
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $optional;
|
||||
|
||||
/**
|
||||
* The mode by which the requirements are evaluated
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mode;
|
||||
|
||||
/**
|
||||
* The registered requirements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $requirements;
|
||||
|
||||
/**
|
||||
* The raw state of this set's requirements
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $forcedState;
|
||||
|
||||
/**
|
||||
* Initialize a new set of requirements
|
||||
*
|
||||
* @param bool $optional Whether this set is optional
|
||||
* @param int $mode The mode by which to evaluate this set
|
||||
*/
|
||||
public function __construct($optional = false, $mode = null)
|
||||
{
|
||||
$this->optional = $optional;
|
||||
$this->requirements = array();
|
||||
$this->setMode($mode ?: static::MODE_AND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state of this set
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
$this->state = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state of this set
|
||||
*
|
||||
* Alias for RequirementSet::fulfilled(true).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->fulfilled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this set of requirements should be optional
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function setOptional($state = true)
|
||||
{
|
||||
$this->optional = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this set of requirements is optional
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isOptional()
|
||||
{
|
||||
return $this->optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode by which to evaluate the requirements
|
||||
*
|
||||
* @param int $mode
|
||||
*
|
||||
* @return RequirementSet
|
||||
*
|
||||
* @throws LogicException In case the given mode is invalid
|
||||
*/
|
||||
public function setMode($mode)
|
||||
{
|
||||
if ($mode !== static::MODE_AND && $mode !== static::MODE_OR) {
|
||||
throw new LogicException(sprintf('Invalid mode %u given.'), $mode);
|
||||
}
|
||||
|
||||
$this->mode = $mode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode by which the requirements are evaluated
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMode()
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a requirement
|
||||
*
|
||||
* @param Requirement $requirement The requirement to add
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function add(Requirement $requirement)
|
||||
{
|
||||
$merged = false;
|
||||
foreach ($this->requirements as $knownRequirement) {
|
||||
if ($knownRequirement instanceof Requirement && $requirement->equals($knownRequirement)) {
|
||||
$knownRequirement->setOptional($requirement->isOptional());
|
||||
foreach ($requirement->getDescriptions() as $description) {
|
||||
$knownRequirement->addDescription($description);
|
||||
}
|
||||
|
||||
$merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $merged) {
|
||||
$this->requirements[] = $requirement;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered requirements
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given set of requirements
|
||||
*
|
||||
* @param RequirementSet $set The set to register
|
||||
*
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function merge(RequirementSet $set)
|
||||
{
|
||||
if ($this->getMode() === $set->getMode() && $this->isOptional() === $set->isOptional()) {
|
||||
foreach ($set->getAll() as $requirement) {
|
||||
if ($requirement instanceof static) {
|
||||
$this->merge($requirement);
|
||||
} else {
|
||||
$this->add($requirement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->requirements[] = $set;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether all requirements can successfully be evaluated based on the current mode
|
||||
*
|
||||
* In case this is a optional set of requirements (and $force is false), true is returned immediately.
|
||||
*
|
||||
* @param bool $force Whether to ignore the optionality of a set or single requirement
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fulfilled($force = false)
|
||||
{
|
||||
$state = $this->isOptional();
|
||||
if (! $force && $state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $force && $this->state !== null) {
|
||||
return $this->state;
|
||||
} elseif ($force && $this->forcedState !== null) {
|
||||
return $this->forcedState;
|
||||
}
|
||||
|
||||
$self = $this->requirements;
|
||||
foreach ($self as $requirement) {
|
||||
if ($requirement->getState()) {
|
||||
$state = true;
|
||||
if ($this->getMode() === static::MODE_OR) {
|
||||
break;
|
||||
}
|
||||
} elseif ($force || !$requirement->isOptional()) {
|
||||
$state = false;
|
||||
if ($this->getMode() === static::MODE_AND) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($force) {
|
||||
return $this->forcedState = $state;
|
||||
}
|
||||
|
||||
return $this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the current element represents a nested set of requirements
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
$current = $this->current();
|
||||
return $current instanceof static;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a iterator for the current nested set of requirements
|
||||
*
|
||||
* @return RecursiveIterator
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the iterator to its first element
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the current iterator position is valid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->key() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element in the iteration
|
||||
*
|
||||
* @return Requirement|RequirementSet
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position of the current element in the iteration
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the iterator to the next element
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
next($this->requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this set of requirements rendered as HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$renderer = new RequirementsRenderer($this);
|
||||
return (string) $renderer;
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Container to store and handle requirements
|
||||
*
|
||||
* TODO: Requirements should be registered as objects with a specific purpose (PhpModRequirement, PhpIniRequirement, ..)
|
||||
* so that it's not necessary to define unique identifiers which may differ between different modules.
|
||||
*/
|
||||
class Requirements implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Identifier representing the state OK
|
||||
*/
|
||||
const STATE_OK = 2;
|
||||
|
||||
/**
|
||||
* Identifier representing the state OPTIONAL
|
||||
*/
|
||||
const STATE_OPTIONAL = 1;
|
||||
|
||||
/**
|
||||
* Identifier representing the state MANDATORY
|
||||
*/
|
||||
const STATE_MANDATORY = 0;
|
||||
|
||||
/**
|
||||
* The registered requirements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $requirements = array();
|
||||
|
||||
/**
|
||||
* Register a requirement
|
||||
*
|
||||
* @param object $requirement The requirement to add
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add($name, $requirement)
|
||||
{
|
||||
$this->requirements[$name] = array_key_exists($name, $this->requirements)
|
||||
? $this->combine($this->requirements[$name], $requirement)
|
||||
: $requirement;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the two given requirements
|
||||
*
|
||||
* Returns the most important requirement with the description from the other one being added.
|
||||
*
|
||||
* @param object $oldRequirement
|
||||
* @param object $newRequirement
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function combine($oldRequirement, $newRequirement)
|
||||
{
|
||||
if ($newRequirement->state === static::STATE_MANDATORY && $oldRequirement->state === static::STATE_OPTIONAL) {
|
||||
$tempRequirement = $oldRequirement;
|
||||
$oldRequirement = $newRequirement;
|
||||
$newRequirement = $tempRequirement;
|
||||
}
|
||||
|
||||
if (! is_array($oldRequirement->description)) {
|
||||
$oldRequirement->description = array($oldRequirement->description);
|
||||
}
|
||||
|
||||
$oldRequirement->description[] = $newRequirement->description;
|
||||
return $oldRequirement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered requirements
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator of all registered requirements
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->getAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an optional requirement
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @param string $description
|
||||
* @param bool $state
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addOptional($name, $title, $description, $state, $message)
|
||||
{
|
||||
$this->add(
|
||||
$name,
|
||||
(object) array(
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'description' => $description,
|
||||
'state' => (bool) $state ? static::STATE_OK : static::STATE_OPTIONAL
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a mandatory requirement
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @param string $description
|
||||
* @param bool $state
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addMandatory($name, $title, $description, $state, $message)
|
||||
{
|
||||
$this->add(
|
||||
$name,
|
||||
(object) array(
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'description' => $description,
|
||||
'state' => (bool) $state ? static::STATE_OK : static::STATE_MANDATORY
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given requirements
|
||||
*
|
||||
* @param Requirements $requirements The requirements to register
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function merge(Requirements $requirements)
|
||||
{
|
||||
foreach ($requirements->getAll() as $name => $requirement) {
|
||||
$this->add($name, $requirement);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make all registered requirements being optional
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function allOptional()
|
||||
{
|
||||
foreach ($this->getAll() as $requirement) {
|
||||
if ($requirement->state === static::STATE_MANDATORY) {
|
||||
$requirement->state = static::STATE_OPTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether all mandatory requirements are fulfilled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fulfilled()
|
||||
{
|
||||
foreach ($this->getAll() as $requirement) {
|
||||
if ($requirement->state === static::STATE_MANDATORY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
83
modules/setup/library/Setup/RequirementsRenderer.php
Normal file
83
modules/setup/library/Setup/RequirementsRenderer.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Setup;
|
||||
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
class RequirementsRenderer extends RecursiveIteratorIterator
|
||||
{
|
||||
public function beginIteration()
|
||||
{
|
||||
$this->tags[] = '<table class="requirements">';
|
||||
$this->tags[] = '<tbody>';
|
||||
}
|
||||
|
||||
public function endIteration()
|
||||
{
|
||||
$this->tags[] = '</tbody>';
|
||||
$this->tags[] = '</table>';
|
||||
}
|
||||
|
||||
public function beginChildren()
|
||||
{
|
||||
$this->tags[] = '<tr>';
|
||||
$currentSet = $this->getSubIterator();
|
||||
$state = $currentSet->getState() ? 'fulfilled' : (
|
||||
$currentSet->isOptional() ? 'not-available' : 'missing'
|
||||
);
|
||||
$colSpanRequired = $this->hasSingleRequirements($this->getSubIterator($this->getDepth() - 1));
|
||||
$this->tags[] = '<td class="set-state ' . $state . '"' . ($colSpanRequired ? ' colspan=3' : '') . '>';
|
||||
$this->beginIteration();
|
||||
}
|
||||
|
||||
public function endChildren()
|
||||
{
|
||||
$this->endIteration();
|
||||
$this->tags[] = '</td>';
|
||||
$this->tags[] = '</tr>';
|
||||
}
|
||||
|
||||
protected function hasSingleRequirements(RequirementSet $requirements)
|
||||
{
|
||||
$set = $requirements->getAll();
|
||||
foreach ($set as $entry) {
|
||||
if ($entry instanceof Requirement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
foreach ($this as $requirement) {
|
||||
$this->tags[] = '<tr>';
|
||||
$this->tags[] = '<td class="title"><h2>' . $requirement->getTitle() . '</h2></td>';
|
||||
$this->tags[] = '<td class="desc">';
|
||||
$descriptions = $requirement->getDescriptions();
|
||||
if (count($descriptions) > 1) {
|
||||
$this->tags[] = '<ul>';
|
||||
foreach ($descriptions as $d) {
|
||||
$this->tags[] = '<li>' . $d . '</li>';
|
||||
}
|
||||
$this->tags[] = '</ul>';
|
||||
} elseif (! empty($descriptions)) {
|
||||
$this->tags[] = $descriptions[0];
|
||||
}
|
||||
$this->tags[] = '</td>';
|
||||
$this->tags[] = '<td class="state ' . ($requirement->getState() ? 'fulfilled' : (
|
||||
$requirement->isOptional() ? 'not-available' : 'missing'
|
||||
)) . '">' . $requirement->getStateText() . '</td>';
|
||||
$this->tags[] = '</tr>';
|
||||
}
|
||||
|
||||
return implode("\n", $this->tags);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ interface SetupWizard
|
||||
/**
|
||||
* Return the requirements of this wizard
|
||||
*
|
||||
* @return Requirements
|
||||
* @return RequirementSet
|
||||
*/
|
||||
public function getRequirements();
|
||||
}
|
||||
|
@ -126,11 +126,15 @@ class AuthenticationStep extends Step
|
||||
. '</tr>'
|
||||
. ($authType === 'ldap' ? (
|
||||
'<tr>'
|
||||
. '<td><strong>' . t('User Object Class') . '</strong></td>'
|
||||
. '<td><strong>' . mt('setup', 'User Object Class') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['user_class'] . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . t('User Name Attribute') . '</strong></td>'
|
||||
. '<td><strong>' . mt('setup', 'Custom Filter') . '</strong></td>'
|
||||
. '<td>' . trim($this->data['backendConfig']['filter']) ?: t('None', 'auth.ldap.filter') . '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
. '<td><strong>' . mt('setup', 'User Name Attribute') . '</strong></td>'
|
||||
. '<td>' . $this->data['backendConfig']['user_name_attribute'] . '</td>'
|
||||
. '</tr>'
|
||||
) : ($authType === 'external' ? (
|
||||
|
@ -9,7 +9,6 @@ use Icinga\Web\Wizard;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Module\Setup\Forms\ModulePage;
|
||||
use Icinga\Module\Setup\Forms\WelcomePage;
|
||||
use Icinga\Module\Setup\Forms\SummaryPage;
|
||||
@ -29,8 +28,13 @@ use Icinga\Module\Setup\Steps\GeneralConfigStep;
|
||||
use Icinga\Module\Setup\Steps\ResourceStep;
|
||||
use Icinga\Module\Setup\Steps\AuthenticationStep;
|
||||
use Icinga\Module\Setup\Utils\EnableModuleStep;
|
||||
use Icinga\Module\Setup\Utils\MakeDirStep;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
use Icinga\Module\Setup\Requirement\OSRequirement;
|
||||
use Icinga\Module\Setup\Requirement\ClassRequirement;
|
||||
use Icinga\Module\Setup\Requirement\PhpConfigRequirement;
|
||||
use Icinga\Module\Setup\Requirement\PhpModuleRequirement;
|
||||
use Icinga\Module\Setup\Requirement\PhpVersionRequirement;
|
||||
use Icinga\Module\Setup\Requirement\ConfigDirectoryRequirement;
|
||||
|
||||
/**
|
||||
* Icinga Web 2 Setup Wizard
|
||||
@ -111,7 +115,7 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
public function setupPage(Form $page, Request $request)
|
||||
{
|
||||
if ($page->getName() === 'setup_requirements') {
|
||||
$page->setRequirements($this->getRequirements());
|
||||
$page->setWizard($this);
|
||||
} elseif ($page->getName() === 'setup_preferences_type') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
@ -351,203 +355,159 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
/**
|
||||
* @see SetupWizard::getRequirements()
|
||||
*/
|
||||
public function getRequirements()
|
||||
public function getRequirements($skipModules = false)
|
||||
{
|
||||
$requirements = new Requirements();
|
||||
$set = new RequirementSet();
|
||||
|
||||
$phpVersion = Platform::getPhpVersion();
|
||||
$requirements->addMandatory(
|
||||
'php_version_>=_5_3_2',
|
||||
mt('setup', 'PHP Version'),
|
||||
mt(
|
||||
$set->add(new PhpVersionRequirement(array(
|
||||
'condition' => array('>=', '5.3.2'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Running Icinga Web 2 requires PHP version 5.3.2. Advanced features'
|
||||
. ' like the built-in web server require PHP version 5.4.'
|
||||
),
|
||||
version_compare($phpVersion, '5.3.2', '>='),
|
||||
sprintf(mt('setup', 'You are running PHP version %s.'), $phpVersion)
|
||||
);
|
||||
)
|
||||
)));
|
||||
|
||||
$defaultTimezone = Platform::getPhpConfig('date.timezone');
|
||||
$requirements->addMandatory(
|
||||
'existing_default_timezone',
|
||||
mt('setup', 'Default Timezone'),
|
||||
sprintf(
|
||||
$set->add(new PhpConfigRequirement(array(
|
||||
'condition' => array('date.timezone', true),
|
||||
'title' => mt('setup', 'Default Timezone'),
|
||||
'description' => sprintf(
|
||||
mt('setup', 'It is required that a default timezone has been set using date.timezone in %s.'),
|
||||
php_ini_loaded_file() ?: 'php.ini'
|
||||
),
|
||||
$defaultTimezone,
|
||||
$defaultTimezone ? sprintf(mt('setup', 'Your default timezone is: %s'), $defaultTimezone) : (
|
||||
mt('setup', 'You did not define a default timezone.')
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'platform=linux',
|
||||
mt('setup', 'Linux Platform'),
|
||||
mt(
|
||||
$set->add(new OSRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'linux',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Icinga Web 2 is developed for and tested on Linux. While we cannot'
|
||||
. ' guarantee they will, other platforms may also perform as well.'
|
||||
),
|
||||
Platform::isLinux(),
|
||||
sprintf(mt('setup', 'You are running PHP on a %s system.'), Platform::getOperatingSystemName())
|
||||
);
|
||||
|
||||
$requirements->addMandatory(
|
||||
'existing_php_mod_openssl',
|
||||
mt('setup', 'PHP Module: OpenSSL'),
|
||||
mt('setup', 'The PHP module for OpenSSL is required to generate cryptographically safe password salts.'),
|
||||
Platform::extensionLoaded('openssl'),
|
||||
Platform::extensionLoaded('openssl') ? mt('setup', 'The PHP module for OpenSSL is available.') : (
|
||||
mt('setup', 'The PHP module for OpenSSL is missing.')
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_json',
|
||||
mt('setup', 'PHP Module: JSON'),
|
||||
mt('setup', 'The JSON module for PHP is required for various export functionalities as well as APIs.'),
|
||||
Platform::extensionLoaded('json'),
|
||||
Platform::extensionLoaded('json') ? mt('setup', 'The PHP module JSON is available.') : (
|
||||
mt('setup', 'The PHP module JSON is missing.')
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'condition' => 'OpenSSL',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'The PHP module for OpenSSL is required to generate cryptographically safe password salts.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_ldap',
|
||||
mt('setup', 'PHP Module: LDAP'),
|
||||
mt('setup', 'If you\'d like to authenticate users using LDAP the corresponding PHP module is required'),
|
||||
Platform::extensionLoaded('ldap'),
|
||||
Platform::extensionLoaded('ldap') ? mt('setup', 'The PHP module LDAP is available') : (
|
||||
mt('setup', 'The PHP module LDAP is missing')
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'JSON',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'The JSON module for PHP is required for various export functionalities as well as APIs.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_intl',
|
||||
mt('setup', 'PHP Module: INTL'),
|
||||
mt(
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'LDAP',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'If you\'d like to authenticate users using LDAP the corresponding PHP module is required.'
|
||||
)
|
||||
)));
|
||||
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'INTL',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'If you want your users to benefit from language, timezone and date/time'
|
||||
. ' format negotiation, the INTL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('intl'),
|
||||
Platform::extensionLoaded('intl') ? mt('setup', 'The PHP module INTL is available') : (
|
||||
mt('setup', 'The PHP module INTL is missing')
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
// TODO(6172): Remove this requirement once we do not ship dompdf with Icinga Web 2 anymore
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_dom',
|
||||
mt('setup', 'PHP Module: DOM'),
|
||||
mt('setup', 'To be able to export views and reports to PDF, the DOM module for PHP is required.'),
|
||||
Platform::extensionLoaded('dom'),
|
||||
Platform::extensionLoaded('dom') ? mt('setup', 'The PHP module DOM is available') : (
|
||||
mt('setup', 'The PHP module DOM is missing')
|
||||
)
|
||||
);
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_gd',
|
||||
mt('setup', 'PHP Module: GD'),
|
||||
mt(
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'DOM',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'In case you want views being exported to PDF,'
|
||||
. ' you\'ll need the GD extension for PHP.'
|
||||
),
|
||||
Platform::extensionLoaded('gd'),
|
||||
Platform::extensionLoaded('gd') ? mt('setup', 'The PHP module GD is available') : (
|
||||
mt('setup', 'The PHP module GD is missing')
|
||||
'To be able to export views and reports to PDF, the DOM module for PHP is required.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_imagick',
|
||||
mt('setup', 'PHP Module: Imagick'),
|
||||
mt(
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'GD',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'In case you want graphs being exported to PDF as well'
|
||||
. ', you\'ll need the ImageMagick extension for PHP.'
|
||||
),
|
||||
Platform::extensionLoaded('imagick'),
|
||||
Platform::extensionLoaded('imagick') ? mt('setup', 'The PHP module Imagick is available') : (
|
||||
mt('setup', 'The PHP module Imagick is missing')
|
||||
'In case you want views being exported to PDF, you\'ll need the GD extension for PHP.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_pdo_mysql',
|
||||
mt('setup', 'PHP Module: PDO-MySQL'),
|
||||
mt(
|
||||
$set->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Imagick',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Is Icinga Web 2 supposed to access a MySQL database the PDO-MySQL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('mysql'),
|
||||
Platform::extensionLoaded('mysql') ? mt('setup', 'The PHP module PDO-MySQL is available.') : (
|
||||
mt('setup', 'The PHP module PDO-MySQL is missing.')
|
||||
'In case you want graphs being exported to PDF as well, you\'ll need the ImageMagick extension for PHP.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
$requirements->addOptional(
|
||||
'existing_php_mod_pdo_pgsql',
|
||||
mt('setup', 'PHP Module: PDO-PostgreSQL'),
|
||||
mt(
|
||||
$mysqlSet = new RequirementSet(true);
|
||||
$mysqlSet->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'mysql',
|
||||
'alias' => 'PDO-MySQL',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'Is Icinga Web 2 supposed to access a PostgreSQL database'
|
||||
. ' the PDO-PostgreSQL module for PHP is required.'
|
||||
),
|
||||
Platform::extensionLoaded('pgsql'),
|
||||
Platform::extensionLoaded('pgsql') ? mt('setup', 'The PHP module PDO-PostgreSQL is available.') : (
|
||||
mt('setup', 'The PHP module PDO-PostgreSQL is missing.')
|
||||
'To store users or preferences in a MySQL database the PDO-MySQL module for PHP is required.'
|
||||
)
|
||||
);
|
||||
|
||||
$mysqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Mysql');
|
||||
$requirements->addOptional(
|
||||
'existing_class_Zend_Db_Adapter_Pdo_Mysql',
|
||||
mt('setup', 'Zend Database Adapter For MySQL'),
|
||||
mt('setup', 'The Zend database adapter for MySQL is required to access a MySQL database.'),
|
||||
$mysqlAdapterFound,
|
||||
$mysqlAdapterFound ? mt('setup', 'The Zend database adapter for MySQL is available.') : (
|
||||
mt('setup', 'The Zend database adapter for MySQL is missing.')
|
||||
)));
|
||||
$mysqlSet->add(new ClassRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Zend_Db_Adapter_Pdo_Mysql',
|
||||
'alias' => mt('setup', 'Zend database adapter for MySQL'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'The Zend database adapter for MySQL is required to access a MySQL database.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
$set->merge($mysqlSet);
|
||||
|
||||
$pgsqlAdapterFound = Platform::zendClassExists('Zend_Db_Adapter_Pdo_Pgsql');
|
||||
$requirements->addOptional(
|
||||
'existing_class_Zend_Db_Adapter_Pdo_Pgsql',
|
||||
mt('setup', 'Zend Database Adapter For PostgreSQL'),
|
||||
mt('setup', 'The Zend database adapter for PostgreSQL is required to access a PostgreSQL database.'),
|
||||
$pgsqlAdapterFound,
|
||||
$pgsqlAdapterFound ? mt('setup', 'The Zend database adapter for PostgreSQL is available.') : (
|
||||
mt('setup', 'The Zend database adapter for PostgreSQL is missing.')
|
||||
$pgsqlSet = new RequirementSet(true);
|
||||
$pgsqlSet->add(new PhpModuleRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'pgsql',
|
||||
'alias' => 'PDO-PostgreSQL',
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'To store users or preferences in a PostgreSQL database the PDO-PostgreSQL module for PHP is required.'
|
||||
)
|
||||
);
|
||||
)));
|
||||
$pgsqlSet->add(new ClassRequirement(array(
|
||||
'optional' => true,
|
||||
'condition' => 'Zend_Db_Adapter_Pdo_Pgsql',
|
||||
'alias' => mt('setup', 'Zend database adapter for PostgreSQL'),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'The Zend database adapter for PostgreSQL is required to access a PostgreSQL database.'
|
||||
)
|
||||
)));
|
||||
$set->merge($pgsqlSet);
|
||||
|
||||
$configDir = Icinga::app()->getConfigDir();
|
||||
$requirements->addMandatory(
|
||||
'writable_directory_' . $configDir,
|
||||
mt('setup', 'Writable Config Directory'),
|
||||
mt(
|
||||
$set->add(new ConfigDirectoryRequirement(array(
|
||||
'condition' => Icinga::app()->getConfigDir(),
|
||||
'description' => mt(
|
||||
'setup',
|
||||
'The Icinga Web 2 configuration directory defaults to "/etc/icingaweb2", if' .
|
||||
' not explicitly set in the environment variable "ICINGAWEB_CONFIGDIR".'
|
||||
),
|
||||
is_writable($configDir),
|
||||
sprintf(
|
||||
is_writable($configDir) ? mt('setup', 'The current configuration directory is writable: %s') : (
|
||||
mt('setup', 'The current configuration directory is not writable: %s')
|
||||
),
|
||||
$configDir
|
||||
)
|
||||
);
|
||||
)));
|
||||
|
||||
foreach ($this->getWizards() as $wizard) {
|
||||
$requirements->merge($wizard->getRequirements());
|
||||
if (! $skipModules) {
|
||||
foreach ($this->getWizards() as $wizard) {
|
||||
$set->merge($wizard->getRequirements());
|
||||
}
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
return $set;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ location ~ ^{urlPath}/index\.php(.*)$ {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME {documentRoot}/index.php;
|
||||
fastcgi_param ICINGAWEB_CONFIGDIR {configDir};
|
||||
fastcgi_param REMOTE_USER $remote_user;
|
||||
}
|
||||
|
||||
location ~ ^{urlPath}(.+)? {
|
||||
|
496
modules/setup/test/php/library/Setup/RequirementSetTest.php
Normal file
496
modules/setup/test/php/library/Setup/RequirementSetTest.php
Normal file
@ -0,0 +1,496 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Tests\Icinga\Module\Setup;
|
||||
|
||||
use Icinga\Test\BaseTestCase;
|
||||
use Icinga\Module\Setup\Requirement;
|
||||
use Icinga\Module\Setup\RequirementSet;
|
||||
|
||||
class TrueRequirement extends Requirement
|
||||
{
|
||||
protected function evaluate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FalseRequirement extends Requirement
|
||||
{
|
||||
protected function evaluate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class RequirementSetTest extends BaseTestCase
|
||||
{
|
||||
public function testFlatMandatoryRequirementsOfTypeAnd()
|
||||
{
|
||||
$emptySet = new RequirementSet();
|
||||
$this->assertFalse($emptySet->fulfilled(), 'A empty mandatory set of type and is fulfilled');
|
||||
|
||||
$singleTrueSet = new RequirementSet();
|
||||
$singleTrueSet->add(new TrueRequirement());
|
||||
$this->assertTrue(
|
||||
$singleTrueSet->fulfilled(),
|
||||
'A mandatory set of type and with a single TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$singleFalseSet = new RequirementSet();
|
||||
$singleFalseSet->add(new FalseRequirement());
|
||||
$this->assertFalse(
|
||||
$singleFalseSet->fulfilled(),
|
||||
'A mandatory set of type and with a single FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$mixedSet = new RequirementSet();
|
||||
$mixedSet->add(new TrueRequirement());
|
||||
$mixedSet->add(new FalseRequirement());
|
||||
$this->assertFalse(
|
||||
$mixedSet->fulfilled(),
|
||||
'A mandatory set of type and with one True- and one FalseRequirement is fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlatOptionalRequirementsOfTypeAnd()
|
||||
{
|
||||
$emptySet = new RequirementSet(true);
|
||||
$this->assertTrue($emptySet->fulfilled(), 'A empty optional set of type and is not fulfilled');
|
||||
|
||||
$singleTrueSet = new RequirementSet(true);
|
||||
$singleTrueSet->add(new TrueRequirement());
|
||||
$this->assertTrue(
|
||||
$singleTrueSet->fulfilled(),
|
||||
'A optional set of type and with a single TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$singleFalseSet = new RequirementSet(true);
|
||||
$singleFalseSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$singleFalseSet->fulfilled(),
|
||||
'A optional set of type and with a single FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$mixedSet = new RequirementSet(true);
|
||||
$mixedSet->add(new TrueRequirement());
|
||||
$mixedSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$mixedSet->fulfilled(),
|
||||
'A optional set of type and with one True- and one FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlatMixedRequirementsOfTypeAnd()
|
||||
{
|
||||
$mandatoryOptionalTrueSet = new RequirementSet();
|
||||
$mandatoryOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$mandatoryOptionalTrueSet->add(new FalseRequirement());
|
||||
$this->assertFalse(
|
||||
$mandatoryOptionalTrueSet->fulfilled(),
|
||||
'A mandatory set of type and with one optional True- and one mandatory FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$mandatoryOptionalFalseSet = new RequirementSet();
|
||||
$mandatoryOptionalFalseSet->add(new TrueRequirement());
|
||||
$mandatoryOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$this->assertTrue(
|
||||
$mandatoryOptionalFalseSet->fulfilled(),
|
||||
'A mandatory set of type and with one mandatory True- and one optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalTrueSet = new RequirementSet(true);
|
||||
$optionalOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$optionalOptionalTrueSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$optionalOptionalTrueSet->fulfilled(),
|
||||
'A optional set of type and with one optional True- and one mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalFalseSet = new RequirementSet(true);
|
||||
$optionalOptionalFalseSet->add(new TrueRequirement());
|
||||
$optionalOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$this->assertTrue(
|
||||
$optionalOptionalFalseSet->fulfilled(),
|
||||
'A optional set of type and with one mandatory True- and one optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlatMandatoryRequirementsOfTypeOr()
|
||||
{
|
||||
$emptySet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$this->assertFalse($emptySet->fulfilled(), 'A empty mandatory set of type or is fulfilled');
|
||||
|
||||
$singleTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$singleTrueSet->add(new TrueRequirement());
|
||||
$this->assertTrue(
|
||||
$singleTrueSet->fulfilled(),
|
||||
'A mandatory set of type or with a single TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$singleFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$singleFalseSet->add(new FalseRequirement());
|
||||
$this->assertFalse(
|
||||
$singleFalseSet->fulfilled(),
|
||||
'A mandatory set of type or with a single FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$mixedSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mixedSet->add(new TrueRequirement());
|
||||
$mixedSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$mixedSet->fulfilled(),
|
||||
'A mandatory set of type or with one True- and one FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlatOptionalRequirementsOfTypeOr()
|
||||
{
|
||||
$emptySet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$this->assertTrue($emptySet->fulfilled(), 'A empty optional set of type or is not fulfilled');
|
||||
|
||||
$singleTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$singleTrueSet->add(new TrueRequirement());
|
||||
$this->assertTrue(
|
||||
$singleTrueSet->fulfilled(),
|
||||
'A optional set of type or with a single TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$singleFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$singleFalseSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$singleFalseSet->fulfilled(),
|
||||
'A optional set of type or with a single FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$mixedSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$mixedSet->add(new TrueRequirement());
|
||||
$mixedSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$mixedSet->fulfilled(),
|
||||
'A optional set of type or with one True- and one FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFlatMixedRequirementsOfTypeOr()
|
||||
{
|
||||
$mandatoryOptionalTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$mandatoryOptionalTrueSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$mandatoryOptionalTrueSet->fulfilled(),
|
||||
'A mandatory set of type or with one optional True- and one mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$mandatoryOptionalFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryOptionalFalseSet->add(new TrueRequirement());
|
||||
$mandatoryOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$this->assertTrue(
|
||||
$mandatoryOptionalFalseSet->fulfilled(),
|
||||
'A mandatory set of type or with one mandatory True- and one optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$optionalOptionalTrueSet->add(new FalseRequirement());
|
||||
$this->assertTrue(
|
||||
$optionalOptionalTrueSet->fulfilled(),
|
||||
'A optional set of type or with one optional True- and one mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalFalseSet->add(new TrueRequirement());
|
||||
$optionalOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$this->assertTrue(
|
||||
$optionalOptionalFalseSet->fulfilled(),
|
||||
'A optional set of type or with one mandatory True- and one optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedMandatoryRequirementsOfTypeAnd()
|
||||
{
|
||||
$trueSet = new RequirementSet();
|
||||
$trueSet->add(new TrueRequirement());
|
||||
$falseSet = new RequirementSet();
|
||||
$falseSet->add(new FalseRequirement());
|
||||
|
||||
$nestedTrueSet = new RequirementSet();
|
||||
$nestedTrueSet->merge($trueSet);
|
||||
$this->assertTrue(
|
||||
$nestedTrueSet->fulfilled(),
|
||||
'A nested mandatory set of type and with one mandatory TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedFalseSet = new RequirementSet();
|
||||
$nestedFalseSet->merge($falseSet);
|
||||
$this->assertFalse(
|
||||
$nestedFalseSet->fulfilled(),
|
||||
'A nested mandatory set of type and with one mandatory FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$nestedMixedSet = new RequirementSet();
|
||||
$nestedMixedSet->merge($trueSet);
|
||||
$nestedMixedSet->merge($falseSet);
|
||||
$this->assertFalse(
|
||||
$nestedMixedSet->fulfilled(),
|
||||
'Two nested mandatory sets of type and with one mandatory True- and'
|
||||
. ' one mandatory FalseRequirement respectively are fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedOptionalRequirementsOfTypeAnd()
|
||||
{
|
||||
$trueSet = new RequirementSet(true);
|
||||
$trueSet->add(new TrueRequirement());
|
||||
$falseSet = new RequirementSet(true);
|
||||
$falseSet->add(new FalseRequirement());
|
||||
|
||||
$nestedTrueSet = new RequirementSet(true);
|
||||
$nestedTrueSet->merge($trueSet);
|
||||
$this->assertTrue(
|
||||
$nestedTrueSet->fulfilled(),
|
||||
'A nested optional set of type and with one mandatory TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedFalseSet = new RequirementSet(true);
|
||||
$nestedFalseSet->merge($falseSet);
|
||||
$this->assertTrue(
|
||||
$nestedFalseSet->fulfilled(),
|
||||
'A nested optional set of type and with one mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedMixedSet = new RequirementSet(true);
|
||||
$nestedMixedSet->merge($trueSet);
|
||||
$nestedMixedSet->merge($falseSet);
|
||||
$this->assertTrue(
|
||||
$nestedMixedSet->fulfilled(),
|
||||
'Two nested optional sets of type and with one mandatory True- and'
|
||||
. ' one mandatory FalseRequirement respectively are not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedMixedRequirementsOfTypeAnd()
|
||||
{
|
||||
$mandatoryMandatoryTrueSet = new RequirementSet();
|
||||
$mandatoryMandatoryTrueSet->add(new TrueRequirement());
|
||||
$mandatoryOptionalTrueSet = new RequirementSet();
|
||||
$mandatoryOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$mandatoryMandatoryFalseSet = new RequirementSet();
|
||||
$mandatoryMandatoryFalseSet->add(new FalseRequirement());
|
||||
$mandatoryOptionalFalseSet = new RequirementSet();
|
||||
$mandatoryOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$optionalMandatoryTrueSet = new RequirementSet(true);
|
||||
$optionalMandatoryTrueSet->add(new TrueRequirement());
|
||||
$optionalOptionalTrueSet = new RequirementSet(true);
|
||||
$optionalOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$optionalMandatoryFalseSet = new RequirementSet(true);
|
||||
$optionalMandatoryFalseSet->add(new FalseRequirement());
|
||||
$optionalOptionalFalseSet = new RequirementSet(true);
|
||||
$optionalOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
|
||||
$mandatoryMandatoryOptionalTrueSet = new RequirementSet();
|
||||
$mandatoryMandatoryOptionalTrueSet->merge($mandatoryOptionalTrueSet);
|
||||
$mandatoryMandatoryOptionalTrueSet->merge($mandatoryMandatoryFalseSet);
|
||||
$this->assertFalse(
|
||||
$mandatoryMandatoryOptionalTrueSet->fulfilled(),
|
||||
'A mandatory set of type and with two nested mandatory sets of type and where one has a optional'
|
||||
. ' TrueRequirement and the other one has a mandatory FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$mandatoryMandatoryOptionalFalseSet = new RequirementSet();
|
||||
$mandatoryMandatoryOptionalFalseSet->merge($mandatoryOptionalFalseSet);
|
||||
$mandatoryMandatoryOptionalFalseSet->merge($mandatoryMandatoryTrueSet);
|
||||
$this->assertTrue(
|
||||
$mandatoryMandatoryOptionalFalseSet->fulfilled(),
|
||||
'A mandatory set of type and with two nested mandatory sets of type and where one has a mandatory'
|
||||
. ' TrueRequirement and the other one has a optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalOptionalTrueSet = new RequirementSet(true);
|
||||
$optionalOptionalOptionalTrueSet->merge($optionalOptionalTrueSet);
|
||||
$optionalOptionalOptionalTrueSet->merge($optionalMandatoryFalseSet);
|
||||
$this->assertTrue(
|
||||
$optionalOptionalOptionalTrueSet->fulfilled(),
|
||||
'A optional set of type and with two nested optional sets of type and where one has a optional'
|
||||
. ' TrueRequirement and the other one has a mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$optionalOptionalOptionalFalseSet = new RequirementSet(true);
|
||||
$optionalOptionalOptionalFalseSet->merge($optionalOptionalFalseSet);
|
||||
$optionalOptionalOptionalFalseSet->merge($optionalMandatoryTrueSet);
|
||||
$this->assertTrue(
|
||||
$optionalOptionalOptionalFalseSet->fulfilled(),
|
||||
'A optional set of type and with two nested optional sets of type and where one has a mandatory'
|
||||
. ' TrueRequirement and the other one has a optional FalseRequirement is not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedMandatoryRequirementsOfTypeOr()
|
||||
{
|
||||
$trueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$trueSet->add(new TrueRequirement());
|
||||
$falseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$falseSet->add(new FalseRequirement());
|
||||
|
||||
$nestedTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$nestedTrueSet->merge($trueSet);
|
||||
$this->assertTrue(
|
||||
$nestedTrueSet->fulfilled(),
|
||||
'A nested mandatory set of type or with one mandatory TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$nestedFalseSet->merge($falseSet);
|
||||
$this->assertFalse(
|
||||
$nestedFalseSet->fulfilled(),
|
||||
'A nested mandatory set of type or with one mandatory FalseRequirement is fulfilled'
|
||||
);
|
||||
|
||||
$nestedMixedSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$nestedMixedSet->merge($trueSet);
|
||||
$nestedMixedSet->merge($falseSet);
|
||||
$this->assertTrue(
|
||||
$nestedMixedSet->fulfilled(),
|
||||
'Two nested mandatory sets of type or with one mandatory True- and'
|
||||
. ' one mandatory FalseRequirement respectively are not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedOptionalRequirementsOfTypeOr()
|
||||
{
|
||||
$trueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$trueSet->add(new TrueRequirement());
|
||||
$falseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$falseSet->add(new FalseRequirement());
|
||||
|
||||
$nestedTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$nestedTrueSet->merge($trueSet);
|
||||
$this->assertTrue(
|
||||
$nestedTrueSet->fulfilled(),
|
||||
'A nested optional set of type or with one mandatory TrueRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$nestedFalseSet->merge($falseSet);
|
||||
$this->assertTrue(
|
||||
$nestedFalseSet->fulfilled(),
|
||||
'A nested optional set of type or with one mandatory FalseRequirement is not fulfilled'
|
||||
);
|
||||
|
||||
$nestedMixedSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$nestedMixedSet->merge($trueSet);
|
||||
$nestedMixedSet->merge($falseSet);
|
||||
$this->assertTrue(
|
||||
$nestedMixedSet->fulfilled(),
|
||||
'Two nested optional sets of type or with one mandatory True- and'
|
||||
. ' one mandatory FalseRequirement respectively are not fulfilled'
|
||||
);
|
||||
}
|
||||
|
||||
public function testNestedMixedRequirementsOfTypeOr()
|
||||
{
|
||||
$mandatoryMandatoryTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryMandatoryTrueSet->add(new TrueRequirement());
|
||||
$mandatoryOptionalTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$mandatoryMandatoryFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryMandatoryFalseSet->add(new FalseRequirement());
|
||||
$mandatoryOptionalFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
$optionalMandatoryTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalMandatoryTrueSet->add(new TrueRequirement());
|
||||
$optionalOptionalTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalTrueSet->add(new TrueRequirement(array('optional' => true)));
|
||||
$optionalMandatoryFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalMandatoryFalseSet->add(new FalseRequirement());
|
||||
$optionalOptionalFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalFalseSet->add(new FalseRequirement(array('optional' => true)));
|
||||
|
||||
$mandatoryMandatoryOptionalTrueSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryMandatoryOptionalTrueSet->merge($mandatoryOptionalTrueSet);
|
||||
$mandatoryMandatoryOptionalTrueSet->merge($mandatoryMandatoryFalseSet);
|
||||
$this->assertTrue($mandatoryMandatoryOptionalTrueSet->fulfilled());
|
||||
|
||||
$mandatoryMandatoryOptionalFalseSet = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$mandatoryMandatoryOptionalFalseSet->merge($mandatoryOptionalFalseSet);
|
||||
$mandatoryMandatoryOptionalFalseSet->merge($mandatoryMandatoryTrueSet);
|
||||
$this->assertTrue($mandatoryMandatoryOptionalFalseSet->fulfilled());
|
||||
|
||||
$optionalOptionalOptionalTrueSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalOptionalTrueSet->merge($optionalOptionalTrueSet);
|
||||
$optionalOptionalOptionalTrueSet->merge($optionalMandatoryFalseSet);
|
||||
$this->assertTrue($optionalOptionalOptionalTrueSet->fulfilled());
|
||||
|
||||
$optionalOptionalOptionalFalseSet = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$optionalOptionalOptionalFalseSet->merge($optionalOptionalFalseSet);
|
||||
$optionalOptionalOptionalFalseSet->merge($optionalMandatoryTrueSet);
|
||||
$this->assertTrue($optionalOptionalOptionalFalseSet->fulfilled());
|
||||
}
|
||||
|
||||
public function testNestedMandatoryRequirementsOfDifferentTypes()
|
||||
{
|
||||
$true = new TrueRequirement();
|
||||
$false = new FalseRequirement();
|
||||
|
||||
$level1And = new RequirementSet();
|
||||
$level2FirstOr = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level2SecondOr = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level1And->merge($level2FirstOr)->merge($level2SecondOr);
|
||||
$level3FirstAnd = new RequirementSet();
|
||||
$level3SecondAnd = new RequirementSet();
|
||||
$level2FirstOr->merge($level3FirstAnd)->merge($level3SecondAnd);
|
||||
$level2SecondOr->merge($level3FirstAnd)->merge($level3SecondAnd);
|
||||
$level3FirstAnd->add($true)->add($true);
|
||||
$level3SecondAnd->add($false)->add($true);
|
||||
$this->assertTrue($level1And->fulfilled());
|
||||
|
||||
$level1Or = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level2FirstAnd = new RequirementSet();
|
||||
$level2SecondAnd = new RequirementSet();
|
||||
$level1Or->merge($level2FirstAnd)->merge($level2SecondAnd);
|
||||
$level3FirstOr = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level3SecondOr = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level2FirstAnd->merge($level3FirstOr)->merge($level3SecondOr);
|
||||
$level2SecondAnd->merge($level3FirstOr)->merge($level3SecondOr);
|
||||
$level3FirstOr->add($false);
|
||||
$level3SecondOr->add($true);
|
||||
$this->assertFalse($level1Or->fulfilled());
|
||||
}
|
||||
|
||||
public function testNestedOptionalRequirementsOfDifferentTypes()
|
||||
{
|
||||
$true = new TrueRequirement();
|
||||
$false = new FalseRequirement();
|
||||
|
||||
$level1And = new RequirementSet();
|
||||
$level2FirstAnd = new RequirementSet(true);
|
||||
$level2SecondAnd = new RequirementSet(true);
|
||||
$level1And->merge($level2FirstAnd)->merge($level2SecondAnd);
|
||||
$level3FirstOr = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$level3SecondOr = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$level2FirstAnd->merge($level3FirstOr)->merge($level3SecondOr);
|
||||
$level2SecondAnd->merge($level3FirstOr)->merge($level3SecondOr);
|
||||
$level3FirstOr->add($false);
|
||||
$level3SecondOr->add($false);
|
||||
$this->assertFalse($level1And->fulfilled());
|
||||
$this->assertTrue($level2FirstAnd->fulfilled());
|
||||
$this->assertTrue($level2SecondAnd->fulfilled());
|
||||
|
||||
$level1Or = new RequirementSet(false, RequirementSet::MODE_OR);
|
||||
$level2FirstOr = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$level2SecondOr = new RequirementSet(true, RequirementSet::MODE_OR);
|
||||
$level1Or->merge($level2FirstOr)->merge($level2SecondOr);
|
||||
$level3FirstAnd = new RequirementSet(true);
|
||||
$level3SecondAnd = new RequirementSet(true);
|
||||
$level2FirstOr->merge($level3FirstAnd)->merge($level3SecondAnd);
|
||||
$level2SecondOr->merge($level3FirstAnd)->merge($level3SecondAnd);
|
||||
$level3FirstAnd->add($true)->add($true);
|
||||
$level3SecondAnd->add($false)->add($true);
|
||||
$this->assertTrue($level1Or->fulfilled());
|
||||
}
|
||||
|
||||
public function testNestedMixedRequirementsOfDifferentTypes()
|
||||
{
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
}
|
@ -2,4 +2,4 @@ Module: test
|
||||
Version: 2.0.0~alpha4
|
||||
Description: Translation module
|
||||
This module allows developers to run (unit) tests against Icinga Web 2 and
|
||||
any of it's modules. Usually you do not need to enable this.
|
||||
any of its modules. Usually you do not need to enable this.
|
||||
|
@ -15,7 +15,7 @@ use Icinga\Module\Translation\Util\GettextTranslationHelper;
|
||||
* Domains are the global one 'icinga' and all available and enabled modules
|
||||
* identified by their name.
|
||||
*
|
||||
* Once a PO-file is compiled it's content is used by Icinga Web 2 to display
|
||||
* Once a PO-file is compiled its content is used by Icinga Web 2 to display
|
||||
* messages in the configured language.
|
||||
*/
|
||||
class CompileCommand extends TranslationCommand
|
||||
|
@ -109,7 +109,7 @@ When you are done, just save your new settings.
|
||||
|
||||
To work with Icinga Web 2 .po files, you can open for e.g. the german icinga.po file which is located under
|
||||
`application/locale/de_DE/LC_MESSAGES/icinga.po`, as shown below, you will get then a full list of all available
|
||||
translation strings for the core application. Each module names it's translation files `%module_name%.po`. For a
|
||||
translation strings for the core application. Each module names its translation files `%module_name%.po`. For a
|
||||
module called __yourmodule__ the .po translation file will be named `yourmodule.po`.
|
||||
|
||||
|
||||
@ -196,4 +196,4 @@ The last step is to compile the __yourmodule.po__ to the __yourmodule.mo__:
|
||||
icingacli translation compile module development ll_CC
|
||||
|
||||
At this moment, everywhere in the module where the `Dummy` should be translated, it would returns the translated
|
||||
string `Attrappe`.
|
||||
string `Attrappe`.
|
||||
|
@ -2,6 +2,6 @@ Module: translation
|
||||
Version: 2.0.0~alpha4
|
||||
Description: Translation module
|
||||
This module allows developers and translators to translate Icinga Web 2 and
|
||||
it's modules for multiple languages. You do not need this module to run an
|
||||
its modules for multiple languages. You do not need this module to run an
|
||||
internationalized web frontend. This is only for people who want to contribute
|
||||
translations or translate just their own moduls.
|
||||
|
@ -1,3 +0,0 @@
|
||||
PLEASE DO NOT BUILD PACKAGES BASES ON THIS
|
||||
|
||||
Packages may still be renamed
|
@ -1,5 +0,0 @@
|
||||
icingaweb (2.0.0) stable; urgency=low
|
||||
|
||||
* First stable release (Closes: #0001)
|
||||
|
||||
-- Thomas Gelf <thomas@gelf.net> Fri, 28 Mar 2014 23:37:31 +0100
|
@ -1 +0,0 @@
|
||||
9
|
@ -1,68 +0,0 @@
|
||||
Source: icingaweb
|
||||
Section: admin
|
||||
Maintainer: Icinga Development Team <info@icinga.org>
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>=9)
|
||||
Standards-Version: 3.9.4
|
||||
Homepage: https://www.icinga.org
|
||||
|
||||
Package: php-icinga
|
||||
Architecture: any
|
||||
Depends: php5 (>= 5.3.2)
|
||||
Recommends: php5-ldap, php5-mysql, php5-json
|
||||
Suggests: php5-pgsql
|
||||
Description: Icinga PHP libraries
|
||||
PHP libraries
|
||||
|
||||
Package: icingaweb-common
|
||||
Architecture: any
|
||||
Depends: php-icinga
|
||||
Description: Icinga PHP common libraries
|
||||
PHP common libraries, application and modules
|
||||
|
||||
Package: icingaweb-module-doc
|
||||
Architecture: any
|
||||
Depends: icingaweb-common
|
||||
Description: Icingaweb documentation module
|
||||
This module renders documentation for Icingaweb and it's modules
|
||||
|
||||
Package: icingaweb-module-monitoring
|
||||
Architecture: any
|
||||
Depends: icingaweb-common
|
||||
Description: Icingaweb monitoring module
|
||||
Use this module to visualize your monitoring environment in Icingaweb
|
||||
|
||||
Package: icingaweb-module-setup
|
||||
Architecture: any
|
||||
Depends: icingaweb-common
|
||||
Description: Icingaweb setup module
|
||||
This setup wizard does your initial Icingaweb configuration
|
||||
|
||||
Package: icingaweb-module-test
|
||||
Architecture: any
|
||||
Depends: icingacli
|
||||
Description: Icingaweb test module
|
||||
Use this module to run unit tests against Icingaweb or any of it's modules
|
||||
|
||||
Package: icingaweb-module-translation
|
||||
Architecture: any
|
||||
Depends: icingaweb-common
|
||||
Description: Icingaweb translation module
|
||||
This module helps translators to get Icingaweb translations done
|
||||
|
||||
Package: icingacli
|
||||
Architecture: any
|
||||
Depends: icingaweb-common, php5-cli (>= 5.3.2)
|
||||
Description: Icinga CLI tool
|
||||
The Icinga CLI allows one to access it's Icinga monitoring
|
||||
system from a terminal.
|
||||
.
|
||||
The CLI is based on the Icinga PHP libraries
|
||||
|
||||
Package: icingaweb
|
||||
Architecture: any
|
||||
Depends: icingaweb-common, libapache2-mod-php5, zendframework | icingaweb-vendor-zend, icingaweb-vendor-parsedown, icingaweb-vendor-lessphp, icingaweb-vendor-jshrink, icingaweb-vendor-htmlpurifier, icingaweb-module-setup
|
||||
Recommends: icingaweb-module-monitoring, icingaweb-module-doc
|
||||
Suggests: php5-ldap
|
||||
Description: Icingaweb Frontend
|
||||
Icingaweb is a modular web frontend
|
@ -1 +0,0 @@
|
||||
library/Icinga usr/share/php
|
@ -1,3 +0,0 @@
|
||||
packages/files/bin/icingacli usr/bin
|
||||
etc/bash_completion.d etc/bash_completion.d
|
||||
application/clicommands usr/share/icingaweb/application
|
@ -1 +0,0 @@
|
||||
etc/icingaweb
|
@ -1,2 +0,0 @@
|
||||
application/locales usr/share/icingaweb/application
|
||||
modules usr/share/icingaweb
|
@ -1 +0,0 @@
|
||||
modules/doc usr/share/icingaweb/modules
|
@ -1 +0,0 @@
|
||||
modules/doc usr/share/icingaweb/modules
|
@ -1 +0,0 @@
|
||||
modules/setup usr/share/icingaweb/modules
|
@ -1 +0,0 @@
|
||||
modules/test usr/share/icingaweb/modules
|
@ -1 +0,0 @@
|
||||
modules/translation usr/share/icingaweb/modules
|
@ -1 +0,0 @@
|
||||
library/vendor/HTMLPurifier usr/share/icingaweb/library/vendor
|
@ -1 +0,0 @@
|
||||
library/vendor/JShrink usr/share/icingaweb/library/vendor
|
@ -1 +0,0 @@
|
||||
library/vendor/Zend usr/share/icingaweb/library/vendor
|
@ -1 +0,0 @@
|
||||
library/vendor/Parsedown usr/share/icingaweb/library/vendor
|
@ -1 +0,0 @@
|
||||
library/vendor/Zend usr/share/icingaweb/library/vendor
|
@ -1,10 +0,0 @@
|
||||
public/css usr/share/icingaweb/public
|
||||
public/img usr/share/icingaweb/public
|
||||
public/js usr/share/icingaweb/public
|
||||
public/error_norewrite.html usr/share/icingaweb/public
|
||||
application/controllers usr/share/icingaweb/application
|
||||
application/fonts usr/share/icingaweb/application
|
||||
application/layouts usr/share/icingaweb/application
|
||||
application/views usr/share/icingaweb/application
|
||||
packages/files/public/index.php usr/share/icingaweb/public
|
||||
packages/files/apache/icingaweb.conf etc/apache2/conf.d
|
@ -1,35 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_clean
|
||||
|
||||
build:
|
||||
dh_testdir
|
||||
|
||||
binary:
|
||||
dh_testroot
|
||||
dh_prep
|
||||
dh_installdirs
|
||||
dh_install
|
||||
dh_installchangelogs
|
||||
dh_installexamples
|
||||
dh_installman
|
||||
dh_installcron
|
||||
dh_installdebconf
|
||||
dh_installinfo
|
||||
dh_installinit
|
||||
dpkg-statoverride --force --add root www-data 2770 /etc/icingaweb
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
dh_strip
|
||||
dh_shlibdeps
|
||||
dh_installdeb
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
@ -1 +0,0 @@
|
||||
3.0 (git)
|
@ -1 +0,0 @@
|
||||
misc:Depends=
|
@ -110,7 +110,8 @@
|
||||
}
|
||||
|
||||
#setup div.buttons {
|
||||
margin: 1.5em 0;
|
||||
margin-top: 1.5em; // Yes, -top and -bottom, keep it like that...
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
.double {
|
||||
position: absolute;
|
||||
@ -166,25 +167,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
#setup table.requirements {
|
||||
form#setup_requirements {
|
||||
margin-top: 2em;
|
||||
padding-top: 0.5em;
|
||||
border-top: 2px solid @colorPetrol;
|
||||
|
||||
div.buttons div.requirements-refresh {
|
||||
width: 25%;
|
||||
float: right;
|
||||
text-align: center;
|
||||
|
||||
a.button-like {
|
||||
padding: 0.1em 0.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#setup > table.requirements {
|
||||
font-size: 0.9em;
|
||||
margin: -1em -1em 2em;
|
||||
}
|
||||
|
||||
#setup table.requirements {
|
||||
margin: -1em;
|
||||
border-spacing: 1em;
|
||||
border-collapse: separate;
|
||||
border-bottom: 2px solid @colorPetrol;
|
||||
|
||||
td {
|
||||
padding: 0;
|
||||
|
||||
h2 {
|
||||
margin: 0 1em 0 0;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: 102%; // Just a hack for webkit, remove this in case you can't see any difference or make it work without it
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
&.title {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
&.desc {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
&.state {
|
||||
width: 25%;
|
||||
color: white;
|
||||
padding: 0.4em;
|
||||
|
||||
@ -201,24 +235,6 @@
|
||||
background-color: @colorCritical;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-update {
|
||||
padding-top: 0.3em;
|
||||
text-align: center;
|
||||
|
||||
div.buttons {
|
||||
margin: 0;
|
||||
|
||||
a.button-like {
|
||||
padding: 0.2em 0.5em;
|
||||
background-color: @colorPetro;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,10 @@
|
||||
$(document).on('change', 'form select.autosubmit', { self: this }, this.autoSubmitForm);
|
||||
$(document).on('change', 'form input.autosubmit', { self: this }, this.autoSubmitForm);
|
||||
|
||||
// Automatically check a radio button once a specific input is focused
|
||||
$(document).on('focus', 'form select[data-related-radiobtn]', { self: this }, this.autoCheckRadioButton);
|
||||
$(document).on('focus', 'form input[data-related-radiobtn]', { self: this }, this.autoCheckRadioButton);
|
||||
|
||||
$(document).on('keyup', '#menu input.search', {self: this}, this.autoSubmitSearch);
|
||||
|
||||
$(document).on('click', '.tree .handle', { self: this }, this.treeNodeToggle);
|
||||
@ -163,6 +167,15 @@
|
||||
icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
autoCheckRadioButton: function (event) {
|
||||
var $input = $(event.currentTarget);
|
||||
var $radio = $('#' + $input.attr('data-related-radiobtn'));
|
||||
if ($radio.length) {
|
||||
$radio.prop('checked', true);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
autoSubmitSearch: function(event) {
|
||||
var self = event.data.self;
|
||||
if ($('#menu input.search').val() === self.searchValue) {
|
||||
@ -560,6 +573,8 @@
|
||||
$(document).off('submit', 'form', this.submitForm);
|
||||
$(document).off('change', 'form select.autosubmit', this.submitForm);
|
||||
$(document).off('change', 'form input.autosubmit', this.submitForm);
|
||||
$(document).off('focus', 'form select[data-related-radiobtn]', this.autoCheckRadioButton);
|
||||
$(document).off('focus', 'form input[data-related-radiobtn]', this.autoCheckRadioButton);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
@ -726,7 +726,7 @@ EOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a INI-configuration string to a temporary file and return it's path
|
||||
* Write a INI-configuration string to a temporary file and return its path
|
||||
*
|
||||
* @param string $config The config string to write
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user