Merge branch 'master' into feature/improve-multi-select-view-8565

Conflicts:
	modules/monitoring/application/controllers/HostsController.php
	modules/monitoring/application/controllers/ServicesController.php
	modules/monitoring/application/views/scripts/hosts/show.phtml
	modules/monitoring/application/views/scripts/list/hosts.phtml
	modules/monitoring/application/views/scripts/partials/host/objects-header.phtml
	modules/monitoring/application/views/scripts/partials/service/objects-header.phtml
	modules/monitoring/application/views/scripts/services/show.phtml
	modules/monitoring/public/css/module.less
	public/js/icinga/behavior/tooltip.js
This commit is contained in:
Matthias Jentsch 2015-05-11 12:18:50 +02:00
commit 25f397042b
338 changed files with 14225 additions and 5957 deletions

View File

@ -90,7 +90,7 @@ class icingaweb2_dev (
source => $name,
}
icingaweb2::config::general { [ 'config', 'resources' ]:
icingaweb2::config::general { [ 'config', 'resources', 'roles' ]:
source => $name,
replace => false,
}

View File

@ -0,0 +1,3 @@
[admins]
users = icingaadmin
permissions = *

View File

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

View File

@ -1 +1 @@
v2.0.0-beta2
v2.0.0-beta3

View File

@ -102,21 +102,21 @@ class AuthenticationController extends ActionController
$this->view->form->addError(
$this->translate(
'No authentication methods available. Did you create'
. ' authentication.ini when setting up Icinga Web 2?'
. ' authentication.ini when setting up Icinga Web 2?'
)
);
} else if ($backendsTried === $backendsWithError) {
$this->view->form->addError(
$this->translate(
'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.'
. ' Please check the system log or Icinga Web 2 log for more information.'
)
);
} elseif ($backendsWithError) {
$this->view->form->addError(
$this->translate(
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
. ' Check the system log or Icinga Web 2 log for more information.'
)
);
}
@ -144,7 +144,7 @@ class AuthenticationController extends ActionController
$this->view->form->addError($e->getMessage());
}
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated();
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && ! $auth->isAuthenticated();
$this->view->requiresSetup = Icinga::app()->requiresSetup();
}

View File

@ -11,19 +11,19 @@ use Icinga\Forms\Config\GeneralConfigForm;
use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Web\Widget;
/**
* Application and module configuration
*/
class ConfigController extends ActionController
class ConfigController extends Controller
{
/**
* The first allowed config action according to the user's permissions
*
* @type string
* @var string
*/
protected $firstAllowedAction;
@ -37,7 +37,7 @@ class ConfigController extends ActionController
$tabs = $this->getTabs();
$auth = $this->Auth();
$allowedActions = array();
if ($auth->hasPermission('system/config/application')) {
if ($auth->hasPermission('config/application/general')) {
$tabs->add('application', array(
'title' => $this->translate('Adjust the general configuration of Icinga Web 2'),
'label' => $this->translate('Application'),
@ -45,7 +45,7 @@ class ConfigController extends ActionController
));
$allowedActions[] = 'application';
}
if ($auth->hasPermission('system/config/authentication')) {
if ($auth->hasPermission('config/application/authentication')) {
$tabs->add('authentication', array(
'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'),
'label' => $this->translate('Authentication'),
@ -53,7 +53,7 @@ class ConfigController extends ActionController
));
$allowedActions[] = 'authentication';
}
if ($auth->hasPermission('system/config/resources')) {
if ($auth->hasPermission('config/application/resources')) {
$tabs->add('resource', array(
'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'),
'label' => $this->translate('Resources'),
@ -61,7 +61,7 @@ class ConfigController extends ActionController
));
$allowedActions[] = 'resource';
}
if ($auth->hasPermission('system/config/roles')) {
if ($auth->hasPermission('config/application/roles')) {
$tabs->add('roles', array(
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
@ -72,7 +72,6 @@ class ConfigController extends ActionController
$allowedActions[] = 'roles';
}
$this->firstAllowedAction = array_shift($allowedActions);
$this->getTabs()->setTitle($this->translate('Config Navigation'));
}
public function devtoolsAction()
@ -103,7 +102,7 @@ class ConfigController extends ActionController
*/
public function applicationAction()
{
$this->assertPermission('system/config/application');
$this->assertPermission('config/application/general');
$form = new GeneralConfigForm();
$form->setIniConfig(Config::app());
$form->handleRequest();
@ -131,26 +130,35 @@ class ConfigController extends ActionController
->order('enabled', 'desc')
->order('name')
->paginate();
$this->setupLimitControl();
$this->setupPaginationControl($this->view->modules);
// TODO: Not working
/*$this->setupSortControl(array(
'name' => $this->translate('Modulename'),
'path' => $this->translate('Installation Path'),
'enabled' => $this->translate('State')
));*/
}
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');
}
/**
@ -158,12 +166,11 @@ class ConfigController extends ActionController
*/
public function moduleenableAction()
{
$this->assertPermission('system/config/modules');
$this->assertPermission('config/modules');
$module = $this->getParam('name');
$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) {
@ -179,7 +186,7 @@ class ConfigController extends ActionController
*/
public function moduledisableAction()
{
$this->assertPermission('system/config/modules');
$this->assertPermission('config/modules');
$module = $this->getParam('name');
$manager = Icinga::app()->getModuleManager();
try {
@ -199,7 +206,7 @@ class ConfigController extends ActionController
*/
public function authenticationAction()
{
$this->assertPermission('system/config/authentication');
$this->assertPermission('config/application/authentication');
$form = new AuthenticationBackendReorderForm();
$form->setIniConfig(Config::app('authentication'));
$form->handleRequest();
@ -214,7 +221,7 @@ class ConfigController extends ActionController
*/
public function createauthenticationbackendAction()
{
$this->assertPermission('system/config/authentication');
$this->assertPermission('config/application/authentication');
$form = new AuthenticationBackendConfigForm();
$form->setTitle($this->translate('Create New Authentication Backend'));
$form->addDescription($this->translate(
@ -236,7 +243,7 @@ class ConfigController extends ActionController
*/
public function editauthenticationbackendAction()
{
$this->assertPermission('system/config/authentication');
$this->assertPermission('config/application/authentication');
$form = new AuthenticationBackendConfigForm();
$form->setTitle($this->translate('Edit Backend'));
$form->setIniConfig(Config::app('authentication'));
@ -254,7 +261,7 @@ class ConfigController extends ActionController
*/
public function removeauthenticationbackendAction()
{
$this->assertPermission('system/config/authentication');
$this->assertPermission('config/application/authentication');
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($form) {
$configForm = new AuthenticationBackendConfigForm();
@ -292,7 +299,7 @@ class ConfigController extends ActionController
*/
public function resourceAction()
{
$this->assertPermission('system/config/resources');
$this->assertPermission('config/application/resources');
$this->view->resources = Config::app('resources', true)->keys();
$this->view->tabs->activate('resource');
}
@ -302,7 +309,7 @@ class ConfigController extends ActionController
*/
public function createresourceAction()
{
$this->assertPermission('system/config/resources');
$this->assertPermission('config/application/resources');
$form = new ResourceConfigForm();
$form->setTitle($this->translate('Create A New Resource'));
$form->addDescription($this->translate('Resources are entities that provide data to Icinga Web 2.'));
@ -319,7 +326,7 @@ class ConfigController extends ActionController
*/
public function editresourceAction()
{
$this->assertPermission('system/config/resources');
$this->assertPermission('config/application/resources');
$form = new ResourceConfigForm();
$form->setTitle($this->translate('Edit Existing Resource'));
$form->setIniConfig(Config::app('resources'));
@ -335,7 +342,7 @@ class ConfigController extends ActionController
*/
public function removeresourceAction()
{
$this->assertPermission('system/config/resources');
$this->assertPermission('config/application/resources');
$form = new ConfirmRemovalForm(array(
'onSuccess' => function ($form) {
$configForm = new ResourceConfigForm();

View File

@ -3,6 +3,7 @@
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Exception\MissingParameterException;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller\ActionController;
@ -33,7 +34,11 @@ class ErrorController extends ActionController
$path = preg_split('~/~', $path);
$path = array_shift($path);
$this->getResponse()->setHttpResponseCode(404);
$this->view->message = $this->translate('Page not found.');
$title = preg_replace('/\r?\n.*$/s', '', $exception->getMessage());
$this->view->title = 'Server error: ' . $title;
if ($this->getInvokeArg('displayExceptions')) {
$this->view->stackTrace = $exception->getTraceAsString();
}
if ($modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
$this->view->message .= ' ' . sprintf(
$this->translate('Enabling the "%s" module might help!'),
@ -42,19 +47,26 @@ class ErrorController extends ActionController
}
break;
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_OTHER:
if ($exception instanceof SecurityException) {
$this->getResponse()->setHttpResponseCode(403);
$this->view->message = $exception->getMessage();
break;
}
// Move to default
default:
switch (true) {
case $exception instanceof SecurityException:
$this->getResponse()->setHttpResponseCode(403);
break;
case $exception instanceof MissingParameterException:
$this->getResponse()->setHttpResponseCode(400);
$this->getResponse()->setHeader(
'X-Status-Reason',
'Missing parameter ' . $exception->getParameter()
);
break;
default:
$this->getResponse()->setHttpResponseCode(500);
break;
}
$title = preg_replace('/\r?\n.*$/s', '', $exception->getMessage());
$this->getResponse()->setHttpResponseCode(500);
$this->view->title = 'Server error: ' . $title;
$this->view->message = $exception->getMessage();
if ($this->getInvokeArg('displayExceptions') == true) {
if ($this->getInvokeArg('displayExceptions')) {
$this->view->stackTrace = $exception->getTraceAsString();
}
break;

View File

@ -3,6 +3,9 @@
use Icinga\Module\Monitoring\Controller;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Protocol\File\FileReader;
@ -28,7 +31,7 @@ class ListController extends Controller
'list/'
. str_replace(' ', '', $action)
)
))->activate($action);
))->extend(new OutputFormat())->extend(new DashboardAction())->activate($action);
}
/**
@ -41,16 +44,16 @@ class ListController extends Controller
}
$this->addTitleTab('application log');
$pattern = '/^(?<datetime>[0-9]{4}(-[0-9]{2}){2}' // date
. 'T[0-9]{2}(:[0-9]{2}){2}([\\+\\-][0-9]{2}:[0-9]{2})?)' // time
. ' - (?<loglevel>[A-Za-z]+)' // loglevel
. ' - (?<message>.*)$/'; // message
$loggerWriter = Logger::getInstance()->getWriter();
$resource = new FileReader(new ConfigObject(array(
'filename' => $loggerWriter->getPath(),
'fields' => $pattern
'filename' => Config::app()->get('logging', 'file'),
'fields' => '/(?<!.)(?<datetime>[0-9]{4}(?:-[0-9]{2}){2}' // date
. 'T[0-9]{2}(?::[0-9]{2}){2}(?:[\+\-][0-9]{2}:[0-9]{2})?)' // time
. ' - (?<loglevel>[A-Za-z]+) - (?<message>.*)(?!.)/msS' // loglevel, message
)));
$this->view->logData = $resource->select()->order('DESC')->paginate();
$this->setupLimitControl();
$this->setupPaginationControl($this->view->logData);
}
}

View File

@ -20,24 +20,24 @@ class RolesController extends ActionController
*/
public function init()
{
$this->assertPermission('system/config/roles');
$this->assertPermission('config/application/roles');
$tabs = $this->getTabs();
$auth = $this->Auth();
if ($auth->hasPermission('system/config/application')) {
if ($auth->hasPermission('config/application/general')) {
$tabs->add('application', array(
'title' => $this->translate('Adjust the general configuration of Icinga Web 2'),
'label' => $this->translate('Application'),
'url' => 'config'
));
}
if ($auth->hasPermission('system/config/authentication')) {
if ($auth->hasPermission('config/application/authentication')) {
$tabs->add('authentication', array(
'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'),
'label' => $this->translate('Authentication'),
'url' => 'config/authentication'
));
}
if ($auth->hasPermission('system/config/resources')) {
if ($auth->hasPermission('config/application/resources')) {
$tabs->add('resource', array(
'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'),
'label' => $this->translate('Resources'),
@ -51,7 +51,6 @@ class RolesController extends ActionController
'label' => $this->translate('Roles'),
'url' => 'roles'
));
$this->getTabs()->setTitle($this->translate('Role Configuration'));
}
/**

View File

@ -21,6 +21,8 @@ class AutoRefreshForm extends Form
public function init()
{
$this->setName('form_auto_refresh');
// Post against the current location
$this->setAction('');
}
/**

View File

@ -34,7 +34,7 @@ class DbBackendForm extends Form
*
* @param array $resources The resources to choose from
*
* @return self
* @return $this
*/
public function setResources(array $resources)
{

View File

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

View File

@ -35,7 +35,7 @@ class LdapBackendForm extends Form
*
* @param array $resources The resources to choose from
*
* @return self
* @return $this
*/
public function setResources(array $resources)
{
@ -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()));

View File

@ -38,7 +38,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
*
* @param Config $resources The resource configuration
*
* @return self
* @return $this
*/
public function setResourceConfig(Config $resourceConfig)
{
@ -82,7 +82,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
*
* @param array $values The values to extend the configuration with
*
* @return self
* @return $this
*
* @throws InvalidArgumentException In case the backend does already exist
*/
@ -159,7 +159,7 @@ class AuthenticationBackendConfigForm extends ConfigForm
* @param string $name The name of the backend to be moved
* @param int $position The new (absolute) position of the backend
*
* @return self
* @return $this
*
* @throws InvalidArgumentException In case the backend does not exist
*/

View File

@ -51,7 +51,7 @@ class AuthenticationBackendReorderForm extends ConfigForm
try {
if ($configForm->move($backendName, $position)->save()) {
Notification::success($this->translate('Authentication order updated!'));
Notification::success($this->translate('Authentication order updated'));
} else {
return false;
}

View File

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

View File

@ -49,6 +49,7 @@ class DbResourceForm extends Form
'db',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Database Type'),
'description' => $this->translate('The type of SQL database'),
'multiOptions' => $dbChoices
@ -68,10 +69,11 @@ class DbResourceForm extends Form
'number',
'port',
array(
'required' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => 3306
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => ! array_key_exists('db', $formData) || $formData['db'] === 'mysql' ? 3306 : 5432
)
);
$this->addElement(

View File

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

View File

@ -7,6 +7,7 @@ use Exception;
use Icinga\Web\Form;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Protocol\Ldap\Connection;
/**
* Form class for adding/modifying ldap resources
@ -26,6 +27,10 @@ class LdapResourceForm extends Form
*/
public function createElements(array $formData)
{
$defaultPort = ! array_key_exists('encryption', $formData) || $formData['encryption'] !== Connection::LDAPS
? 389
: 636;
$this->addElement(
'text',
'name',
@ -51,12 +56,48 @@ class LdapResourceForm extends Form
'number',
'port',
array(
'required' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port of the LDAP server to use for authentication'),
'value' => 389
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port of the LDAP server to use for authentication'),
'value' => $defaultPort
)
);
$this->addElement(
'select',
'encryption',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Encryption'),
'description' => $this->translate(
'Whether to encrypt communication. Choose STARTTLS or LDAPS for encrypted communication or'
. ' none for unencrypted communication'
),
'multiOptions' => array(
'none' => $this->translate('None', 'resource.ldap.encryption'),
Connection::STARTTLS => 'STARTTLS',
Connection::LDAPS => 'LDAPS'
)
)
);
if (isset($formData['encryption']) && $formData['encryption'] !== 'none') {
// TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors
$this->addElement(
'checkbox',
'reqcert',
array(
'required' => true,
'label' => $this->translate('Require Certificate'),
'description' => $this->translate(
'When checked, the LDAP server must provide a valid and known (trusted) certificate.'
),
'value' => 1
)
);
}
$this->addElement(
'text',
'root_dn',
@ -119,12 +160,15 @@ class LdapResourceForm extends Form
$form->getElement('bind_pw')->getValue()
)
) {
throw new Exception();
throw new Exception(); // TODO: Get the exact error message
}
} catch (Exception $e) {
$form->addError(
$form->translate('Connectivity validation failed, connection to the given resource not possible.')
);
$msg = $form->translate('Connectivity validation failed, connection to the given resource not possible.');
if (($error = $e->getMessage())) {
$msg .= ' (' . $error . ')';
}
$form->addError($msg);
return false;
}

View File

@ -53,7 +53,7 @@ class ResourceConfigForm extends ConfigForm
*
* @param array $values The values to extend the configuration with
*
* @return self
* @return $this
*
* @thrwos InvalidArgumentException In case the resource does already exist
*/

View File

@ -25,7 +25,7 @@ class ConfigForm extends Form
*
* @param Config $config The configuration to use
*
* @return self
* @return $this
*/
public function setIniConfig(Config $config)
{

View File

@ -148,9 +148,9 @@ class DashletForm extends Form
$this->populate(array(
'pane' => $dashlet->getPane()->getName(),
'org_pane' => $dashlet->getPane()->getName(),
'dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getTitle(),
'url' => $dashlet->getUrl()
'dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getTitle(),
'url' => $dashlet->getUrl()->getRelativeUrl()
));
}
}

View File

@ -48,7 +48,7 @@ class PreferenceForm extends Form
*
* @param Preferences $preferences The preferences to work with
*
* @return self
* @return $this
*/
public function setPreferences(Preferences $preferences)
{
@ -61,17 +61,18 @@ class PreferenceForm extends Form
*
* @param PreferencesStore $store The preference store to use
*
* @return self
* @return $this
*/
public function setStore(PreferencesStore $store)
{
$this->store = $store;
return $this;
}
/**
* Persist preferences
*
* @return self
* @return $this
*/
public function save()
{

View File

@ -18,39 +18,39 @@ class RoleForm extends ConfigForm
/**
* Provided permissions by currently loaded modules
*
* @type array
* @var array
*/
protected $providedPermissions = array(
'*' => '*',
'system/config/*' => 'system/config/*',
'system/config/application' => 'system/config/application',
'system/config/authentication' => 'system/config/authentication',
'system/config/modules' => 'system/config/modules',
'system/config/resources' => 'system/config/resources',
'system/config/roles' => 'system/config/roles'
'*' => '*',
'config/*' => 'config/*',
'config/application/*' => 'config/application/*',
'config/application/general' => 'config/application/general',
'config/application/authentication' => 'config/application/authentication',
'config/application/resources' => 'config/application/resources',
'config/application/roles' => 'config/application/roles',
'config/modules' => 'config/modules'
);
/**
* Provided restrictions by currently loaded modules
*
* @type array
* @var array
*/
protected $providedRestrictions = array();
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::init() For the method documentation.
* {@inheritdoc}
*/
public function init()
{
$helper = new Zend_Form_Element('bogus');
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
foreach ($module->getProvidedPermissions() as $permission) {
/** @type object $permission */
/** @var object $permission */
$this->providedPermissions[$permission->name] = $permission->name . ': ' . $permission->description;
}
foreach ($module->getProvidedRestrictions() as $restriction) {
/** @type object $restriction */
/** @var object $restriction */
$name = $helper->filterName($restriction->name); // Zend only permits alphanumerics, the underscore,
// the circumflex and any ASCII character in range
// \x7f to \xff (127 to 255)
@ -68,8 +68,7 @@ class RoleForm extends ConfigForm
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
@ -253,8 +252,7 @@ class RoleForm extends ConfigForm
}
/**
* (non-PHPDoc)
* @see \Zend_Form::getValues() For the method documentation.
* {@inheritdoc}
*/
public function getValues($suppressArrayNotation = false)
{

View File

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

View File

@ -21,7 +21,7 @@ if (! $this->auth()->isAuthenticated()) {
</li>
</ul>
</div>
<div id="menu" data-last-update="<?= (time() - 14) ?>000" data-base-target="_main" class="container"
<div id="menu" data-last-update="-1" data-base-target="_main" class="container"
data-icinga-url="<?= $this->href('layout/menu') ?>" data-icinga-refresh="15">
<?= $this->partial(
'layout/menu.phtml',

File diff suppressed because it is too large Load Diff

View File

@ -130,8 +130,8 @@ msgid "Authentication backend name missing"
msgstr "Falta o nome do backend de autenticação"
#: /usr/local/icingaweb/application/forms/Config/AuthenticationBackendReorderForm.php:55
msgid "Authentication order updated!"
msgstr "Ordem da autenticação atualizada!"
msgid "Authentication order updated"
msgstr "Ordem da autenticação atualizada"
#: /usr/local/icingaweb/application/forms/Config/AuthenticationBackendConfigForm.php:307
msgid "Autologin"

View File

@ -37,7 +37,7 @@ class Zend_View_Helper_DateFormat extends Zend_View_Helper_Abstract
/**
* Helper entry point
*
* @return self
* @return $this
*/
public function dateFormat()
{

View File

@ -9,39 +9,39 @@
-->
<div class="content">
<div class="alert alert-warning" id="logout-status">
<b> <?= t('Logging out...'); ?> </b> <br />
<?= t(
'If this message does not disappear, it might be necessary to quit the ' .
'current session manually by clearing the cache, or by closing the current ' .
'browser session.'
<b><?= $this->translate('Logging out...'); ?></b>
<br>
<?= $this->translate(
'If this message does not disappear, it might be necessary to quit the'
. ' current session manually by clearing the cache, or by closing the current'
. ' browser session.'
); ?>
</div>
<div class="container" >
<a href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a>
<div class="container">
<a href="<?= $this->href('dashboard/index?renderLayout'); ?>"><?= $this->translate('Login'); ?></a>
</div>
</div>
<script type="text/javascript">
/**
/*
* When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it
* before it is able to finish. This will cause the browser to show a new authentication prompt in the next
* request.
*/
$(document).ready(function() {
msg = $('#logout-status');
document.addEventListener('DOMContentLoaded', function () {
var msg = document.getElementById('logout-status');
try {
if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
document.execCommand('ClearAuthenticationCache');
} else {
var xhttp = getXMLHttpRequest();
var xhttp = new XMLHttpRequest();
xhttp.open('GET', 'arbitrary url', true, 'logout', 'logout');
xhttp.send('');
xhttp.abort();
}
} catch (e) {
}
msg.html('<?= t('Logout successful!'); ?>');
msg.removeClass();
msg.addClass('alert alert-success');
msg.innerHTML = '<?= $this->translate('Logout successful!'); ?>';
msg.className = 'alert alert-success';
});
</script>

View File

@ -2,20 +2,9 @@
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<h1 tabindex="-1" id="authentication-configuration">
<?= $this->translate('Authentication Configuration'); ?>
</h1>
<h2 tabindex="-1" id="authentication-new-backend" class="sr-only">
<?= t('New Authentication Backend'); ?>
</h2>
<p>
<a href="<?= $this->href('/config/createAuthenticationBackend'); ?>">
<?= $this->icon('plus'); ?><?= $this->translate('Create A New Authentication Backend'); ?>
</a>
</p>
<h2 tabindex="-1" id="authentication-reorder" class="sr-only">
<?= t('Reorder Authentication Backends'); ?>
</h2>
<a href="<?= $this->href('/config/createAuthenticationBackend'); ?>">
<?= $this->icon('plus'); ?><?= $this->translate('Create A New Authentication Backend'); ?>
</a>
<div id="authentication-reorder-form">
<?= $form; ?>
</div>

View File

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

View File

@ -1,10 +1,13 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs ?>
<?= $this->paginationControl($modules) ?>
<?= $this->tabs; ?>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content">
<h1 tabindex="-1"><?= $this->translate('Installed Modules') ?></h1>
<table class="action" data-base-target="_next">
<tbody>
<?php foreach ($modules as $module): ?>

View File

@ -2,20 +2,9 @@
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<h1 tabindex="-1" id="resource-index">
<?= t('Resource Configuration'); ?>
</h1>
<h2 tabindex="-1" id="resource-new-resource" class="sr-only">
<?= t('Create New Resource'); ?>
</h2>
<p>
<a href="<?= $this->href('/config/createresource'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Resource'); ?>
</a>
</p>
<h2 tabindex="-1" id="resource-edit-resource" class="sr-only">
<?= t('Edit Existing Resources'); ?>
</h2>
<a href="<?= $this->href('/config/createresource'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Resource'); ?>
</a>
<table class="action" id="resource-edit-table">
<thead>
<th><?= $this->translate('Resource'); ?></th>

View File

@ -55,8 +55,8 @@
</td>
<td style="table-layout: fixed; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
<?= $this->qlink(
$dashlet->getUrl(),
$dashlet->getUrl(),
$dashlet->getUrl()->getRelativeUrl(),
$dashlet->getUrl()->getRelativeUrl(),
null,
array('title' => sprintf($this->translate('Show dashlet %s'), $dashlet->getTitle()))
); ?>
@ -78,4 +78,4 @@
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>

View File

@ -5,7 +5,7 @@ use Icinga\Web\Widget\SearchDashboard;
<? if (SearchDashboard::search('dummy')->getPane('search')->hasDashlets()): ?>
<form action="<?= $this->href('search') ?>" method="get" role="search">
<input
type="text" name="q" id="search" class="search" placeholder="<?= $this->translate('Search...') ?>"
type="text" name="q" id="search" class="search" placeholder="<?= $this->translate('Search') ?> &hellip;"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
/>
</form>

View File

@ -1,9 +1,12 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs->render($this) ?>
<div style="margin-top: 1em"></div>
<?= $this->logData ?>
<?= $this->tabs; ?>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content">
<?php if ($this->logData !== null): ?>
<table class="action">
@ -16,7 +19,7 @@
<?= $this->escape($value->loglevel) ?>
</td>
<td>
<?= $this->escape($value->message) ?>
<?= nl2br($this->escape($value->message), false) ?>
</td>
</tr>
<?php endforeach; ?>

View File

@ -2,9 +2,6 @@
<?= $tabs ?>
</div>
<div class="content">
<h1 tabindex="-1" id="roles-index">
<?= $this->translate('Roles') ?>
</h1>
<div>
<?php /** @var \Icinga\Application\Config $roles */ if ($roles->isEmpty()): ?>
<?= $this->translate('No roles found.') ?>
@ -70,9 +67,6 @@
</tbody>
</table>
<?php endif ?>
<h2 tabindex="-1" id="roles-index-new" class="sr-only">
<?= $this->translate('Create New Role'); ?>
</h2>
<a data-base-target="_next" href="<?= $this->href('roles/new') ?>">
<?= $this->translate('Create a New Role') ?>
</a>

View File

@ -107,7 +107,7 @@ Icinga Web 2 uses the MD5 based BSD password algorithm. For generating a passwor
command:
````
openssl passwd -1 "password"
openssl passwd -1 password
````
> Note: The switch to `openssl passwd` is the **number one** (`-1`) for using the MD5 based BSD password algorithm.

View File

@ -3,21 +3,133 @@
The preferred way of installing Icinga Web 2 is to use the official package repositories depending on which operating
system and distribution you are running. But it is also possible to install Icinga Web 2 directly from source.
## <a id="installation-requirements"></a> Installing Requirements
## <a id="installing-requirements"></a> Installing Requirements
* A web server, e.g. Apache or nginx
* PHP >= 5.3.0 w/ gettext and OpenSSL support
* MySQL or PostgreSQL PHP libraries when using a database for authentication or storing user preferences into a database
* PHP >= 5.3.0 w/ gettext, intl and OpenSSL support
* MySQL or PostgreSQL PHP libraries when using a database for authentication or for storing preferences into a database
* LDAP PHP library when using Active Directory or LDAP for authentication
* Icinga 1.x w/ Livestatus or IDO, Icinga 2 w/ Livestatus or IDO feature enabled
* Icinga 1.x w/ Livestatus or IDO; Icinga 2.x w/ Livestatus or IDO feature enabled
* MySQL or PostgreSQL PHP libraries when using IDO
## <a id="installation-from-package"></a> Installing Icinga Web 2 from Package
## <a id="installing-from-package"></a> Installing Icinga Web 2 from Package
A guide on how to install Icinga Web 2 from package will follow shortly.
Below is a list of official package repositories for installing Icinga Web 2 for various operating systems.
## <a id="installation-from-source"></a> Installing Icinga Web 2 from Source
Distribution | Repository
------------------------|---------------------------
Debian | [debmon](http://debmon.org/packages/debmon-wheezy/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/)
Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/)
RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/)
openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/)
SLES | [Icinga Repository](http://packages.icinga.org/SUSE/)
Gentoo | -
FreeBSD | -
ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2)
**Step 1: Getting the Source**
Packages for distributions other than the ones listed above may also be available.
Please contact your distribution packagers.
### <a id="package-repositories"></a> Setting up Package Repositories
You need to add the Icinga repository to your package management configuration for installing Icinga Web 2.
Below is a list with examples for various distributions.
Debian (debmon):
````
wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add -
echo 'deb http://debmon.org/debmon debmon-wheezy main' >/etc/apt/sources.list.d/debmon.list
apt-get update
````
Ubuntu Trusty:
````
wget -O - http://packages.icinga.org/icinga.key | apt-key add -
add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-trusty main'
apt-get update
````
For other Ubuntu versions just replace trusty with your distribution's code name.
RHEL and CentOS:
````
rpm --import http://packages.icinga.org/icinga.key
curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/epel/ICINGA-release.repo
yum makecache
````
Fedora:
````
rpm --import http://packages.icinga.org/icinga.key
curl -o /etc/yum.repos.d/ICINGA-release.repo http://packages.icinga.org/fedora/ICINGA-release.repo
yum makecache
````
SLES 11:
````
zypper ar http://packages.icinga.org/SUSE/ICINGA-release-11.repo
zypper ref
````
SLES 12:
````
zypper ar http://packages.icinga.org/SUSE/ICINGA-release.repo
zypper ref
````
openSUSE:
````
zypper ar http://packages.icinga.org/openSUSE/ICINGA-release.repo
zypper ref
````
The packages for RHEL/CentOS depend on other packages which are distributed as part of the
[EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following
[these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F).
### <a id="installing-from-package-example"></a> Installing Icinga Web 2
You can install Icinga Web 2 by using your distribution's package manager to install the `icingaweb2` package.
Below is a list with examples for various distributions.
Debian and Ubuntu:
````
apt-get install icingaweb2
````
RHEL, CentOS and Fedora:
````
yum install icingaweb2
````
SLES and openSUSE:
````
zypper install icingaweb2
````
### <a id="preparing-web-setup-from-package"></a> Preparing Web Setup
You can set up Icinga Web 2 quickly and easily with the Icinga Web 2 setup wizard which is available the first time
you visit Icinga Web 2 in your browser. When using the web setup you are required to authenticate using a token.
In order to generate a token use the `icingacli`:
````
icingacli setup token create
````
In case you do not remember the token you can show it using the `icingacli`:
````
icingacli setup token show
````
Finally visit Icinga Web 2 in your browser to access the setup wizard and complete the installation:
`/icingaweb2/setup`.
## <a id="installing-from-source"></a> Installing Icinga Web 2 from Source
Although the preferred way of installing Icinga Web 2 is to use packages, it is also possible to install Icinga Web 2
directly from source.
### <a id="getting-the-source"></a> Getting the Source
First of all, you need to download the sources. Icinga Web 2 is available through a Git repository. You can clone this
repository either via git or http protocol using the following URLs:
@ -33,7 +145,7 @@ This version also offers snapshots for easy download which you can use if you do
git clone git://git.icinga.org/icingaweb2.git
````
**Step 2: Install the Source**
### <a id="installing-from-source-example"></a> Installing Icinga Web 2
Choose a target directory and move Icinga Web 2 there.
@ -41,7 +153,7 @@ Choose a target directory and move Icinga Web 2 there.
mv icingaweb2 /usr/share/icingaweb2
````
**Step 3: Configuring the Web Server**
### <a id="configuring-web-server"></a> Configuring the Web Server
Use `icingacli` to generate web server configuration for either Apache or nginx.
@ -57,13 +169,15 @@ nginx:
Save the output as new file in your webserver's configuration directory.
Example for Apache on RHEL/CentOS:
Example for Apache on RHEL or CentOS:
````
./bin/icingacli setup config webserver apache --document-root /usr/share/icingaweb2/public > /etc/httpd/conf.d/icingaweb2.conf
````
### <a id="preparing-web-setup-from-source"></a> Preparing Web Setup
**Step 4: Preparing Web Setup**
You can set up Icinga Web 2 quickly and easily with the Icinga Web 2 setup wizard which is available the first time
you visit Icinga Web 2 in your browser. Please follow the steps listed below for preparing the web setup.
Because both web and CLI must have access to configuration and logs, permissions will be managed using a special
system group. The web server user and CLI user have to be added to this system group.
@ -102,6 +216,7 @@ Use `icingacli` to create the configuration directory which defaults to **/etc/i
./bin/icingacli setup config directory
````
When using the web setup you are required to authenticate using a token. In order to generate a token use the
`icingacli`:
````
@ -113,11 +228,10 @@ In case you do not remember the token you can show it using the `icingacli`:
./bin/icingacli setup token show
````
**Step 5: Web Setup**
Finally visit Icinga Web 2 in your browser to access the setup wizard and complete the installation:
`/icingaweb2/setup`.
Visit Icinga Web 2 in your browser and complete installation using the web setup: /icingaweb2/setup
## Upgrading to Icinga Web 2 Beta 2
## <a id="upgrading-to-beta2"></a> Upgrading to Icinga Web 2 Beta 2
Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2,
you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following
@ -133,3 +247,8 @@ After please log out from Icinga Web 2 and log in again for having all permissio
If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external`
authentication backend to be able to log in again. The new name better reflects whats going on. A similar change
affects environments that opted for not storing preferences, your new backend is `none`.
## <a id="upgrading-to-beta3"></a> Upgrading to Icinga Web 2 Beta 3
Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your
configuration files after upgrading to Icinga Web 2 Beta 3.

View File

@ -1,6 +1,6 @@
# Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+
%define revision 2.rc1
%define revision 3.beta3
Name: icingaweb2
Version: 2.0.0

View File

@ -115,7 +115,7 @@ abstract class ApplicationBootstrap
/**
* Whether Icinga Web 2 requires setup
*
* @type bool
* @var bool
*/
protected $requiresSetup = false;
@ -506,12 +506,21 @@ abstract class ApplicationBootstrap
protected function setupLogger()
{
if ($this->config->hasSection('logging')) {
$loggingConfig = $this->config->getSection('logging');
try {
Logger::create($this->config->getSection('logging'));
Logger::create($loggingConfig);
} catch (ConfigurationError $e) {
Logger::error($e);
Logger::getInstance()->registerConfigError($e->getMessage());
try {
Logger::getInstance()->setLevel($loggingConfig->get('level', Logger::ERROR));
} catch (ConfigurationError $e) {
Logger::getInstance()->registerConfigError($e->getMessage());
}
}
}
return $this;
}

View File

@ -77,7 +77,7 @@ class Config implements Countable, Iterator
*
* @param string $filepath The path to the ini file
*
* @return self
* @return $this
*/
public function setConfigFile($filepath)
{
@ -223,7 +223,7 @@ class Config implements Countable, Iterator
* @param string $name
* @param array|ConfigObject $config
*
* @return self
* @return $this
*/
public function setSection($name, $config = null)
{
@ -242,7 +242,7 @@ class Config implements Countable, Iterator
*
* @param string $name
*
* @return self
* @return $this
*/
public function removeSection($name)
{

View File

@ -20,7 +20,7 @@ class EmbeddedWeb extends ApplicationBootstrap
* Embedded bootstrap parts
*
* @see ApplicationBootstrap::bootstrap
* @return self
* @return $this
*/
protected function bootstrap()
{

View File

@ -67,6 +67,13 @@ class Logger
*/
protected $level;
/**
* Error messages to be displayed prior to any other log message
*
* @var array
*/
protected $configErrors = array();
/**
* Create a new logger object
*
@ -81,39 +88,71 @@ class Logger
throw new ConfigurationError('Required logging configuration directive \'log\' missing');
}
if (($level = $config->level) !== null) {
if (is_numeric($level)) {
$level = (int) $level;
if (! isset(static::$levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level %d. Logging level is not defined. Use one of %s or one of the'
. ' Logger\'s constants.',
$level,
implode(', ', array_keys(static::$levels))
);
}
$this->level = $level;
} else {
$level = strtoupper($level);
$levels = array_flip(static::$levels);
if (! isset($levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level "%s". Logging level is not defined. Use one of %s.',
$level,
implode(', ', array_keys($levels))
);
}
$this->level = $levels[$level];
}
} else {
$this->level = static::ERROR;
}
$this->setLevel($config->get('level', static::ERROR));
if (strtolower($config->get('log', 'syslog')) !== 'none') {
$this->writer = $this->createWriter($config);
}
}
/**
* Set the logging level to use
*
* @param mixed $level
*
* @return $this
*
* @throws ConfigurationError In case the given level is invalid
*/
public function setLevel($level)
{
if (is_numeric($level)) {
$level = (int) $level;
if (! isset(static::$levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level %d. Logging level is invalid. Use one of %s or one of the'
. ' Logger\'s constants.',
$level,
implode(', ', array_keys(static::$levels))
);
}
$this->level = $level;
} else {
$level = strtoupper($level);
$levels = array_flip(static::$levels);
if (! isset($levels[$level])) {
throw new ConfigurationError(
'Can\'t set logging level "%s". Logging level is invalid. Use one of %s.',
$level,
implode(', ', array_keys($levels))
);
}
$this->level = $levels[$level];
}
return $this;
}
/**
* Register the given message as config error
*
* Config errors are logged every time a log message is being logged.
*
* @param mixed $arg,... A string, exception or format-string + substitutions
*
* @return $this
*/
public function registerConfigError()
{
if (func_num_args() > 0) {
$this->configErrors[] = static::formatMessage(func_get_args());
}
return $this;
}
/**
* Create a new logger object
*
@ -156,6 +195,10 @@ class Logger
public function log($level, $message)
{
if ($this->writer !== null && $this->level <= $level) {
foreach ($this->configErrors as $error_message) {
$this->writer->log(static::ERROR, $error_message);
}
$this->writer->log($level, $message);
}
}
@ -271,7 +314,7 @@ class Logger
*/
public static function writesToSyslog()
{
return static::$instance && static::$instance instanceof SyslogWriter;
return static::$instance && static::$instance->getWriter() instanceof SyslogWriter;
}
/**
@ -281,7 +324,7 @@ class Logger
*/
public static function writesToFile()
{
return static::$instance && static::$instance instanceof FileWriter;
return static::$instance && static::$instance->getWriter() instanceof FileWriter;
}
/**

View File

@ -67,6 +67,6 @@ class SyslogWriter extends LogWriter
public function log($level, $message)
{
openlog($this->ident, LOG_PID, $this->facility);
syslog(static::$severityMap[$level], $message);
syslog(static::$severityMap[$level], str_replace("\n", ' ', $message));
}
}

View File

@ -12,7 +12,6 @@ use Icinga\Exception\ConfigurationError;
use Icinga\Exception\SystemPermissionException;
use Icinga\Exception\ProgrammingError;
use Icinga\Exception\NotReadableError;
use Icinga\Exception\NotFoundError;
/**
* Module manager that handles detecting, enabling and disabling of modules
@ -102,6 +101,16 @@ class Manager
*/
private function detectEnabledModules()
{
if (! file_exists($parent = dirname($this->enableDir))) {
return;
}
if (! is_readable($parent)) {
throw new NotReadableError(
'Cannot read enabled modules. Module directory\'s parent directory "%s" is not readable',
$parent
);
}
if (! file_exists($this->enableDir)) {
return;
}
@ -156,7 +165,7 @@ class Manager
/**
* Try to set all enabled modules in loaded sate
*
* @return self
* @return $this
* @see Manager::loadModule()
*/
public function loadEnabledModules()
@ -173,7 +182,7 @@ class Manager
* @param string $name The name of the module to load
* @param mixed $basedir Optional module base directory
*
* @return self
* @return $this
*/
public function loadModule($name, $basedir = null)
{
@ -197,9 +206,8 @@ class Manager
*
* @param string $name The module to enable
*
* @return self
* @return $this
* @throws ConfigurationError When trying to enable a module that is not installed
* @throws NotFoundError In case the "enabledModules" directory does not exist
* @throws SystemPermissionException When insufficient permissions for the application exist
*/
public function enableModule($name)
@ -218,14 +226,15 @@ class Manager
if (! is_dir($this->enableDir) && !@mkdir($this->enableDir, 02770, true)) {
$error = error_get_last();
throw new SystemPermissionException(
'Failed to create enabledModule directory "%s" (%s)',
'Failed to create enabledModules directory "%s" (%s)',
$this->enableDir,
$error['message']
);
} elseif (! is_writable($this->enableDir)) {
throw new SystemPermissionException(
'Cannot enable module "%s". Insufficient system permissions for enabling modules.',
$name
'Cannot enable module "%s". Check the permissions for the enabledModules directory: %s',
$name,
$this->enableDir
);
}
@ -237,10 +246,11 @@ class Manager
$error = error_get_last();
if (strstr($error["message"], "File exists") === false) {
throw new SystemPermissionException(
'Could not enable module "%s" due to file system errors. '
'Cannot enable module "%s" at %s due to file system errors. '
. 'Please check path and mounting points because this is not a permission error. '
. 'Primary error was: %s',
$name,
$this->enableDir,
$error['message']
);
}
@ -252,39 +262,44 @@ 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
*
* @return self
* @return $this
*
* @throws ConfigurationError When the module is not installed or it's not a symlink
* @throws SystemPermissionException When the module can't be disabled
* @throws SystemPermissionException When insufficient permissions for the application exist
*/
public function disableModule($name)
{
if (! $this->hasEnabled($name)) {
return $this;
}
if (! is_writable($this->enableDir)) {
throw new SystemPermissionException(
'Could not disable module. Module path is not writable.'
'Cannot disable module "%s". Check the permissions for the enabledModules directory: %s',
$name,
$this->enableDir
);
}
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
if (! file_exists($link)) {
throw new ConfigurationError(
'Could not disable module. The module %s was not found.',
'Cannot disable module "%s". Module is not installed.',
$name
);
}
if (! is_link($link)) {
throw new ConfigurationError(
'Could not disable module. The module "%s" is not a symlink. '
'Cannot disable module %s at %s. '
. 'It looks like you have installed this module manually and moved it to your module folder. '
. 'In order to dynamically enable and disable modules, you have to create a symlink to '
. 'the enabled_modules folder.',
$name
. 'the enabledModules folder.',
$name,
$this->enableDir
);
}
@ -292,10 +307,11 @@ class Manager
if (! @unlink($link)) {
$error = error_get_last();
throw new SystemPermissionException(
'Could not disable module "%s" due to file system errors. '
'Cannot enable module "%s" at %s due to file system errors. '
. 'Please check path and mounting points because this is not a permission error. '
. 'Primary error was: %s',
$name,
$this->enableDir,
$error['message']
);
}
@ -491,7 +507,7 @@ class Manager
*
* @param array $availableDirs Installed modules location
*
* @return self
* @return $this
*/
public function detectInstalledModules(array $availableDirs = null)
{

View File

@ -113,6 +113,13 @@ class Module
*/
private $triedToLaunchConfigScript = false;
/**
* Whether this module has been registered
*
* @var bool
*/
private $registered = false;
/**
* Provided permissions
*
@ -177,14 +184,18 @@ class Module
protected $searchUrls = array();
/**
* @param string $title
* @param string $url
* Provide a search URL
*
* @param string $title
* @param string $url
* @param int $priority
*/
public function provideSearchUrl($title, $url)
public function provideSearchUrl($title, $url, $priority = 0)
{
$searchUrl = (object) array(
'title' => $title,
'url' => $url
'title' => (string) $title,
'url' => (string) $url,
'priority' => (int) $priority
);
$this->searchUrls[] = $searchUrl;
@ -279,6 +290,10 @@ class Module
*/
public function register()
{
if ($this->registered) {
return true;
}
$this->registerAutoloader();
try {
$this->launchRunScript();
@ -291,10 +306,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
*
@ -725,7 +752,7 @@ class Module
* @param string $name Unique tab name
* @param string $config Tab config
*
* @return self
* @return $this
*/
protected function provideConfigTab($name, $config = array())
{
@ -742,7 +769,7 @@ class Module
*
* @param string $className The name of the class
*
* @return self
* @return $this
*/
protected function provideSetupWizard($className)
{
@ -753,7 +780,7 @@ class Module
/**
* Register new namespaces on the autoloader
*
* @return self
* @return $this
*/
protected function registerAutoloader()
{
@ -775,7 +802,7 @@ class Module
/**
* Bind text domain for i18n
*
* @return self
* @return $this
*/
protected function registerLocales()
{
@ -822,7 +849,7 @@ class Module
*
* Add controller directory to mvc
*
* @return self
* @return $this
*/
protected function registerWebIntegration()
{
@ -845,7 +872,7 @@ class Module
/**
* Add routes for static content and any route added via addRoute() to the route chain
*
* @return self
* @return $this
* @see addRoute()
*/
protected function registerRoutes()
@ -885,7 +912,7 @@ class Module
/**
* Run module bootstrap script
*
* @return self
* @return $this
*/
protected function launchRunScript()
{
@ -897,7 +924,7 @@ class Module
*
* @param string $file File to include
*
* @return self
* @return $this
*/
protected function includeScript($file)
{
@ -913,7 +940,7 @@ class Module
*/
protected function launchConfigScript()
{
if ($this->triedToLaunchConfigScript) {
if ($this->triedToLaunchConfigScript || !$this->registered) {
return;
}
$this->triedToLaunchConfigScript = true;
@ -931,7 +958,7 @@ class Module
* @param string $class
* @param string $key
*
* @return self
* @return $this
*/
protected function registerHook($name, $class, $key = null)
{
@ -950,7 +977,7 @@ class Module
* @param string $name Name of the route
* @param Zend_Controller_Router_Route_Abstract $route Instance of the route
*
* @return self
* @return $this
* @see registerRoutes()
*/
protected function addRoute($name, Zend_Controller_Router_Route_Abstract $route)

View File

@ -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');
}
}

View File

@ -5,28 +5,23 @@ namespace Icinga\Application;
require_once __DIR__ . '/ApplicationBootstrap.php';
use Icinga\Authentication\Manager as AuthenticationManager;
use Icinga\Authentication\Manager;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Application\Logger;
use Icinga\Util\TimezoneDetect;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\View;
use Icinga\Web\Session\Session as BaseSession;
use Icinga\Web\Session;
use Icinga\User;
use Icinga\Util\Translator;
use Icinga\Util\DateTimeFactory;
use DateTimeZone;
use Exception;
use Zend_Controller_Action_HelperBroker;
use Zend_Controller_Front;
use Zend_Controller_Router_Route;
use Zend_Layout;
use Zend_Paginator;
use Zend_View_Helper_PaginationControl;
use Zend_Controller_Action_HelperBroker as ActionHelperBroker;
use Zend_Controller_Router_Route;
use Zend_Controller_Front;
use Icinga\Application\Logger;
use Icinga\Authentication\Manager;
use Icinga\User;
use Icinga\Util\TimezoneDetect;
use Icinga\Util\Translator;
use Icinga\Web\Notification;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\Session;
use Icinga\Web\Session\Session as BaseSession;
use Icinga\Web\View;
/**
* Use this if you want to make use of Icinga functionality in other web projects
@ -84,7 +79,7 @@ class Web extends ApplicationBootstrap
/**
* Initialize all together
*
* @return self
* @return $this
*/
protected function bootstrap()
{
@ -95,6 +90,7 @@ class Web extends ApplicationBootstrap
->loadConfig()
->setupResourceFactory()
->setupSession()
->setupNotifications()
->setupUser()
->setupTimezone()
->setupLogger()
@ -112,7 +108,7 @@ class Web extends ApplicationBootstrap
/**
* Prepare routing
*
* @return self
* @return $this
*/
private function setupRoute()
{
@ -161,44 +157,39 @@ class Web extends ApplicationBootstrap
/**
* Prepare Zend MVC Base
*
* @return self
* @return $this
*/
private function setupZendMvc()
{
// TODO: Replace Zend_Application:
Zend_Layout::startMvc(
array(
'layout' => 'layout',
'layoutPath' => $this->getApplicationDir('/layouts/scripts')
)
);
$this->setupFrontController();
$this->setupViewRenderer();
return $this;
}
/**
* Create user object
*
* @return self
* @return $this
*/
private function setupUser()
{
$authenticationManager = AuthenticationManager::getInstance();
if ($authenticationManager->isAuthenticated() === true) {
$this->user = $authenticationManager->getUser();
$auth = Manager::getInstance();
if ($auth->isAuthenticated()) {
$this->user = $auth->getUser();
}
return $this;
}
/**
* Initialize a session provider
*
* @return self
* @return $this
*/
private function setupSession()
{
@ -206,85 +197,82 @@ class Web extends ApplicationBootstrap
return $this;
}
/**
* Initialize notifications to remove them immediately from session
*
* @return $this
*/
private function setupNotifications()
{
Notification::getInstance();
return $this;
}
/**
* Inject dependencies into request
*
* @return self
* @return $this
*/
private function setupRequest()
{
$this->request = new Request();
if ($this->user instanceof User) {
$this->request->setUser($this->user);
}
return $this;
}
/**
* Instantiate front controller
*
* @return self
* @return $this
*/
private function setupFrontController()
{
$this->frontController = Zend_Controller_Front::getInstance();
$this->frontController->setRequest($this->request);
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
$this->frontController->setParams(
array(
'displayExceptions' => true
)
);
return $this;
}
/**
* Register helper paths and views for renderer
*
* @return self
* @return $this
*/
private function setupViewRenderer()
{
$view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
/** @var \Zend_Controller_Action_Helper_ViewRenderer $view */
$view = ActionHelperBroker::getStaticHelper('viewRenderer');
$view->setView(new View());
$view->view->addHelperPath($this->getApplicationDir('/views/helpers'));
$view->view->setEncoding('UTF-8');
$view->view->headTitle()->prepend($this->config->get('global', 'project', 'Icinga'));
$view->view->headTitle()->setSeparator(' :: ');
$this->viewRenderer = $view;
return $this;
}
/**
* Configure pagination settings
*
* @return self
* @return $this
*/
private function setupPagination()
{
Zend_Paginator::addScrollingStylePrefixPath(
'Icinga_Web_Paginator_ScrollingStyle',
'Icinga/Web/Paginator/ScrollingStyle'
);
Zend_Paginator::setDefaultScrollingStyle('SlidingWithBorder');
Zend_View_Helper_PaginationControl::setDefaultViewPartial(
array('mixedPagination.phtml', 'default')
);
return $this;
}
@ -307,26 +295,30 @@ class Web extends ApplicationBootstrap
/**
* Setup internationalization using gettext
*
* Uses the preferred user language or the configured default and system default, respectively.
* Uses the preferred user language or the browser suggested language or our default.
*
* @return self
* @return string Detected locale code
*
* @see Translator::DEFAULT_LOCALE For the the default locale code.
*/
protected function detectLocale()
{
$auth = Manager::getInstance();
if (! $auth->isAuthenticated()
|| ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) === null
&& isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])
if ($auth->isAuthenticated()
&& ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) !== null
) {
$locale = Translator::getPreferredLocaleCode($_SERVER['HTTP_ACCEPT_LANGUAGE']);
return $locale;
}
return $locale;
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
return Translator::getPreferredLocaleCode($_SERVER['HTTP_ACCEPT_LANGUAGE']);
}
return Translator::DEFAULT_LOCALE;
}
/**
* Setup an autoloader namespace for Icinga\Forms
*
* @return self
* @return $this
*/
private function setupFormNamespace()
{
@ -337,4 +329,3 @@ class Web extends ApplicationBootstrap
return $this;
}
}
// @codeCoverageIgnoreEnd

View File

@ -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;
}
/**

View File

@ -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,17 +26,55 @@ class LdapUserBackend extends UserBackend
protected $userNameAttribute;
protected $customFilter;
protected $groupOptions;
public function __construct(Connection $conn, $userClass, $userNameAttribute, $baseDn, $groupOptions = null)
{
/**
* Normed attribute names based on known LDAP environments
*
* @var array
*/
protected $normedAttributes = array(
'uid' => 'uid',
'user' => 'user',
'inetorgperson' => 'inetOrgPerson',
'samaccountname' => 'sAMAccountName'
);
public function __construct(
Connection $conn,
$userClass,
$userNameAttribute,
$baseDn,
$cutomFilter,
$groupOptions = null
) {
$this->conn = $conn;
$this->baseDn = trim($baseDn) !== '' ? $baseDn : $conn->getDN();
$this->userClass = $userClass;
$this->userNameAttribute = $userNameAttribute;
$this->baseDn = trim($baseDn) ?: $conn->getDN();
$this->userClass = $this->getNormedAttribute($userClass);
$this->userNameAttribute = $this->getNormedAttribute($userNameAttribute);
$this->customFilter = trim($cutomFilter);
$this->groupOptions = $groupOptions;
}
/**
* Return the given attribute name normed to known LDAP enviroments, if possible
*
* @param string $name
*
* @return string
*/
protected function getNormedAttribute($name)
{
$loweredName = strtolower($name);
if (array_key_exists($loweredName, $this->normedAttributes)) {
return $this->normedAttributes[$loweredName];
}
return $name;
}
/**
* Create a query to select all usernames
*
@ -43,12 +82,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 +133,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'
);
}

View File

@ -26,7 +26,7 @@ abstract class UserBackend implements Countable
*
* @param string $name
*
* @return self
* @return $this
*/
public function setName($name)
{
@ -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;

View File

@ -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
@ -115,7 +115,7 @@ class Axis implements Drawable
*
* @param AxisUnit $unit The AxisUnit implementation to use for the x axis
*
* @return self This Axis Object
* @return $this This Axis Object
* @see Axis::CalendarUnit
* @see Axis::LinearUnit
*/
@ -130,7 +130,7 @@ class Axis implements Drawable
*
* @param AxisUnit $unit The AxisUnit implementation to use for the y axis
*
* @return self This Axis Object
* @return $this This Axis Object
* @see Axis::CalendarUnit
* @see Axis::LinearUnit
*/
@ -331,7 +331,7 @@ class Axis implements Drawable
*
* @param string $label The label to use for the y axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setYLabel($label)
{
@ -346,7 +346,7 @@ class Axis implements Drawable
*
* @param int $xMin The minimum value to use for the x axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setXMin($xMin)
{
@ -361,7 +361,7 @@ class Axis implements Drawable
*
* @param int $yMin The minimum value to use for the x axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setYMin($yMin)
{
@ -376,7 +376,7 @@ class Axis implements Drawable
*
* @param int $xMax The minimum value to use for the x axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setXMax($xMax)
{
@ -391,7 +391,7 @@ class Axis implements Drawable
*
* @param int $yMax The minimum value to use for the y axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setYMax($yMax)
{

View File

@ -132,7 +132,7 @@ class GridChart extends Chart
*
* @param array $axis,... The line definitions to draw
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function drawLines(array $axis)
{
@ -146,7 +146,7 @@ class GridChart extends Chart
* Refer to the graphs.md for a detailed list of allowed attributes
*
* @param array $axis
* @return self
* @return $this
*/
public function drawBars(array $axis)
{
@ -215,7 +215,7 @@ class GridChart extends Chart
* @param string $yAxisLabel The label to use for the y axis
* @param string $axisName The name of the axis, for now 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAxisLabel($xAxisLabel, $yAxisLabel, $axisName = 'default')
{
@ -229,7 +229,7 @@ class GridChart extends Chart
* @param AxisUnit $unit The unit for the x axis
* @param string $axisName The name of the axis to set the label for, currently only 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setXAxis(AxisUnit $unit, $axisName = 'default')
{
@ -243,7 +243,7 @@ class GridChart extends Chart
* @param AxisUnit $unit The unit for the y axis
* @param string $axisName The name of the axis to set the label for, currently only 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setYAxis(AxisUnit $unit, $axisName = 'default')
{
@ -276,7 +276,7 @@ class GridChart extends Chart
* @param Axis $axis The new axis to use
* @param string $name The name of the axis, currently only 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAxis(Axis $axis, $name = 'default')
{
@ -290,7 +290,7 @@ class GridChart extends Chart
* @param Axis $axis The axis object to add
* @param string $name The name of the axis
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function addAxis(Axis $axis, $name)
{
@ -307,7 +307,7 @@ class GridChart extends Chart
* @param int $yMin The minimum value for the y axis or null to use a dynamic value
* @param string $axisName The name of the axis to set the minimum, currently only 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAxisMin($xMin = null, $yMin = null, $axisName = 'default')
{
@ -324,7 +324,7 @@ class GridChart extends Chart
* @param int $yMax The maximum value for the y axis or null to use a dynamic value
* @param string $axisName The name of the axis to set the maximum, currently only 'default'
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAxisMax($xMax = null, $yMax = null, $axisName = 'default')
{

View File

@ -107,7 +107,7 @@ class PieChart extends Chart
*
* @param array $dataSet,... The pie definition, see graphs.md for further details concerning the format
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function drawPie(array $dataSet)
{
@ -272,7 +272,7 @@ class PieChart extends Chart
*
* @param string $type Either self::STACKED or self::ROW
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function setType($type)
{
@ -283,7 +283,7 @@ class PieChart extends Chart
/**
* Hide the caption from this PieChart
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function disableLegend()
{

View File

@ -77,7 +77,7 @@ class Path extends Styleable implements Drawable
*
* @param array $points Either a single [x, y] point or an array of x, y points
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function append(array $points)
{
@ -96,7 +96,7 @@ class Path extends Styleable implements Drawable
*
* @param array $points Either a single [x, y] point or an array of x, y points
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function prepend(array $points)
{
@ -115,7 +115,7 @@ class Path extends Styleable implements Drawable
*
* @param boolean $bool True to draw discrete or false to draw straight lines between points
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setDiscrete($bool)
{
@ -126,7 +126,7 @@ class Path extends Styleable implements Drawable
/**
* Mark this path as containing absolute coordinates
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function toAbsolute()
{

View File

@ -189,7 +189,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param int $x The new x position
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setX($x)
{
@ -202,7 +202,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param int $y The new y position
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setY($y)
{
@ -215,7 +215,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param DOMElement $group The label group
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setLabelGroup(DOMElement $group)
{
@ -228,7 +228,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param string $caption The caption for this element
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setCaption($caption)
{
@ -241,7 +241,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param int $offset The offset for the caption handle
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setCaptionOffset($offset)
{
@ -254,7 +254,7 @@ class PieSlice extends Animatable implements Drawable
*
* @param int $bound The offset for the caption text
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setOuterCaptionBound($bound)
{

View File

@ -59,7 +59,7 @@ class Styleable
*
* @param string $width The stroke with with unit
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setStrokeWidth($width)
{
@ -72,7 +72,7 @@ class Styleable
*
* @param string $color The color to set for the stroke
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setStrokeColor($color)
{
@ -85,7 +85,7 @@ class Styleable
*
* @param string $styles The styles to set additionally
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAdditionalStyle($styles)
{
@ -98,7 +98,7 @@ class Styleable
*
* @param string $color The color to use for filling or null to use no fill
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setFill($color = null)
{
@ -111,7 +111,7 @@ class Styleable
*
* @param string $id The id to set for this element
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setId($id)
{

View File

@ -104,7 +104,7 @@ class Text extends Styleable implements Drawable
*
* @param string $size The font size including a unit
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setFontSize($size)
{
@ -117,7 +117,7 @@ class Text extends Styleable implements Drawable
*
* @param String $align Value how to align
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setAlignment($align)
{
@ -130,7 +130,7 @@ class Text extends Styleable implements Drawable
*
* @param string $weight The weight of the string
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function setFontWeight($weight)
{

View File

@ -110,7 +110,7 @@ class CalendarUnit extends LinearUnit
* @param array $dataset The dataset to update
* @param int $idx The index to use for determining the data
*
* @return self Fluid interface
* @return $this Fluid interface
*/
public function addValues(array $dataset, $idx = 0)
{

View File

@ -74,7 +74,7 @@ class LinearUnit implements AxisUnit
* @param array $dataset The dataset to add
* @param int $idx The idx (0 for x, 1 for y)
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function addValues(array $dataset, $idx = 0)
{

View File

@ -65,7 +65,7 @@ class LogarithmicUnit implements AxisUnit
* @param array $dataset The dataset to add
* @param int $idx The idx (0 for x, 1 for y)
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function addValues(array $dataset, $idx = 0)
{

View File

@ -15,7 +15,7 @@ class StaticAxis implements AxisUnit
* @param array $dataset The dataset that will be shown in the Axis
* @param int $idx The idx in the dataset (0 for x, 1 for y)
*
* @return self Fluent interface
* @return $this Fluent interface
*/
public function addValues(array $dataset, $idx = 0)
{

View File

@ -17,7 +17,7 @@ abstract class Command
protected $docs;
/**
* @type Params
* @var Params
*/
protected $params;
protected $screen;

View File

@ -3,6 +3,8 @@
namespace Icinga\Cli;
use Icinga\Exception\MissingParameterException;
/**
* Params
*
@ -155,13 +157,36 @@ class Params
return $default;
}
/**
* Require a parameter
*
* @param string $name Name of the parameter
* @param bool $strict Whether the parameter's value must not be the empty string
*
* @return mixed
*
* @throws MissingParameterException If the parameter was not given
*/
public function req($name, $strict = true)
{
if ($this->has($name)) {
$value = $this->get($name);
if (! $strict || strlen($value) > 0) {
return $value;
}
}
$e = new MissingParameterException(t('Required parameter \'%s\' missing'), $name);
$e->setParameter($name);
throw $e;
}
/**
* Set a value for the given option
*
* @param string $key The option name
* @param mixed $value The value to set
*
* @return self
* @return $this
*/
public function set($key, $value)
{
@ -174,7 +199,7 @@ class Params
*
* @param string|array $keys The option or options to remove
*
* @return self
* @return $this
*/
public function remove($keys = array())
{
@ -238,7 +263,7 @@ class Params
*
* @param mixed $key The argument
*
* @return self
* @return $this
*/
public function unshift($key)
{

View File

@ -274,7 +274,7 @@ class ConfigObject implements Countable, Iterator, ArrayAccess
*
* @param array|Config $data An array or a config
*
* @return self
* @return $this
*/
public function merge($data)
{

View File

@ -183,7 +183,7 @@ class DbConnection implements Selectable
*
* @param string $prefix
*
* @return self
* @return $this
*/
public function setTablePrefix($prefix)
{

View File

@ -63,7 +63,7 @@ class PivotTable
/**
* Prepare the queries used for the pre processing
*
* @return self
* @return $this
*/
protected function prepareQueries()
{
@ -82,7 +82,7 @@ class PivotTable
/**
* Set a default sorting for the x- and y-axis without losing any existing rules
*
* @return self
* @return $this
*/
protected function adjustSorting()
{

View File

@ -106,7 +106,7 @@ class SimpleQuery implements QueryInterface, Queryable
*
* Query will return all available columns if none are given here
*
* @return self
* @return $this
*/
public function from($target, array $fields = null)
{
@ -125,7 +125,7 @@ class SimpleQuery implements QueryInterface, Queryable
* @param string $condition
* @param mixed $value
*
* @return self
* @return $this
*/
public function where($condition, $value = null)
{
@ -161,6 +161,26 @@ class SimpleQuery implements QueryInterface, Queryable
throw new IcingaException('This function does nothing and will be removed');
}
/**
* Split order field into its field and sort direction
*
* @param string $field
*
* @return array
*/
public function splitOrder($field)
{
$fieldAndDirection = explode(' ', $field, 2);
if (count($fieldAndDirection) === 1) {
$direction = null;
} else {
$field = $fieldAndDirection[0];
$direction = (strtoupper(trim($fieldAndDirection[1])) === 'DESC') ?
Sortable::SORT_DESC : Sortable::SORT_ASC;
}
return array($field, $direction);
}
/**
* Sort result set by the given field (and direction)
*
@ -172,18 +192,14 @@ class SimpleQuery implements QueryInterface, Queryable
* @param string $field
* @param string $direction
*
* @return self
* @return $this
*/
public function order($field, $direction = null)
{
if ($direction === null) {
$fieldAndDirection = explode(' ', $field, 2);
if (count($fieldAndDirection) === 1) {
$direction = self::SORT_ASC;
} else {
$field = $fieldAndDirection[0];
$direction = (strtoupper(trim($fieldAndDirection[1])) === 'DESC') ?
Sortable::SORT_DESC : Sortable::SORT_ASC;
list($field, $direction) = $this->splitOrder($field);
if ($direction === null) {
$direction = Sortable::SORT_ASC;
}
} else {
switch (($direction = strtoupper($direction))) {
@ -254,7 +270,7 @@ class SimpleQuery implements QueryInterface, Queryable
* @param int $count Number of rows to return
* @param int $offset Start returning after this many rows
*
* @return self
* @return $this
*/
public function limit($count = null, $offset = null)
{
@ -401,7 +417,7 @@ class SimpleQuery implements QueryInterface, Queryable
*
* @param array $columns
*
* @return self
* @return $this
*/
public function columns(array $columns)
{

View File

@ -14,14 +14,14 @@ class SimpleTree implements IteratorAggregate
/**
* Root node
*
* @type TreeNode
* @var TreeNode
*/
protected $sentinel;
/**
* Nodes
*
* @type array
* @var array
*/
protected $nodes = array();

View File

@ -10,7 +10,7 @@ class TreeNode implements Identifiable
/**
* The node's ID
*
* @type mixed
* @var mixed
*/
protected $id;
@ -24,7 +24,7 @@ class TreeNode implements Identifiable
/**
* The node's children
*
* @type array
* @var array
*/
protected $children = array();

View File

@ -14,7 +14,7 @@ class TreeNodeIterator implements RecursiveIterator
/**
* The node's children
*
* @type array
* @var array
*/
protected $children;

View File

@ -4,9 +4,37 @@
namespace Icinga\Exception;
/**
* Class MissingParameterException
* @package Icinga\Exception
* Exception thrown if a mandatory parameter was not given
*/
class MissingParameterException extends IcingaException
{
/**
* Name of the missing parameter
*
* @var string
*/
protected $parameter;
/**
* Get the name of the missing parameter
*
* @return string
*/
public function getParameter()
{
return $this->parameter;
}
/**
* Set the name of the missing parameter
*
* @param string $name
*
* @return $this
*/
public function setParameter($name)
{
$this->parameter = (string) $name;
return $this;
}
}

View File

@ -33,7 +33,7 @@ class FileExtensionFilterIterator extends FilterIterator
/**
* The extension to filter for
*
* @type string
* @var string
*/
protected $extension;
@ -58,7 +58,7 @@ class FileExtensionFilterIterator extends FilterIterator
public function accept()
{
$current = $this->current();
/** @type $current \SplFileInfo */
/** @var $current \SplFileInfo */
if (! $current->isFile()) {
return false;
}

View File

@ -37,7 +37,7 @@ class NonEmptyFileIterator extends FilterIterator
public function accept()
{
$current = $this->current();
/** @type $current \SplFileInfo */
/** @var $current \SplFileInfo */
if (! $current->isFile()
|| $current->getSize() === 0
) {

View File

@ -26,6 +26,13 @@ class FileReader implements Selectable, Countable
*/
protected $filename;
/**
* Cache for static::count()
*
* @var int
*/
protected $count = null;
/**
* Create a new reader
*
@ -51,7 +58,7 @@ class FileReader implements Selectable, Countable
*/
public function iterate()
{
return new FileIterator($this->filename, $this->fields);
return new LogFileIterator($this->filename, $this->fields);
}
/**
@ -71,7 +78,10 @@ class FileReader implements Selectable, Countable
*/
public function count()
{
return iterator_count($this->iterate());
if ($this->count === null) {
$this->count = iterator_count($this->iterate());
}
return $this->count;
}
/**

View File

@ -0,0 +1,155 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Protocol\File;
use Icinga\Exception\IcingaException;
use SplFileObject;
use Iterator;
/**
* Iterate over a log file, yielding the regex fields of the log messages
*/
class LogFileIterator implements Iterator
{
/**
* Log file
*
* @var SplFileObject
*/
protected $file;
/**
* A PCRE string with the fields to extract
* from the log messages as named subpatterns
*
* @var string
*/
protected $fields;
/**
* Value for static::current()
*
* @var string
*/
protected $current;
/**
* Index for static::key()
*
* @var int
*/
protected $index;
/**
* Value for static::valid()
*
* @var boolean
*/
protected $valid;
/**
* @var string
*/
protected $next = null;
/**
* @param string $filename The log file's name
* @param string $fields A PCRE string with the fields to extract
* from the log messages as named subpatterns
*/
public function __construct($filename, $fields)
{
$this->file = new SplFileObject($filename);
$this->file->setFlags(
SplFileObject::DROP_NEW_LINE |
SplFileObject::READ_AHEAD
);
$this->fields = $fields;
}
public function rewind()
{
$this->file->rewind();
$this->index = 0;
$this->nextMessage();
}
public function next()
{
$this->file->next();
++$this->index;
$this->nextMessage();
}
/**
* @return string
*/
public function current()
{
return $this->current;
}
/**
* @return int
*/
public function key()
{
return $this->index;
}
/**
* @return boolean
*/
public function valid()
{
return $this->valid;
}
protected function nextMessage()
{
$message = $this->next === null ? array() : array($this->next);
$this->valid = null;
while ($this->file->valid()) {
if (false === ($res = preg_match(
$this->fields, $current = $this->file->current()
))) {
throw new IcingaException('Failed at preg_match()');
}
if (empty($message)) {
if ($res === 1) {
$message[] = $current;
}
} else if ($res === 1) {
$this->next = $current;
$this->valid = true;
break;
} else {
$message[] = $current;
}
$this->file->next();
}
if ($this->valid === null) {
$this->next = null;
$this->valid = ! empty($message);
}
if ($this->valid) {
while (! empty($message)) {
$matches = array();
if (false === ($res = preg_match(
$this->fields, implode(PHP_EOL, $message), $matches
))) {
throw new IcingaException('Failed at preg_match()');
}
if ($res === 1) {
$this->current = $matches;
return;
}
array_pop($message);
}
$this->valid = false;
}
}
}

View File

@ -121,7 +121,7 @@ class Capability
*
* @return bool Whether StartTLS is supported
*/
public function hasStartTLS()
public function hasStartTls()
{
return isset($this->oids[self::LDAP_SERVER_START_TLS_OID]);
}

View File

@ -3,7 +3,6 @@
namespace Icinga\Protocol\Ldap;
use Exception;
use Icinga\Exception\ProgrammingError;
use Icinga\Protocol\Ldap\Exception as LdapException;
use Icinga\Application\Platform;
@ -24,10 +23,6 @@ use Icinga\Data\ConfigObject;
* 'bind_pw' => '***'
* ));
* </code>
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Connection
{
@ -36,6 +31,27 @@ class Connection
const LDAP_ADMINLIMIT_EXCEEDED = 11;
const PAGE_SIZE = 1000;
/**
* Encrypt connection using STARTTLS (upgrading a plain text connection)
*
* @var string
*/
const STARTTLS = 'starttls';
/**
* Encrypt connection using LDAP over SSL (using a separate port)
*
* @var string
*/
const LDAPS = 'ldaps';
/**
* Encryption for the connection if any
*
* @var string|null
*/
protected $encryption;
protected $ds;
protected $hostname;
protected $port = 389;
@ -43,6 +59,7 @@ class Connection
protected $bind_pw;
protected $root_dn;
protected $count;
protected $reqCert = true;
/**
* Whether the bind on this connection was already performed
@ -66,8 +83,6 @@ class Connection
/**
* Constructor
*
* TODO: Allow to pass port and SSL options
*
* @param ConfigObject $config
*/
public function __construct(ConfigObject $config)
@ -77,6 +92,11 @@ class Connection
$this->bind_pw = $config->bind_pw;
$this->root_dn = $config->root_dn;
$this->port = $config->get('port', $this->port);
$this->encryption = $config->get('encryption');
if ($this->encryption !== null) {
$this->encryption = strtolower($this->encryption);
}
$this->reqCert = (bool) $config->get('reqcert', $this->reqCert);
}
public function getHostname()
@ -470,59 +490,52 @@ class Connection
*/
protected function prepareNewConnection()
{
$use_tls = false;
$force_tls = false;
if ($use_tls) {
if ($this->encryption === static::STARTTLS || $this->encryption === static::LDAPS) {
$this->prepareTlsEnvironment();
}
$ds = ldap_connect($this->hostname, $this->port);
$hostname = $this->hostname;
if ($this->encryption === static::LDAPS) {
$hostname = 'ldaps://' . $hostname;
}
$ds = ldap_connect($hostname, $this->port);
try {
$this->capabilities = $this->discoverCapabilities($ds);
$this->discoverySuccess = true;
} catch (LdapException $e) {
// create empty default capabilities
Logger::debug($e);
Logger::warning('LADP discovery failed, assuming default LDAP settings.');
$this->capabilities = new Capability();
$this->capabilities = new Capability(); // create empty default capabilities
}
if ($use_tls) {
if ($this->capabilities->hasStartTLS()) {
if ($this->encryption === static::STARTTLS) {
$force_tls = false;
if ($this->capabilities->hasStartTls()) {
if (@ldap_start_tls($ds)) {
Logger::debug('LDAP STARTTLS succeeded');
} else {
Logger::debug('LDAP STARTTLS failed: %s', ldap_error($ds));
throw new LdapException(
'LDAP STARTTLS failed: %s',
ldap_error($ds)
);
Logger::error('LDAP STARTTLS failed: %s', ldap_error($ds));
throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds));
}
} elseif ($force_tls) {
throw new LdapException(
'TLS is required but not announced by %s',
$this->hostname
);
throw new LdapException('STARTTLS is required but not announced by %s', $this->hostname);
} else {
Logger::warning('LDAP TLS enabled but not announced');
Logger::warning('LDAP STARTTLS enabled but not announced');
}
}
// ldap_rename requires LDAPv3:
if ($this->capabilities->hasLdapV3()) {
if (! ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
throw new LdapException('LDAPv3 is required');
}
} else {
// TODO: remove this -> FORCING v3 for now
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
Logger::warning('No LDAPv3 support detected');
}
// Not setting this results in "Operations error" on AD when using the
// whole domain as search base:
// Not setting this results in "Operations error" on AD when using the whole domain as search base
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
// ldap_set_option($ds, LDAP_OPT_DEREF, LDAP_DEREF_NEVER);
return $ds;
@ -530,17 +543,16 @@ class Connection
protected function prepareTlsEnvironment()
{
$strict_tls = true;
// TODO: allow variable known CA location (system VS Icinga)
if (Platform::isWindows()) {
// putenv('LDAP...')
putenv('LDAPTLS_REQCERT=never');
} else {
if ($strict_tls) {
if ($this->reqCert) {
$ldap_conf = $this->getConfigDir('ldap_ca.conf');
} else {
$ldap_conf = $this->getConfigDir('ldap_nocert.conf');
}
putenv('LDAPRC=' . $ldap_conf);
putenv('LDAPRC=' . $ldap_conf); // TODO: Does not have any effect
if (getenv('LDAPRC') !== $ldap_conf) {
throw new LdapException('putenv failed');
}

View 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();
}
}

View File

@ -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) . '))';
@ -331,6 +348,11 @@ class Query
}
}
public function __toString()
{
return $this->create();
}
/**
* Descructor
*/

View File

@ -416,7 +416,7 @@ if ($col > $size - 1) return $res;
/**
* Disconnect in case we are connected to a Livestatus socket
*
* @return self
* @return $this
*/
public function disconnect()
{

View File

@ -413,22 +413,30 @@ class User
/**
* Whether the user has a given permission
*
* @param string $permission
* @param string $requiredPermission
*
* @return bool
*/
public function can($permission)
public function can($requiredPermission)
{
if (isset($this->permissions['*']) || isset($this->permissions[$permission])) {
if (isset($this->permissions['*']) || isset($this->permissions[$requiredPermission])) {
return true;
}
foreach ($this->permissions as $permitted) {
$wildcard = strpos($permitted, '*');
// If the permission to check contains a wildcard, grant the permission if any permit related to the permission
// matches
$any = strpos($requiredPermission, '*');
foreach ($this->permissions as $grantedPermission) {
if ($any !== false && strpos($grantedPermission, '*') === false) {
$wildcard = $any;
} else {
// If the permit contains a wildcard, grant the permission if it's related to the permit
$wildcard = strpos($grantedPermission, '*');
}
if ($wildcard !== false) {
if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) {
if (substr($requiredPermission, 0, $wildcard) === substr($grantedPermission, 0, $wildcard)) {
return true;
}
} elseif ($permission === $permitted) {
} elseif ($requiredPermission === $grantedPermission) {
return true;
}
}

View File

@ -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
)
);
}

View File

@ -3,7 +3,10 @@
namespace Icinga\Web;
use Zend_Paginator;
use Icinga\Web\Controller\ModuleActionController;
use Icinga\Web\Widget\SortBox;
use Icinga\Web\Widget\Limiter;
/**
* This is the controller all modules should inherit from
@ -12,4 +15,78 @@ use Icinga\Web\Controller\ModuleActionController;
*/
class Controller extends ModuleActionController
{
/**
* Create a SortBox widget at the `sortBox' view property
*
* In case the current view has been requested as compact this method does nothing.
*
* @param array $columns An array containing the sort columns, with the
* submit value as the key and the label as the value
*
* @return $this
*/
protected function setupSortControl(array $columns)
{
if (! $this->view->compact) {
$req = $this->getRequest();
$this->view->sortBox = SortBox::create(
'sortbox-' . $req->getActionName(),
$columns
)->applyRequest($req);
}
return $this;
}
/**
* Create a Limiter widget at the `limiter' view property
*
* In case the current view has been requested as compact this method does nothing.
*
* @return $this
*/
protected function setupLimitControl()
{
if (! $this->view->compact) {
$this->view->limiter = new Limiter();
}
return $this;
}
/**
* Set the view property `paginator' to the given Zend_Paginator
*
* In case the current view has been requested as compact this method does nothing.
*
* @param Zend_Paginator $paginator The Zend_Paginator for which to show a pagination control
*
* @return $this
*/
protected function setupPaginationControl(Zend_Paginator $paginator)
{
if (! $this->view->compact) {
$this->view->paginator = $paginator;
}
return $this;
}
/**
* Set the view property `filterEditor' to the given FilterEditor
*
* In case the current view has been requested as compact this method does nothing.
*
* @param Form $editor The FilterEditor
*
* @return $this
*/
protected function setupFilterControl($editor)
{
if (! $this->view->compact) {
$this->view->filterEditor = $editor;
}
return $this;
}
}

View File

@ -51,10 +51,15 @@ class ActionController extends Zend_Controller_Action
/**
* Authentication manager
*
* @type Manager|null
* @var Manager|null
*/
private $auth;
/**
* URL parameters
*
* @var UrlParams
*/
protected $params;
/**
@ -82,6 +87,7 @@ class ActionController extends Zend_Controller_Action
$this->_helper->layout()->isIframe = $request->getUrl()->shift('isIframe');
$this->_helper->layout()->moduleName = false;
$this->view->compact = $request->getParam('view') === 'compact';
if ($this->rerenderLayout = $request->getUrl()->shift('renderLayout')) {
$this->xhrLayout = 'body';
}
@ -293,34 +299,36 @@ class ActionController extends Zend_Controller_Action
}
/**
* Redirect to the login path
* Redirect to login
*
* @param Url $afterLogin The action to call when the login was successful. Defaults to '/index/welcome'
* XHR will always redirect to __SELF__ if an URL to redirect to after successful login is set. __SELF__ instructs
* JavaScript to redirect to the current window's URL if it's an auto-refresh request or to redirect to the URL
* which required login if it's not an auto-refreshing one.
*
* @throws \Exception
* XHR will respond with HTTP status code 403 Forbidden.
*
* @param Url|string $redirect URL to redirect to after successful login
*/
protected function redirectToLogin($afterLogin = null)
protected function redirectToLogin($redirect = null)
{
$redir = null;
if ($afterLogin !== null) {
if (! $afterLogin instanceof Url) {
$afterLogin = Url::fromPath($afterLogin);
$login = Url::fromPath('authentication/login');
if ($this->isXhr()) {
if ($redirect !== null) {
$login->setParam('redirect', '__SELF__');
}
if ($this->isXhr()) {
$redir = '__SELF__';
} else {
// TODO: Ignore /?
$redir = $afterLogin->getRelativeUrl();
$this->_response->setHttpResponseCode(403);
} elseif ($redirect !== null) {
if (! $redirect instanceof Url) {
$redirect = Url::fromPath($redirect);
}
if (($relativeUrl = $redirect->getRelativeUrl())) {
$login->setParam('redirect', $relativeUrl);
}
}
$url = Url::fromPath('authentication/login');
if ($redir) {
$url->setParam('redirect', $redir);
}
$this->rerenderLayout()->redirectNow($url);
$this->rerenderLayout()->redirectNow($login);
}
protected function rerenderLayout()

View File

@ -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()
*/

View File

@ -33,7 +33,7 @@ class DomNodeIterator implements RecursiveIterator
/**
* The node's children
*
* @type IteratorIterator
* @var IteratorIterator
*/
protected $children;

View File

@ -33,6 +33,11 @@ use Icinga\Web\Form\Element\CsrfCounterMeasure;
*/
class Form extends Zend_Form
{
/**
* The suffix to append to a field's hidden default field name
*/
const DEFAULT_SUFFIX = '_default';
/**
* Whether this form has been created
*
@ -145,7 +150,7 @@ class Form extends Zend_Form
/**
* Authentication manager
*
* @type Manager|null
* @var Manager|null
*/
private $auth;
@ -157,7 +162,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'))
);
@ -211,7 +216,7 @@ class Form extends Zend_Form
*
* @param string $label The label to use for the submit button
*
* @return self
* @return $this
*/
public function setSubmitLabel($label)
{
@ -234,7 +239,7 @@ class Form extends Zend_Form
*
* @param string|Url $url The url to redirect to
*
* @return self
* @return $this
*/
public function setRedirectUrl($url)
{
@ -263,7 +268,7 @@ class Form extends Zend_Form
*
* @param string $viewScript The view script to use
*
* @return self
* @return $this
*/
public function setViewScript($viewScript)
{
@ -286,7 +291,7 @@ class Form extends Zend_Form
*
* @param bool $disabled Set true in order to disable CSRF protection for this form, otherwise false
*
* @return self
* @return $this
*/
public function setTokenDisabled($disabled = true)
{
@ -314,7 +319,7 @@ class Form extends Zend_Form
*
* @param string $name The name to set
*
* @return self
* @return $this
*/
public function setTokenElementName($name)
{
@ -337,7 +342,7 @@ class Form extends Zend_Form
*
* @param bool $disabled Set true in order to disable identification for this form, otherwise false
*
* @return self
* @return $this
*/
public function setUidDisabled($disabled = true)
{
@ -365,7 +370,7 @@ class Form extends Zend_Form
*
* @param string $name The name to set
*
* @return self
* @return $this
*/
public function setUidElementName($name)
{
@ -388,7 +393,7 @@ class Form extends Zend_Form
*
* @param bool $state
*
* @return self
* @return $this
*/
public function setValidatePartial($state)
{
@ -525,7 +530,7 @@ class Form extends Zend_Form
*
* @param array $formData The data sent by the user
*
* @return self
* @return $this
*/
public function create(array $formData = array())
{
@ -535,7 +540,13 @@ class Form extends Zend_Form
->addCsrfCounterMeasure()
->addSubmitButton();
if ($this->getAction() === '') {
if ($this->getAttrib('action') === null) {
// Use Form::getAttrib() instead of Form::getAction() here because we want to explicitly check against
// null. Form::getAction() would return the empty string '' if the action is not set.
// For not setting the action attribute use Form::setAction(''). This is required for for the
// accessibility's enable/disable auto-refresh mechanic
// TODO(el): Re-evalute this necessity. JavaScript could use the container's URL if there's no action set.
// We MUST set an action as JS gets confused otherwise, if
// this form is being displayed in an additional column
$this->setAction(Url::fromRequest()->without(array_keys($this->getElements())));
@ -587,7 +598,7 @@ class Form extends Zend_Form
* Uses the label previously set with Form::setSubmitLabel(). Overwrite this
* method in order to add multiple submit buttons or one with a custom name.
*
* @return self
* @return $this
*/
public function addSubmitButton()
{
@ -622,7 +633,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();
@ -725,6 +736,20 @@ class Form extends Zend_Form
unset($el->autosubmit);
}
if ($el->getAttrib('preserveDefault')) {
$el->addDecorator(
array('preserveDefault' => 'HtmlTag'),
array(
'tag' => 'input',
'type' => 'hidden',
'name' => $name . static::DEFAULT_SUFFIX,
'value' => $el->getValue()
)
);
unset($el->preserveDefault);
}
return $this->ensureElementAccessibility($el);
}
@ -743,7 +768,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));
}
}
@ -767,7 +792,7 @@ class Form extends Zend_Form
/**
* Add a field with a unique and form specific ID
*
* @return self
* @return $this
*/
public function addFormIdentification()
{
@ -789,7 +814,7 @@ class Form extends Zend_Form
/**
* Add CSRF counter measure field to this form
*
* @return self
* @return $this
*/
public function addCsrfCounterMeasure()
{
@ -808,6 +833,17 @@ class Form extends Zend_Form
public function populate(array $defaults)
{
$this->create($defaults);
foreach ($this->getElements() as $name => $_) {
if (
array_key_exists($name, $defaults)
&& array_key_exists($name . static::DEFAULT_SUFFIX, $defaults)
&& $defaults[$name] === $defaults[$name . static::DEFAULT_SUFFIX]
) {
unset($defaults[$name]);
}
}
return parent::populate($defaults);
}
@ -894,10 +930,18 @@ class Form extends Zend_Form
{
$this->create($formData);
// Ensure that disabled elements are not overwritten (http://www.zendframework.com/issues/browse/ZF-6909)
foreach ($this->getElements() as $name => $element) {
if ($element->getAttrib('disabled')) {
$formData[$name] = $element->getValue();
if (array_key_exists($name, $formData)) {
if ($element->getAttrib('disabled')) {
// Ensure that disabled elements are not overwritten
// (http://www.zendframework.com/issues/browse/ZF-6909)
$formData[$name] = $element->getValue();
} elseif (
array_key_exists($name . static::DEFAULT_SUFFIX, $formData)
&& $formData[$name] === $formData[$name . static::DEFAULT_SUFFIX]
) {
unset($formData[$name]);
}
}
}
@ -942,7 +986,7 @@ class Form extends Zend_Form
* Overwrites Zend_Form::loadDefaultDecorators to avoid having
* the HtmlTag-Decorator added and to provide viewscript usage
*
* @return self
* @return $this
*/
public function loadDefaultDecorators()
{
@ -1071,10 +1115,11 @@ class Form extends Zend_Form
protected function getTranslationDomain()
{
$parts = explode('\\', get_called_class());
if ($parts[1] === 'Module') {
if (count($parts) > 1 && $parts[1] === 'Module') {
// Assume format Icinga\Module\ModuleName\Forms\...
return strtolower($parts[2]);
}
return 'icinga';
}

View File

@ -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);
}
}

View File

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

View File

@ -18,7 +18,7 @@ class Menu implements RecursiveIterator
/**
* The id of this menu
*
* @type string
* @var string
*/
protected $id;
@ -27,7 +27,7 @@ class Menu implements RecursiveIterator
*
* Used for sorting when priority is unset or equal to other items
*
* @type string
* @var string
*/
protected $title;
@ -36,45 +36,56 @@ class Menu implements RecursiveIterator
*
* Used for sorting
*
* @type int
* @var int
*/
protected $priority = 100;
/**
* The url of this menu
*
* @type string
* @var string
*/
protected $url;
/**
* The path to the icon of this menu
*
* @type string
* @var string
*/
protected $icon;
/**
* The sub menus of this menu
*
* @type array
* @var array
*/
protected $subMenus = array();
/**
* A custom item renderer used instead of the default rendering logic
*
* @type MenuItemRenderer
* @var MenuItemRenderer
*/
protected $itemRenderer = null;
/*
* Parent menu
*
* @type Menu
* @var Menu
*/
protected $parent;
/**
* Permission a user is required to have granted to display the menu item
*
* If a permission is set, authentication is of course required.
*
* Note that only one required permission can be set yet.
*
* @var string|null
*/
protected $permission;
/**
* Create a new menu
*
@ -106,13 +117,18 @@ class Menu implements RecursiveIterator
foreach ($props as $key => $value) {
$method = 'set' . implode('', array_map('ucfirst', explode('_', strtolower($key))));
if ($key === 'renderer') {
$class = '\Icinga\Web\Menu\\' . $value;
if (!class_exists($class)) {
throw new ConfigurationError(
sprintf('ItemRenderer with class "%s" does not exist', $class)
);
$value = '\\' . ltrim($value, '\\');
if (class_exists($value)) {
$value = new $value;
} else {
$class = '\Icinga\Web\Menu' . $value;
if (!class_exists($class)) {
throw new ConfigurationError(
sprintf('ItemRenderer with class "%s" does not exist', $class)
);
}
$value = new $class;
}
$value = new $class;
}
if (method_exists($this, $method)) {
$this->{$method}($value);
@ -218,15 +234,18 @@ class Menu implements RecursiveIterator
$section = $this->add(t('System'), array(
'icon' => 'wrench',
'priority' => 200
'priority' => 200,
'renderer' => 'ProblemMenuItemRenderer'
));
$section->add(t('Configuration'), array(
'url' => 'config',
'priority' => 300
'url' => 'config',
'permission' => 'config/application/*',
'priority' => 300
));
$section->add(t('Modules'), array(
'url' => 'config/modules',
'priority' => 400
'url' => 'config/modules',
'permission' => 'config/modules',
'priority' => 400
));
if (Logger::writesToFile()) {
@ -442,15 +461,47 @@ class Menu implements RecursiveIterator
}
/**
* Set required Permissions
* Get the permission a user is required to have granted to display the menu item
*
* @param $permission
* @return string|null
*/
public function getPermission()
{
return $this->permission;
}
/**
* Get parent menu
*
* @return \Icinga\Web\Menu
*/
public function getParent()
{
return $this->parent;
}
/**
* Get submenus
*
* @return array
*/
public function getSubMenus()
{
return $this->subMenus;
}
/**
* Set permission a user is required to have granted to display the menu item
*
* If a permission is set, authentication is of course required.
*
* @param string $permission
*
* @return $this
*/
public function requirePermission($permission)
public function setPermission($permission)
{
// Not implemented yet
$this->permission = (string) $permission;
return $this;
}
@ -614,7 +665,7 @@ class Menu implements RecursiveIterator
*
* @param array $menus The menus to load, as key-value array
*
* @return static
* @return $this
*/
protected function loadSubMenus(array $menus)
{

View File

@ -9,15 +9,9 @@ use Icinga\Web\Url;
/**
* A menu item with a link that surpasses the regular navigation link behavior
*/
class ForeignMenuItemRenderer implements MenuItemRenderer {
public function render(Menu $menu)
{
return sprintf(
'<a href="%s" target="_self">%s%s<span></span></a>',
$menu->getUrl() ?: '#',
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
htmlspecialchars($menu->getTitle())
);
}
class ForeignMenuItemRenderer extends MenuItemRenderer
{
protected $attributes = array(
'target' => '_self'
);
}

View File

@ -3,11 +3,108 @@
namespace Icinga\Web\Menu;
use Icinga\Application\Icinga;
use Icinga\Web\Menu;
use Icinga\Web\Url;
use Icinga\Web\View;
/**
* Renders the html content of a single menu item
* Default MenuItemRenderer class
*/
interface MenuItemRenderer {
public function render(Menu $menu);
class MenuItemRenderer
{
/**
* Contains <a> element specific attributes
*
* @var array
*/
protected $attributes = array();
/**
* View
*
* @var View|null
*/
protected $view;
/**
* Set the view
*
* @param View $view
*
* @return $this
*/
public function setView(View $view)
{
$this->view = $view;
return $this;
}
/**
* Get the view
*
* @return View
*/
public function getView()
{
if ($this->view === null) {
$this->view = Icinga::app()->getViewRenderer()->view;
}
return $this->view;
}
/**
* Renders the html content of a single menu item
*
* @param Menu $menu
*
* @return string
*/
public function render(Menu $menu)
{
return $this->createLink($menu);
}
/**
* Creates a menu item link element
*
* @param Menu $menu
*
* @return string
*/
public function createLink(Menu $menu)
{
if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) {
return sprintf(
'<a href="%s"%s><i aria-hidden="true" class="icon-%s"></i>%s</a>',
$menu->getUrl() ? : '#',
$this->getAttributes(),
$menu->getIcon(),
$this->getView()->escape($menu->getTitle())
);
}
return sprintf(
'<a href="%s"%s>%s%s<span></span></a>',
$menu->getUrl() ? : '#',
$this->getAttributes(),
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
$this->getView()->escape($menu->getTitle())
);
}
/**
* Returns <a> element specific attributes if present
*
* @return string
*/
protected function getAttributes()
{
$attributes = '';
$view = $this->getView();
foreach ($this->attributes as $attribute => $value) {
$attributes .= ' ' . $view->escape($attribute) . '="' . $view->escape($value) . '"';
}
return $attributes;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Icinga\Web\Menu;
use RecursiveFilterIterator;
use Icinga\Authentication\Manager;
use Icinga\Web\Menu;
class PermittedMenuItemFilter extends RecursiveFilterIterator
{
/**
* Accept menu items that are permitted to the user
*
* @return bool Whether the user has the required permission granted to display the menu item
*/
public function accept()
{
$item = $this->current();
/** @var Menu $item */
if (($permission = $item->getPermission()) !== null) {
$auth = Manager::getInstance();
if (! $auth->isAuthenticated()) {
// Don't accept menu item because user is not authenticated and the menu item requires a permission
return false;
}
if (! $auth->getUser()->can($permission)) {
return false;
}
}
// Accept menu item if it does not require a permission
return true;
}
}

View File

@ -3,10 +3,62 @@
namespace Icinga\Web\Menu;
class ProblemMenuItemRenderer extends MonitoringMenuItemRenderer
use Icinga\Web\Menu;
class ProblemMenuItemRenderer extends MenuItemRenderer
{
protected $columns = array(
'hosts_down_unhandled',
'services_critical_unhandled'
);
/**
* Set of summarized problems from submenus
*
* @var array
*/
protected $summary = array();
/**
* Renders the html content of a single menu item and summarizes submenu problems
*
* @param Menu $menu
*
* @return string
*/
public function render(Menu $menu)
{
if ($menu->getParent() !== null && $menu->hasSubMenus()) {
/** @var $submenu Menu */
foreach ($menu->getSubMenus() as $submenu) {
$renderer = $submenu->getRenderer();
if (method_exists($renderer, 'getSummary')) {
if ($renderer->getSummary() !== null) {
$this->summary[] = $renderer->getSummary();
}
}
}
}
return $this->getBadge() . $this->createLink($menu);
}
/**
* Get the problem badge
*
* @return string
*/
protected function getBadge()
{
if (count($this->summary) > 0) {
$problems = 0;
$titles = array();
foreach ($this->summary as $summary) {
$problems += $summary['problems'];
$titles[] = $summary['title'];
}
return sprintf(
'<div title="%s" class="badge-container"><span class="badge badge-critical">%s</span></div>',
implode(', ', $titles),
$problems
);
}
return '';
}
}

View File

@ -4,8 +4,10 @@
namespace Icinga\Web;
use Exception;
use Icinga\Web\Menu\MenuItemRenderer;
use RecursiveIteratorIterator;
use Icinga\Application\Logger;
use Icinga\Web\Menu\PermittedMenuItemFilter;
/**
* A renderer to draw a menu with its sub-menus using an unordered html list
@ -31,6 +33,11 @@ class MenuRenderer extends RecursiveIteratorIterator
*/
protected $useCustomRenderer = false;
/**
* @var MenuItemRenderer
*/
protected $defaultRenderer;
/**
* Create a new MenuRenderer
*
@ -44,7 +51,8 @@ class MenuRenderer extends RecursiveIteratorIterator
} else {
$this->url = Url::fromPath($url);
}
parent::__construct($menu, RecursiveIteratorIterator::CHILD_FIRST);
$this->defaultRenderer = new MenuItemRenderer();
parent::__construct(new PermittedMenuItemFilter($menu), RecursiveIteratorIterator::CHILD_FIRST);
}
/**
@ -113,22 +121,8 @@ class MenuRenderer extends RecursiveIteratorIterator
Logger::error('Could not invoke custom renderer. Exception: '. $e->getMessage());
}
}
if ($child->getIcon() && strpos($child->getIcon(), '.') === false) {
return sprintf(
'<a href="%s"><i aria-hidden="true" class="icon-%s"></i>%s</a>',
$child->getUrl() ?: '#',
$child->getIcon(),
htmlspecialchars($child->getTitle())
);
}
return sprintf(
'<a href="%s">%s%s</a>',
$child->getUrl() ?: '#',
$child->getIcon()
? '<img aria-hidden="true" src="' . Url::fromPath($child->getIcon()) . '" class="icon" /> '
: '',
htmlspecialchars($child->getTitle())
);
return $this->defaultRenderer->render($child);
}
/**

View File

@ -18,8 +18,13 @@ use Icinga\Web\Session;
class Notification
{
protected static $instance;
protected $isCli = false;
protected $session;
protected $messages = array();
public static function info($msg)
{
self::getInstance()->addMessage($msg, 'info');
@ -74,8 +79,7 @@ class Notification
return;
}
$messages = & Session::getSession()->getByRef('messages');
$messages[] = (object) array(
$this->messages[] = (object) array(
'type' => $type,
'message' => $message,
);
@ -83,30 +87,30 @@ class Notification
public function hasMessages()
{
$session = Session::getSession();
return false === empty($session->messages);
return false === empty($this->messages);
}
public function getMessages()
{
$session = Session::getSession();
$messages = $session->messages;
if (false === empty($messages)) {
$session->messages = array();
}
$messages = $this->messages;
$this->messages = array();
return $messages;
}
final private function __construct()
{
$session = Session::getSession();
if (!is_array($session->get('messages'))) {
$session->messages = array();
if (Platform::isCli()) {
$this->isCli = true;
return;
}
if (Platform::isCli()) {
$this->is_cli = true;
$this->session = Session::getSession();
$stored = $this->session->get('messages');
if (is_array($stored)) {
$this->messages = $stored;
$this->session->set('messages', array());
$this->session->write();
}
}
@ -117,4 +121,17 @@ class Notification
}
return self::$instance;
}
final public function __destruct()
{
if ($this->isCli) {
return;
}
if ($this->session->get('messages') !== $this->messages) {
$this->session->set('messages', $this->messages);
}
$this->session->write();
unset($this->session);
}
}

Some files were not shown because too many files have changed in this diff Show More