Merge branch 'master' into feature/show-icinga-web-2-s-version-in-the-frontend-9247
This commit is contained in:
commit
142851ede7
|
@ -14,11 +14,12 @@
|
|||
#
|
||||
class openldap {
|
||||
|
||||
package { ['openldap-servers', 'openldap-clients']:
|
||||
package { [ 'openldap-servers', 'openldap-clients', ]:
|
||||
ensure => latest,
|
||||
}
|
||||
|
||||
service { 'slapd':
|
||||
enable => true,
|
||||
ensure => running,
|
||||
require => Package['openldap-servers'],
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Class: pgsql
|
||||
#
|
||||
# This class installs the postgresql server and client software.
|
||||
# Further it configures pg_hba.conf to trus the local icinga user.
|
||||
# This class installs the PostgreSQL server and client software.
|
||||
# Further it configures pg_hba.conf to trust the local icinga user.
|
||||
#
|
||||
# Parameters:
|
||||
#
|
||||
|
@ -17,26 +17,25 @@ class pgsql {
|
|||
|
||||
Exec { path => '/sbin:/bin:/usr/bin' }
|
||||
|
||||
package { [
|
||||
'postgresql', 'postgresql-server'
|
||||
]:
|
||||
ensure => latest,
|
||||
package { [ 'postgresql', 'postgresql-server', ]:
|
||||
ensure => latest,
|
||||
}
|
||||
|
||||
exec { 'initdb':
|
||||
creates => '/var/lib/pgsql/data/pg_xlog',
|
||||
command => 'service postgresql initdb',
|
||||
require => Package['postgresql-server']
|
||||
creates => '/var/lib/pgsql/data/pg_xlog',
|
||||
require => Package['postgresql-server'],
|
||||
}
|
||||
|
||||
service { 'postgresql':
|
||||
enable => true,
|
||||
ensure => running,
|
||||
require => [Package['postgresql-server'], Exec['initdb']]
|
||||
require => [ Package['postgresql-server'], Exec['initdb'], ]
|
||||
}
|
||||
|
||||
file { '/var/lib/pgsql/data/pg_hba.conf':
|
||||
content => template('pgsql/pg_hba.conf.erb'),
|
||||
require => [Package['postgresql-server'], Exec['initdb']],
|
||||
notify => Service['postgresql']
|
||||
require => [ Package['postgresql-server'], Exec['initdb'], ],
|
||||
notify => Service['postgresql'],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@ class ErrorController extends ActionController
|
|||
Logger::error($exception);
|
||||
Logger::error('Stacktrace: %s', $exception->getTraceAsString());
|
||||
|
||||
if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) {
|
||||
$this->innerLayout = 'error';
|
||||
}
|
||||
|
||||
switch ($error->type) {
|
||||
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
|
||||
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
|
||||
|
@ -45,11 +49,13 @@ class ErrorController extends ActionController
|
|||
$path = array_shift($path);
|
||||
$this->getResponse()->setHttpResponseCode(404);
|
||||
$this->view->message = $this->translate('Page not found.');
|
||||
if ($this->Auth()->isAuthenticated() && $modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
|
||||
$this->view->message .= ' ' . sprintf(
|
||||
$this->translate('Enabling the "%s" module might help!'),
|
||||
$path
|
||||
);
|
||||
if ($isAuthenticated) {
|
||||
if ($modules->hasInstalled($path) && ! $modules->hasEnabled($path)) {
|
||||
$this->view->message .= ' ' . sprintf(
|
||||
$this->translate('Enabling the "%s" module might help!'),
|
||||
$path
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -93,5 +99,6 @@ class ErrorController extends ActionController
|
|||
}
|
||||
|
||||
$this->view->request = $error->request;
|
||||
$this->view->hideControls = ! $isAuthenticated;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,8 @@
|
|||
|
||||
namespace Icinga\Controllers;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Web\Hook;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\MenuRenderer;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* Create complex layout parts
|
||||
|
@ -21,9 +18,6 @@ class LayoutController extends ActionController
|
|||
{
|
||||
$this->setAutorefreshInterval(15);
|
||||
$this->_helper->layout()->disableLayout();
|
||||
|
||||
$url = Url::fromRequest();
|
||||
$menu = new MenuRenderer(Menu::load(), $url->getRelativeUrl());
|
||||
$this->view->menuRenderer = $menu->useCustomRenderer();
|
||||
$this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use Icinga\Protocol\File\FileReader;
|
|||
use Icinga\Web\Controller;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
use Icinga\Web\Widget\Tabextension\OutputFormat;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,7 @@ class ListController extends Controller
|
|||
'list/'
|
||||
. str_replace(' ', '', $action)
|
||||
)
|
||||
))->extend(new OutputFormat())->extend(new DashboardAction())->activate($action);
|
||||
))->extend(new OutputFormat())->extend(new DashboardAction())->extend(new MenuAction())->activate($action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Controllers;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Data\DataArray\ArrayDatasource;
|
||||
use Icinga\Forms\ConfirmRemovalForm;
|
||||
use Icinga\Forms\Navigation\NavigationConfigForm;
|
||||
use Icinga\Web\Controller;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* Navigation configuration
|
||||
*/
|
||||
class NavigationController extends Controller
|
||||
{
|
||||
/**
|
||||
* The global navigation item type configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $itemTypeConfig;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->itemTypeConfig = Navigation::getItemTypeConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the label for the given navigation item type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string $type if no label can be found
|
||||
*/
|
||||
protected function getItemLabel($type)
|
||||
{
|
||||
return isset($this->itemTypeConfig[$type]['label']) ? $this->itemTypeConfig[$type]['label'] : $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of available navigation item types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function listItemTypes()
|
||||
{
|
||||
$types = array();
|
||||
foreach ($this->itemTypeConfig as $type => $options) {
|
||||
$types[$type] = isset($options['label']) ? $options['label'] : $type;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all shared navigation item configurations
|
||||
*
|
||||
* @param string $owner A username if only items shared by a specific user are desired
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fetchSharedNavigationItemConfigs($owner = null)
|
||||
{
|
||||
$configs = array();
|
||||
foreach ($this->itemTypeConfig as $type => $_) {
|
||||
$config = Config::navigation($type);
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
$query = $config->select();
|
||||
if ($owner !== null) {
|
||||
$query->where('owner', $owner);
|
||||
}
|
||||
|
||||
foreach ($query as $itemConfig) {
|
||||
$configs[] = $itemConfig;
|
||||
}
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all user navigation item configurations
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function fetchUserNavigationItemConfigs($username)
|
||||
{
|
||||
$configs = array();
|
||||
foreach ($this->itemTypeConfig as $type => $_) {
|
||||
$config = Config::navigation($type, $username);
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
foreach ($config->select() as $itemConfig) {
|
||||
$configs[] = $itemConfig;
|
||||
}
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the current user a list of his/her navigation items
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$user = $this->Auth()->getUser();
|
||||
$ds = new ArrayDatasource(array_merge(
|
||||
$this->fetchSharedNavigationItemConfigs($user->getUsername()),
|
||||
$this->fetchUserNavigationItemConfigs($user->getUsername())
|
||||
));
|
||||
$query = $ds->select();
|
||||
|
||||
$this->view->types = $this->listItemTypes();
|
||||
$this->view->items = $query;
|
||||
|
||||
$this->getTabs()->add(
|
||||
'navigation',
|
||||
array(
|
||||
'title' => $this->translate('List and configure your own navigation items'),
|
||||
'label' => $this->translate('Navigation'),
|
||||
'url' => 'navigation'
|
||||
)
|
||||
)->activate('navigation');
|
||||
$this->setupSortControl(
|
||||
array(
|
||||
'type' => $this->translate('Type'),
|
||||
'owner' => $this->translate('Shared'),
|
||||
'name' => $this->translate('Navigation')
|
||||
),
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all shared navigation items
|
||||
*/
|
||||
public function sharedAction()
|
||||
{
|
||||
$this->assertPermission('config/application/navigation');
|
||||
$ds = new ArrayDatasource($this->fetchSharedNavigationItemConfigs());
|
||||
$query = $ds->select();
|
||||
|
||||
$removeForm = new Form();
|
||||
$removeForm->setUidDisabled();
|
||||
$removeForm->addElement('hidden', 'name', array(
|
||||
'decorators' => array('ViewHelper')
|
||||
));
|
||||
$removeForm->addElement('hidden', 'redirect', array(
|
||||
'value' => Url::fromPath('navigation/shared'),
|
||||
'decorators' => array('ViewHelper')
|
||||
));
|
||||
$removeForm->addElement('button', 'btn_submit', array(
|
||||
'escape' => false,
|
||||
'type' => 'submit',
|
||||
'class' => 'link-like spinner',
|
||||
'value' => 'btn_submit',
|
||||
'decorators' => array('ViewHelper'),
|
||||
'label' => $this->view->icon('trash'),
|
||||
'title' => $this->translate('Unshare this navigation item')
|
||||
));
|
||||
|
||||
$this->view->removeForm = $removeForm;
|
||||
$this->view->types = $this->listItemTypes();
|
||||
$this->view->items = $query;
|
||||
|
||||
$this->getTabs()->add(
|
||||
'navigation/shared',
|
||||
array(
|
||||
'title' => $this->translate('List and configure shared navigation items'),
|
||||
'label' => $this->translate('Shared Navigation'),
|
||||
'url' => 'navigation/shared'
|
||||
)
|
||||
)->activate('navigation/shared');
|
||||
$this->setupSortControl(
|
||||
array(
|
||||
'type' => $this->translate('Type'),
|
||||
'owner' => $this->translate('Owner'),
|
||||
'name' => $this->translate('Shared Navigation')
|
||||
),
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a navigation item
|
||||
*/
|
||||
public function addAction()
|
||||
{
|
||||
$form = new NavigationConfigForm();
|
||||
$form->setRedirectUrl('navigation');
|
||||
$form->setUser($this->Auth()->getUser());
|
||||
$form->setItemTypes($this->listItemTypes());
|
||||
$form->setTitle($this->translate('Create New Navigation Item'));
|
||||
$form->addDescription($this->translate('Create a new navigation item, such as a menu entry or dashlet.'));
|
||||
|
||||
// TODO: Fetch all "safe" parameters from the url and populate them
|
||||
$form->populate(array('url' => rawurldecode($this->params->get('url', ''))));
|
||||
|
||||
$form->setOnSuccess(function (NavigationConfigForm $form) {
|
||||
$data = array_filter($form->getValues());
|
||||
|
||||
try {
|
||||
$form->add($data);
|
||||
} catch (Exception $e) {
|
||||
$form->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($form->save()) {
|
||||
if ($data['type'] === 'menu-item') {
|
||||
$form->getResponse()->setRerenderLayout();
|
||||
}
|
||||
|
||||
Notification::success(t('Navigation item successfully created'));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
$form->handleRequest();
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->render('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a navigation item
|
||||
*/
|
||||
public function editAction()
|
||||
{
|
||||
$itemName = $this->params->getRequired('name');
|
||||
$itemType = $this->params->getRequired('type');
|
||||
$referrer = $this->params->get('referrer', 'index');
|
||||
|
||||
$user = $this->Auth()->getUser();
|
||||
if ($user->can('config/application/navigation')) {
|
||||
$itemOwner = $this->params->get('owner', $user->getUsername());
|
||||
} else {
|
||||
$itemOwner = $user->getUsername();
|
||||
}
|
||||
|
||||
$form = new NavigationConfigForm();
|
||||
$form->setUser($user);
|
||||
$form->setShareConfig(Config::navigation($itemType));
|
||||
$form->setUserConfig(Config::navigation($itemType, $itemOwner));
|
||||
$form->setRedirectUrl($referrer === 'shared' ? 'navigation/shared' : 'navigation');
|
||||
$form->setTitle(sprintf($this->translate('Edit %s %s'), $this->getItemLabel($itemType), $itemName));
|
||||
$form->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) {
|
||||
$data = array_map(
|
||||
function ($v) {
|
||||
return $v !== '' ? $v : null;
|
||||
},
|
||||
$form->getValues()
|
||||
);
|
||||
|
||||
try {
|
||||
$form->edit($itemName, $data);
|
||||
} catch (NotFoundError $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
$form->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($form->save()) {
|
||||
if (isset($data['type']) && $data['type'] === 'menu-item') {
|
||||
$form->getResponse()->setRerenderLayout();
|
||||
}
|
||||
|
||||
Notification::success(sprintf(t('Navigation item "%s" successfully updated'), $itemName));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
$form->load($itemName);
|
||||
$form->handleRequest();
|
||||
} catch (NotFoundError $_) {
|
||||
$this->httpNotFound(sprintf($this->translate('Navigation item "%s" not found'), $itemName));
|
||||
}
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->render('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a navigation item
|
||||
*/
|
||||
public function removeAction()
|
||||
{
|
||||
$itemName = $this->params->getRequired('name');
|
||||
$itemType = $this->params->getRequired('type');
|
||||
$user = $this->Auth()->getUser();
|
||||
|
||||
$navigationConfigForm = new NavigationConfigForm();
|
||||
$navigationConfigForm->setUser($user);
|
||||
$navigationConfigForm->setShareConfig(Config::navigation($itemType));
|
||||
$navigationConfigForm->setUserConfig(Config::navigation($itemType, $user->getUsername()));
|
||||
|
||||
$form = new ConfirmRemovalForm();
|
||||
$form->setRedirectUrl('navigation');
|
||||
$form->setTitle(sprintf($this->translate('Remove %s %s'), $this->getItemLabel($itemType), $itemName));
|
||||
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($itemName, $navigationConfigForm) {
|
||||
try {
|
||||
$itemConfig = $navigationConfigForm->delete($itemName);
|
||||
} catch (NotFoundError $e) {
|
||||
Notification::success(sprintf(t('Navigation Item "%s" not found. No action required'), $itemName));
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$form->error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($navigationConfigForm->save()) {
|
||||
if ($itemConfig->type === 'menu-item') {
|
||||
$form->getResponse()->setRerenderLayout();
|
||||
}
|
||||
|
||||
Notification::success(sprintf(t('Navigation Item "%s" successfully removed'), $itemName));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
$form->handleRequest();
|
||||
|
||||
$this->view->form = $form;
|
||||
$this->render('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unshare a navigation item
|
||||
*/
|
||||
public function unshareAction()
|
||||
{
|
||||
$this->assertPermission('config/application/navigation');
|
||||
$this->assertHttpMethod('POST');
|
||||
|
||||
// TODO: I'd like these being form fields
|
||||
$itemType = $this->params->getRequired('type');
|
||||
$itemOwner = $this->params->getRequired('owner');
|
||||
|
||||
$navigationConfigForm = new NavigationConfigForm();
|
||||
$navigationConfigForm->setUser($this->Auth()->getUser());
|
||||
$navigationConfigForm->setShareConfig(Config::navigation($itemType));
|
||||
$navigationConfigForm->setUserConfig(Config::navigation($itemType, $itemOwner));
|
||||
|
||||
$form = new Form(array(
|
||||
'onSuccess' => function ($form) use ($navigationConfigForm) {
|
||||
$itemName = $form->getValue('name');
|
||||
|
||||
try {
|
||||
$newConfig = $navigationConfigForm->unshare($itemName);
|
||||
if ($navigationConfigForm->save()) {
|
||||
if ($newConfig->getSection($itemName)->type === 'menu-item') {
|
||||
$form->getResponse()->setRerenderLayout();
|
||||
}
|
||||
|
||||
Notification::success(sprintf(
|
||||
t('Navigation item "%s" has been unshared'),
|
||||
$form->getValue('name')
|
||||
));
|
||||
} else {
|
||||
// TODO: It failed obviously to write one of the configs, so we're leaving the user in
|
||||
// a inconsistent state. Luckily, it's nothing lost but possibly duplicated...
|
||||
Notification::error(sprintf(
|
||||
t('Failed to unshare navigation item "%s"'),
|
||||
$form->getValue('name')
|
||||
));
|
||||
}
|
||||
} catch (NotFoundError $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
Notification::error($e->getMessage());
|
||||
}
|
||||
|
||||
$redirect = $form->getValue('redirect');
|
||||
if (! empty($redirect)) {
|
||||
$form->setRedirectUrl(htmlspecialchars_decode($redirect));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
));
|
||||
$form->setUidDisabled();
|
||||
$form->setSubmitLabel('btn_submit'); // Required to ensure that isSubmitted() is called
|
||||
$form->addElement('hidden', 'name', array('required' => true));
|
||||
$form->addElement('hidden', 'redirect');
|
||||
|
||||
try {
|
||||
$form->handleRequest();
|
||||
} catch (NotFoundError $_) {
|
||||
$this->httpNotFound(sprintf($this->translate('Navigation item "%s" not found'), $form->getValue('name')));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ class PreferenceController extends BasePreferenceController
|
|||
array(
|
||||
'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'),
|
||||
'label' => t('Preferences'),
|
||||
'url' => Url::fromPath('/preference')
|
||||
'url' => Url::fromPath('preference')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
namespace Icinga\Forms\Config\Resource;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying database resources
|
||||
|
@ -43,12 +43,30 @@ class DbResourceForm extends Form
|
|||
$dbChoices['oci'] = 'Oracle (OCI8)';
|
||||
}
|
||||
$offerPostgres = false;
|
||||
$offerMysql = false;
|
||||
if (isset($formData['db'])) {
|
||||
if ($formData['db'] === 'pgsql') {
|
||||
$offerPostgres = true;
|
||||
} elseif ($formData['db'] === 'mysql') {
|
||||
$offerMysql = true;
|
||||
}
|
||||
} elseif (key($dbChoices) === 'pgsql') {
|
||||
$offerPostgres = true;
|
||||
} else {
|
||||
$dbChoice = key($dbChoices);
|
||||
if ($dbChoice === 'pgsql') {
|
||||
$offerPostgres = true;
|
||||
} elseif ($dbChoices === 'mysql') {
|
||||
$offerMysql = true;
|
||||
}
|
||||
}
|
||||
$socketInfo = '';
|
||||
if ($offerPostgres) {
|
||||
$socketInfo = $this->translate(
|
||||
'For using unix domain sockets, specify the path to the unix domain socket directory'
|
||||
);
|
||||
} elseif ($offerMysql) {
|
||||
$socketInfo = $this->translate(
|
||||
'For using unix domain sockets, specify localhost'
|
||||
);
|
||||
}
|
||||
$this->addElement(
|
||||
'text',
|
||||
|
@ -76,7 +94,8 @@ class DbResourceForm extends Form
|
|||
array (
|
||||
'required' => true,
|
||||
'label' => $this->translate('Host'),
|
||||
'description' => $this->translate('The hostname of the database'),
|
||||
'description' => $this->translate('The hostname of the database')
|
||||
. ($socketInfo ? '. ' . $socketInfo : ''),
|
||||
'value' => 'localhost'
|
||||
)
|
||||
);
|
||||
|
@ -119,6 +138,14 @@ class DbResourceForm extends Form
|
|||
'description' => $this->translate('The password to use for authentication')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'charset',
|
||||
array (
|
||||
'description' => $this->translate('The character set for the database'),
|
||||
'label' => $this->translate('Character Set')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'persistent',
|
||||
|
|
|
@ -43,7 +43,7 @@ class ConfigForm extends Form
|
|||
public function save()
|
||||
{
|
||||
try {
|
||||
$this->config->saveIni();
|
||||
$this->writeConfig($this->config);
|
||||
} catch (Exception $e) {
|
||||
$this->addDecorator('ViewScript', array(
|
||||
'viewModule' => 'default',
|
||||
|
@ -58,4 +58,14 @@ class ConfigForm extends Form
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the configuration to disk
|
||||
*
|
||||
* @param Config $config
|
||||
*/
|
||||
protected function writeConfig(Config $config)
|
||||
{
|
||||
$config->saveIni();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Navigation;
|
||||
|
||||
class DashletForm extends NavigationItemForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'pane',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Pane'),
|
||||
'description' => $this->translate('The name of the dashboard pane in which to display this dashlet')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'url',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Url'),
|
||||
'description' => $this->translate(
|
||||
'The url to load in the dashlet. For external urls, make sure to prepend'
|
||||
. ' an appropriate protocol identifier (e.g. http://example.tld)'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Navigation;
|
||||
|
||||
class MenuItemForm extends NavigationItemForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $requiresParentSelection = true;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
parent::createElements($formData);
|
||||
|
||||
// Remove _self and _next as for menu entries only _main is valid
|
||||
$this->getElement('target')->removeMultiOption('_self');
|
||||
$this->getElement('target')->removeMultiOption('_next');
|
||||
|
||||
$parentElement = $this->getParent()->getElement('parent');
|
||||
if ($parentElement !== null) {
|
||||
$parentElement->setDescription($this->translate(
|
||||
'The parent menu to assign this menu entry to. Select "None" to make this a main menu entry'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,856 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Navigation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Forms\ConfigForm;
|
||||
use Icinga\User;
|
||||
use Icinga\Util\String;
|
||||
use Icinga\Web\Form;
|
||||
|
||||
/**
|
||||
* Form for managing navigation items
|
||||
*/
|
||||
class NavigationConfigForm extends ConfigForm
|
||||
{
|
||||
/**
|
||||
* The class namespace where to locate navigation type forms
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FORM_NS = 'Forms\\Navigation';
|
||||
|
||||
/**
|
||||
* The secondary configuration to write
|
||||
*
|
||||
* This is always the reduced configuration and is only written to
|
||||
* disk once the main configuration has been successfully written.
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
protected $secondaryConfig;
|
||||
|
||||
/**
|
||||
* The navigation item to load when displaying the form for the first time
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $itemToLoad;
|
||||
|
||||
/**
|
||||
* The user for whom to manage navigation items
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* The user's navigation configuration
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
protected $userConfig;
|
||||
|
||||
/**
|
||||
* The shared navigation configuration
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
protected $shareConfig;
|
||||
|
||||
/**
|
||||
* The available navigation item types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $itemTypes;
|
||||
|
||||
/**
|
||||
* Initialize this form
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setName('form_config_navigation');
|
||||
$this->setSubmitLabel($this->translate('Save Changes'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user for whom to manage navigation items
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user for whom to manage navigation items
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's navigation configuration
|
||||
*
|
||||
* @param Config $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUserConfig(Config $config)
|
||||
{
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
$this->userConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user's navigation configuration
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getUserConfig($type = null)
|
||||
{
|
||||
if ($this->userConfig === null) {
|
||||
if ($type === null) {
|
||||
throw new ProgrammingError('You need to pass a type if no user configuration is set');
|
||||
}
|
||||
|
||||
$this->setUserConfig(Config::navigation($type, $this->getUser()->getUsername()));
|
||||
}
|
||||
|
||||
return $this->userConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shared navigation configuration
|
||||
*
|
||||
* @param Config $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setShareConfig(Config $config)
|
||||
{
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
$this->shareConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the shared navigation configuration
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getShareConfig($type = null)
|
||||
{
|
||||
if ($this->shareConfig === null) {
|
||||
if ($type === null) {
|
||||
throw new ProgrammingError('You need to pass a type if no share configuration is set');
|
||||
}
|
||||
|
||||
$this->setShareConfig(Config::navigation($type));
|
||||
}
|
||||
|
||||
return $this->shareConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the available navigation item types
|
||||
*
|
||||
* @param array $itemTypes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setItemTypes(array $itemTypes)
|
||||
{
|
||||
$this->itemTypes = $itemTypes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the available navigation item types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getItemTypes()
|
||||
{
|
||||
return $this->itemTypes ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of available parent items for the given type of navigation item
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $owner
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listAvailableParents($type, $owner = null)
|
||||
{
|
||||
$children = $this->itemToLoad ? $this->getFlattenedChildren($this->itemToLoad) : array();
|
||||
|
||||
$names = array();
|
||||
foreach ($this->getShareConfig($type) as $sectionName => $sectionConfig) {
|
||||
if (
|
||||
$sectionName !== $this->itemToLoad
|
||||
&& $sectionConfig->owner === ($owner ?: $this->getUser()->getUsername())
|
||||
&& !in_array($sectionName, $children, true)
|
||||
) {
|
||||
$names[] = $sectionName;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getUserConfig($type) as $sectionName => $sectionConfig) {
|
||||
if (
|
||||
$sectionName !== $this->itemToLoad
|
||||
&& !in_array($sectionName, $children, true)
|
||||
) {
|
||||
$names[] = $sectionName;
|
||||
}
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively return all children of the given navigation item
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFlattenedChildren($name)
|
||||
{
|
||||
$config = $this->getConfigForItem($name);
|
||||
if ($config === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$children = array();
|
||||
foreach ($config->toArray() as $sectionName => $sectionConfig) {
|
||||
if (isset($sectionConfig['parent']) && $sectionConfig['parent'] === $name) {
|
||||
$children[] = $sectionName;
|
||||
$children = array_merge($children, $this->getFlattenedChildren($sectionName));
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form with the given navigation item's config
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws NotFoundError In case no navigation item with the given name is found
|
||||
*/
|
||||
public function load($name)
|
||||
{
|
||||
if ($this->getConfigForItem($name) === null) {
|
||||
throw new NotFoundError('No navigation item called "%s" found', $name);
|
||||
}
|
||||
|
||||
$this->itemToLoad = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new navigation item
|
||||
*
|
||||
* The navigation item to add is identified by the array-key `name'.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException In case $data does not contain a navigation item name or type
|
||||
* @throws IcingaException In case a navigation item with the same name already exists
|
||||
*/
|
||||
public function add(array $data)
|
||||
{
|
||||
if (! isset($data['name'])) {
|
||||
throw new InvalidArgumentException('Key \'name\' missing');
|
||||
} elseif (! isset($data['type'])) {
|
||||
throw new InvalidArgumentException('Key \'type\' missing');
|
||||
}
|
||||
|
||||
$shared = false;
|
||||
$config = $this->getUserConfig($data['type']);
|
||||
if ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) {
|
||||
if ($this->getUser()->can('application/share/navigation')) {
|
||||
$data['owner'] = $this->getUser()->getUsername();
|
||||
$config = $this->getShareConfig($data['type']);
|
||||
$shared = true;
|
||||
} else {
|
||||
unset($data['users']);
|
||||
unset($data['groups']);
|
||||
}
|
||||
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'], $data['type'])) {
|
||||
$data['owner'] = $this->getUser()->getUsername();
|
||||
$config = $this->getShareConfig($data['type']);
|
||||
$shared = true;
|
||||
}
|
||||
|
||||
$itemName = $data['name'];
|
||||
$exists = $config->hasSection($itemName);
|
||||
if (! $exists) {
|
||||
if ($shared) {
|
||||
$exists = $this->getUserConfig($data['type'])->hasSection($itemName);
|
||||
} else {
|
||||
$exists = (bool) $this->getShareConfig($data['type'])
|
||||
->select()
|
||||
->where('name', $itemName)
|
||||
->where('owner', $this->getUser()->getUsername())
|
||||
->count();
|
||||
}
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
throw new IcingaException(
|
||||
$this->translate('A navigation item with the name "%s" does already exist'),
|
||||
$itemName
|
||||
);
|
||||
}
|
||||
|
||||
unset($data['name']);
|
||||
$config->setSection($itemName, $data);
|
||||
$this->setIniConfig($config);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a navigation item
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $data
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws NotFoundError In case no navigation item with the given name is found
|
||||
* @throws IcingaException In case a navigation item with the same name already exists
|
||||
*/
|
||||
public function edit($name, array $data)
|
||||
{
|
||||
$config = $this->getConfigForItem($name);
|
||||
if ($config === null) {
|
||||
throw new NotFoundError('No navigation item called "%s" found', $name);
|
||||
} else {
|
||||
$itemConfig = $config->getSection($name);
|
||||
}
|
||||
|
||||
$shared = false;
|
||||
if ($this->hasBeenShared($name)) {
|
||||
if (isset($data['parent']) && $data['parent']
|
||||
? !$this->hasBeenShared($data['parent'])
|
||||
: ((! isset($data['users']) || !$data['users']) && (! isset($data['groups']) || !$data['groups']))
|
||||
) {
|
||||
// It is shared but shouldn't anymore
|
||||
$config = $this->unshare($name, isset($data['parent']) ? $data['parent'] : null);
|
||||
}
|
||||
} elseif ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) {
|
||||
if ($this->getUser()->can('application/share/navigation')) {
|
||||
// It is not shared yet but should be
|
||||
$this->secondaryConfig = $config;
|
||||
$config = $this->getShareConfig();
|
||||
$data['owner'] = $this->getUser()->getUsername();
|
||||
$shared = true;
|
||||
} else {
|
||||
unset($data['users']);
|
||||
unset($data['groups']);
|
||||
}
|
||||
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'])) {
|
||||
// Its parent is shared so should it itself
|
||||
$this->secondaryConfig = $config;
|
||||
$config = $this->getShareConfig();
|
||||
$data['owner'] = $this->getUser()->getUsername();
|
||||
$shared = true;
|
||||
}
|
||||
|
||||
$oldName = null;
|
||||
if (isset($data['name'])) {
|
||||
if ($data['name'] !== $name) {
|
||||
$oldName = $name;
|
||||
$name = $data['name'];
|
||||
|
||||
$exists = $config->hasSection($name);
|
||||
if (! $exists) {
|
||||
$ownerName = $itemConfig->owner ?: $this->getUser()->getUsername();
|
||||
if ($shared || $this->hasBeenShared($oldName)) {
|
||||
if ($ownerName === $this->getUser()->getUsername()) {
|
||||
$exists = $this->getUserConfig()->hasSection($name);
|
||||
} else {
|
||||
$exists = Config::navigation($itemConfig->type, $ownerName)->hasSection($name);
|
||||
}
|
||||
} else {
|
||||
$exists = (bool) $this->getShareConfig()
|
||||
->select()
|
||||
->where('name', $name)
|
||||
->where('owner', $ownerName)
|
||||
->count();
|
||||
}
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
throw new IcingaException(
|
||||
$this->translate('A navigation item with the name "%s" does already exist'),
|
||||
$name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unset($data['name']);
|
||||
}
|
||||
|
||||
$itemConfig->merge($data);
|
||||
foreach ($itemConfig->toArray() as $k => $v) {
|
||||
if ($v === null) {
|
||||
unset($itemConfig->$k);
|
||||
}
|
||||
}
|
||||
|
||||
if ($shared) {
|
||||
// Share all descendant children
|
||||
foreach ($this->getFlattenedChildren($oldName ?: $name) as $child) {
|
||||
$childConfig = $this->secondaryConfig->getSection($child);
|
||||
$this->secondaryConfig->removeSection($child);
|
||||
$childConfig->owner = $this->getUser()->getUsername();
|
||||
$config->setSection($child, $childConfig);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oldName) {
|
||||
// Update the parent name on all direct children
|
||||
foreach ($config as $sectionConfig) {
|
||||
if ($sectionConfig->parent === $oldName) {
|
||||
$sectionConfig->parent = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$config->removeSection($oldName);
|
||||
}
|
||||
|
||||
if ($this->secondaryConfig !== null) {
|
||||
$this->secondaryConfig->removeSection($oldName ?: $name);
|
||||
}
|
||||
|
||||
$config->setSection($name, $itemConfig);
|
||||
$this->setIniConfig($config);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a navigation item
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return ConfigObject The navigation item's config
|
||||
*
|
||||
* @throws NotFoundError In case no navigation item with the given name is found
|
||||
* @throws IcingaException In case the navigation item has still children
|
||||
*/
|
||||
public function delete($name)
|
||||
{
|
||||
$config = $this->getConfigForItem($name);
|
||||
if ($config === null) {
|
||||
throw new NotFoundError('No navigation item called "%s" found', $name);
|
||||
}
|
||||
|
||||
$children = $this->getFlattenedChildren($name);
|
||||
if (! empty($children)) {
|
||||
throw new IcingaException(
|
||||
$this->translate(
|
||||
'Unable to delete navigation item "%s". There'
|
||||
. ' are other items dependent from it: %s'
|
||||
),
|
||||
$name,
|
||||
join(', ', $children)
|
||||
);
|
||||
}
|
||||
|
||||
$section = $config->getSection($name);
|
||||
$config->removeSection($name);
|
||||
$this->setIniConfig($config);
|
||||
return $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unshare the given navigation item
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $parent
|
||||
*
|
||||
* @return Config The new config of the given navigation item
|
||||
*
|
||||
* @throws NotFoundError In case no navigation item with the given name is found
|
||||
* @throws IcingaException In case the navigation item has a parent assigned to it
|
||||
*/
|
||||
public function unshare($name, $parent = null)
|
||||
{
|
||||
$config = $this->getShareConfig();
|
||||
if (! $config->hasSection($name)) {
|
||||
throw new NotFoundError('No navigation item called "%s" found', $name);
|
||||
}
|
||||
|
||||
$itemConfig = $config->getSection($name);
|
||||
if ($parent === null) {
|
||||
$parent = $itemConfig->parent;
|
||||
}
|
||||
|
||||
if ($parent && $this->hasBeenShared($parent)) {
|
||||
throw new IcingaException(
|
||||
$this->translate(
|
||||
'Unable to unshare navigation item "%s". It is dependent from item "%s".'
|
||||
. ' Dependent items can only be unshared by unsharing their parent'
|
||||
),
|
||||
$name,
|
||||
$parent
|
||||
);
|
||||
}
|
||||
|
||||
$children = $this->getFlattenedChildren($name);
|
||||
$config->removeSection($name);
|
||||
$this->secondaryConfig = $config;
|
||||
|
||||
if (! $itemConfig->owner || $itemConfig->owner === $this->getUser()->getUsername()) {
|
||||
$config = $this->getUserConfig();
|
||||
} else {
|
||||
$config = Config::navigation($itemConfig->type, $itemConfig->owner);
|
||||
}
|
||||
|
||||
foreach ($children as $child) {
|
||||
$childConfig = $this->secondaryConfig->getSection($child);
|
||||
unset($childConfig->owner);
|
||||
$this->secondaryConfig->removeSection($child);
|
||||
$config->setSection($child, $childConfig);
|
||||
}
|
||||
|
||||
unset($itemConfig->owner);
|
||||
unset($itemConfig->users);
|
||||
unset($itemConfig->groups);
|
||||
|
||||
$config->setSection($name, $itemConfig);
|
||||
$this->setIniConfig($config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$shared = false;
|
||||
$itemTypes = $this->getItemTypes();
|
||||
$itemType = isset($formData['type']) ? $formData['type'] : key($itemTypes);
|
||||
if ($itemType === null) {
|
||||
throw new ProgrammingError(
|
||||
'This should actually not happen. Create a bug report at dev.icinga.org'
|
||||
. ' or remove this assertion if you know what you\'re doing'
|
||||
);
|
||||
}
|
||||
|
||||
$itemForm = $this->getItemForm($itemType);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'name',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Name'),
|
||||
'description' => $this->translate(
|
||||
'The name of this navigation item that is used to differentiate it from others'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (
|
||||
(! $itemForm->requiresParentSelection() || !isset($formData['parent']) || !$formData['parent'])
|
||||
&& $this->getUser()->can('application/share/navigation')
|
||||
) {
|
||||
$checked = isset($formData['shared']) ? null : (isset($formData['users']) || isset($formData['groups']));
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'shared',
|
||||
array(
|
||||
'autosubmit' => true,
|
||||
'ignore' => true,
|
||||
'value' => $checked,
|
||||
'label' => $this->translate('Shared'),
|
||||
'description' => $this->translate('Tick this box to share this item with others')
|
||||
)
|
||||
);
|
||||
|
||||
if ($checked || (isset($formData['shared']) && $formData['shared'])) {
|
||||
$shared = true;
|
||||
$this->addElement(
|
||||
'text',
|
||||
'users',
|
||||
array(
|
||||
'label' => $this->translate('Users'),
|
||||
'description' => $this->translate(
|
||||
'Comma separated list of usernames to share this item with'
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
'text',
|
||||
'groups',
|
||||
array(
|
||||
'label' => $this->translate('Groups'),
|
||||
'description' => $this->translate(
|
||||
'Comma separated list of group names to share this item with'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($itemTypes) || count($itemTypes) === 1) {
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'type',
|
||||
array(
|
||||
'value' => $itemType
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->addElement(
|
||||
'select',
|
||||
'type',
|
||||
array(
|
||||
'required' => true,
|
||||
'autosubmit' => true,
|
||||
'label' => $this->translate('Type'),
|
||||
'description' => $this->translate('The type of this navigation item'),
|
||||
'multiOptions' => $itemTypes
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (! $shared && $itemForm->requiresParentSelection()) {
|
||||
if ($this->itemToLoad && $this->hasBeenShared($this->itemToLoad)) {
|
||||
$itemConfig = $this->getShareConfig()->getSection($this->itemToLoad);
|
||||
$availableParents = $this->listAvailableParents($itemType, $itemConfig->owner);
|
||||
} else {
|
||||
$availableParents = $this->listAvailableParents($itemType);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'parent',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'autosubmit' => true,
|
||||
'label' => $this->translate('Parent'),
|
||||
'description' => $this->translate(
|
||||
'The parent item to assign this navigation item to. '
|
||||
. 'Select "None" to make this a main navigation item'
|
||||
),
|
||||
'multiOptions' => array_merge(
|
||||
array('' => $this->translate('None', 'No parent for a navigation item')),
|
||||
empty($availableParents) ? array() : array_combine($availableParents, $availableParents)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->addSubForm($itemForm, 'item_form');
|
||||
$itemForm->create($formData); // May require a parent which gets set by addSubForm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the configuration of the navigation item to load
|
||||
*/
|
||||
public function onRequest()
|
||||
{
|
||||
if ($this->itemToLoad) {
|
||||
$data = $this->getConfigForItem($this->itemToLoad)->getSection($this->itemToLoad)->toArray();
|
||||
$data['name'] = $this->itemToLoad;
|
||||
$this->populate($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($formData)
|
||||
{
|
||||
if (! parent::isValid($formData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$valid = true;
|
||||
if (isset($formData['users']) && $formData['users']) {
|
||||
$parsedUserRestrictions = array();
|
||||
foreach (Auth::getInstance()->getRestrictions('application/share/users') as $userRestriction) {
|
||||
$parsedUserRestrictions[] = array_map('trim', explode(',', $userRestriction));
|
||||
}
|
||||
|
||||
if (! empty($parsedUserRestrictions)) {
|
||||
$desiredUsers = array_map('trim', explode(',', $formData['users']));
|
||||
array_unshift($parsedUserRestrictions, $desiredUsers);
|
||||
$forbiddenUsers = call_user_func_array('array_diff', $parsedUserRestrictions);
|
||||
if (! empty($forbiddenUsers)) {
|
||||
$valid = false;
|
||||
$this->getElement('users')->addError(
|
||||
$this->translate(sprintf(
|
||||
'You are not permitted to share this navigation item with the following users: %s',
|
||||
implode(', ', $forbiddenUsers)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($formData['groups']) && $formData['groups']) {
|
||||
$parsedGroupRestrictions = array();
|
||||
foreach (Auth::getInstance()->getRestrictions('application/share/groups') as $groupRestriction) {
|
||||
$parsedGroupRestrictions[] = array_map('trim', explode(',', $groupRestriction));
|
||||
}
|
||||
|
||||
if (! empty($parsedGroupRestrictions)) {
|
||||
$desiredGroups = array_map('trim', explode(',', $formData['groups']));
|
||||
array_unshift($parsedGroupRestrictions, $desiredGroups);
|
||||
$forbiddenGroups = call_user_func_array('array_diff', $parsedGroupRestrictions);
|
||||
if (! empty($forbiddenGroups)) {
|
||||
$valid = false;
|
||||
$this->getElement('groups')->addError(
|
||||
$this->translate(sprintf(
|
||||
'You are not permitted to share this navigation item with the following groups: %s',
|
||||
implode(', ', $forbiddenGroups)
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
$values = parent::getValues();
|
||||
$values = array_merge($values, $values['item_form']);
|
||||
unset($values['item_form']);
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function writeConfig(Config $config)
|
||||
{
|
||||
parent::writeConfig($config);
|
||||
|
||||
if ($this->secondaryConfig !== null) {
|
||||
$this->config = $this->secondaryConfig; // Causes the config being displayed to the user in case of an error
|
||||
parent::writeConfig($this->secondaryConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the navigation configuration the given item is a part of
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Config|null In case the item is not part of any configuration
|
||||
*/
|
||||
protected function getConfigForItem($name)
|
||||
{
|
||||
if ($this->getUserConfig()->hasSection($name)) {
|
||||
return $this->getUserConfig();
|
||||
} elseif ($this->getShareConfig()->hasSection($name)) {
|
||||
if (
|
||||
$this->getShareConfig()->get($name, 'owner') === $this->getUser()->getUsername()
|
||||
|| $this->getUser()->can('config/application/navigation')
|
||||
) {
|
||||
return $this->getShareConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given navigation item has been shared
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasBeenShared($name, $type = null)
|
||||
{
|
||||
return $this->getShareConfig($type) === $this->getConfigForItem($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form for the given type of navigation item
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
protected function getItemForm($type)
|
||||
{
|
||||
$className = String::cname($type, '-') . 'Form';
|
||||
|
||||
$form = null;
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
|
||||
$classPath = 'Icinga\\Module\\'
|
||||
. ucfirst($module->getName())
|
||||
. '\\'
|
||||
. static::FORM_NS
|
||||
. '\\'
|
||||
. $className;
|
||||
if (class_exists($classPath)) {
|
||||
$form = new $classPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($form === null) {
|
||||
$classPath = 'Icinga\\' . static::FORM_NS . '\\' . $className;
|
||||
if (class_exists($classPath)) {
|
||||
$form = new $classPath();
|
||||
}
|
||||
}
|
||||
|
||||
if ($form === null) {
|
||||
Logger::debug(
|
||||
'Failed to find custom navigation item form %s for item %s. Using form NavigationItemForm now',
|
||||
$className,
|
||||
$type
|
||||
);
|
||||
|
||||
$form = new NavigationItemForm();
|
||||
} elseif (! $form instanceof NavigationItemForm) {
|
||||
throw new ProgrammingError('Class %s must inherit from NavigationItemForm', $classPath);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Navigation;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
class NavigationItemForm extends Form
|
||||
{
|
||||
/**
|
||||
* Whether to create a select input to choose a parent for a navigation item of a particular type
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $requiresParentSelection = false;
|
||||
|
||||
/**
|
||||
* Return whether to create a select input to choose a parent for a navigation item of a particular type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function requiresParentSelection()
|
||||
{
|
||||
return $this->requiresParentSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
$this->addElement(
|
||||
'select',
|
||||
'target',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'label' => $this->translate('Target'),
|
||||
'description' => $this->translate('The target where to open this navigation item\'s url'),
|
||||
'multiOptions' => array(
|
||||
'_blank' => $this->translate('New Window'),
|
||||
'_next' => $this->translate('New Column'),
|
||||
'_main' => $this->translate('Single Column'),
|
||||
'_self' => $this->translate('Current Column')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'url',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'label' => $this->translate('Url'),
|
||||
'description' => $this->translate(
|
||||
'The url of this navigation item. Leave blank if you only want the'
|
||||
. ' name being displayed. For external urls, make sure to prepend'
|
||||
. ' an appropriate protocol identifier (e.g. http://example.tld)'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'icon',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'label' => $this->translate('Icon'),
|
||||
'description' => $this->translate(
|
||||
'The icon of this navigation item. Leave blank if you do not want a icon being displayed'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValues($suppressArrayNotation = false)
|
||||
{
|
||||
$values = parent::getValues($suppressArrayNotation);
|
||||
if (isset($values['url']) && $values['url']) {
|
||||
$url = Url::fromPath($values['url']);
|
||||
if (! $url->isExternal() && ($relativePath = $url->getRelativeUrl())) {
|
||||
$values['url'] = $relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ class RoleForm extends ConfigForm
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $providedRestrictions = array();
|
||||
protected $providedRestrictions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -37,6 +37,8 @@ class RoleForm extends ConfigForm
|
|||
{
|
||||
$this->providedPermissions = array(
|
||||
'*' => $this->translate('Allow everything') . ' (*)',
|
||||
'application/share/navigation' => $this->translate('Allow to share navigation items')
|
||||
. ' (application/share/navigation)',
|
||||
'application/stacktraces' => $this->translate(
|
||||
'Allow to adjust in the preferences whether to show stacktraces'
|
||||
) . ' (application/stacktraces)',
|
||||
|
@ -48,6 +50,7 @@ class RoleForm extends ConfigForm
|
|||
'config/application/resources' => 'config/application/resources',
|
||||
'config/application/userbackend' => 'config/application/userbackend',
|
||||
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
|
||||
'config/application/navigation' => 'config/application/navigation',
|
||||
'config/authentication/*' => 'config/authentication/*',
|
||||
'config/authentication/users/*' => 'config/authentication/users/*',
|
||||
'config/authentication/users/show' => 'config/authentication/users/show',
|
||||
|
@ -67,9 +70,23 @@ class RoleForm extends ConfigForm
|
|||
'config/modules' => 'config/modules'
|
||||
*/
|
||||
);
|
||||
|
||||
|
||||
|
||||
$helper = new Zend_Form_Element('bogus');
|
||||
$this->providedRestrictions = array(
|
||||
$helper->filterName('application/share/users') => array(
|
||||
'name' => 'application/share/users',
|
||||
'description' => $this->translate(
|
||||
'Restrict which users this role can share items and information with'
|
||||
)
|
||||
),
|
||||
$helper->filterName('application/share/groups') => array(
|
||||
'name' => 'application/share/groups',
|
||||
'description' => $this->translate(
|
||||
'Restrict which groups this role can share items and information with'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$mm = Icinga::app()->getModuleManager();
|
||||
foreach ($mm->listInstalledModules() as $moduleName) {
|
||||
$modulePermission = $mm::MODULE_PERMISSION_NS . $moduleName;
|
||||
|
|
|
@ -22,7 +22,7 @@ if ($this->layout()->autorefreshInterval) {
|
|||
<?php if (Auth::getInstance()->isAuthenticated()): ?>
|
||||
<?= $this->qlink(
|
||||
'',
|
||||
'/dashboard',
|
||||
'dashboard',
|
||||
null,
|
||||
array(
|
||||
'icon' => '../logo_icinga-inv.png',
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<div class="logo">
|
||||
<div class="image">
|
||||
<img aria-hidden="true" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="below-logo">
|
||||
<?= $this->render('inline.phtml') ?>
|
||||
</div>
|
|
@ -63,7 +63,7 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
|||
<!--<![endif]-->
|
||||
<script type="text/javascript">
|
||||
var icinga = new Icinga({
|
||||
baseUrl: '<?= $this->href('/') ?>'
|
||||
baseUrl: '<?= $this->baseUrl(); ?>'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\MenuRenderer;
|
||||
use Icinga\Application\Icinga;
|
||||
|
||||
// Don't render a menu for unauthenticated users unless menu is auth aware
|
||||
if (! $this->auth()->isAuthenticated()) {
|
||||
|
@ -27,10 +25,7 @@ if (! $this->auth()->isAuthenticated()) {
|
|||
'layout/menu.phtml',
|
||||
'default',
|
||||
array(
|
||||
'menuRenderer' => new MenuRenderer(
|
||||
Menu::load(),
|
||||
Url::fromRequest()->without('renderLayout')->getRelativeUrl()
|
||||
)
|
||||
'menuRenderer' => Icinga::app()->getMenu()->getRenderer()
|
||||
)
|
||||
) ?>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<img aria-hidden="true" class="fade-in one" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="form" data-base-target="layout">
|
||||
<div class="below-logo" data-base-target="layout">
|
||||
<h1><?= $this->translate('Welcome to Icinga Web 2'); ?></h1>
|
||||
<?php if ($requiresSetup): ?>
|
||||
<p class="config-note"><?= sprintf(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<?= $tabs; ?>
|
||||
</div>
|
||||
<div class="content" data-base-target="_next">
|
||||
<a href="<?= $this->href('/config/createresource'); ?>">
|
||||
<a href="<?= $this->href('config/createresource'); ?>">
|
||||
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Resource'); ?>
|
||||
</a>
|
||||
<table class="action alternating" id="resource-edit-table">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<?= $tabs; ?>
|
||||
</div>
|
||||
<div class="content" data-base-target="_next">
|
||||
<a href="<?= $this->href('/config/createuserbackend'); ?>">
|
||||
<a href="<?= $this->href('config/createuserbackend'); ?>">
|
||||
<?= $this->icon('plus'); ?><?= $this->translate('Create A New User Backend'); ?>
|
||||
</a>
|
||||
<div id="authentication-reorder-form">
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<?php if (! $hideControls): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs->showOnlyCloseButton() ?>
|
||||
<?= $tabs->showOnlyCloseButton(); ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p><strong><?= nl2br($this->escape($message)) ?></strong></p>
|
||||
<?php if (isset($stackTrace)) : ?>
|
||||
<hr />
|
||||
<pre><?= $this->escape($stackTrace) ?></pre>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p><strong><?= nl2br($this->escape($message)); ?></strong></p>
|
||||
<?php if (isset($stackTrace)): ?>
|
||||
<hr />
|
||||
<pre><?= $this->escape($stackTrace) ?></pre>
|
||||
<?php endif ?>
|
||||
</div>
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Icinga\Data\Extensible;
|
||||
use Icinga\Data\Updatable;
|
||||
use Icinga\Data\Selectable;
|
||||
|
||||
$extensible = $this->hasPermission('config/authentication/groups/add') && $backend instanceof Extensible;
|
||||
|
||||
|
@ -67,7 +68,22 @@ foreach ($members as $member): ?>
|
|||
<tbody>
|
||||
<?php endif ?>
|
||||
<tr>
|
||||
<td class="member-name"><?= $this->escape($member->user_name); ?></td>
|
||||
<td class="member-name">
|
||||
<?php if (
|
||||
$this->hasPermission('config/authentication/users/show')
|
||||
&& ($userBackend = $backend->getUserBackend()) !== null
|
||||
&& $userBackend instanceof Selectable
|
||||
): ?>
|
||||
<?= $this->qlink($member->user_name, 'user/show', array(
|
||||
'backend' => $userBackend->getName(),
|
||||
'user' => $member->user_name
|
||||
), array(
|
||||
'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name)
|
||||
)); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->escape($member->user_name); ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<?php if (isset($removeForm)): ?>
|
||||
<td class="member-remove" data-base-target="_self">
|
||||
<?php $removeForm->getElement('user_name')->setValue($member->user_name); echo $removeForm; ?>
|
||||
|
|
|
@ -12,8 +12,5 @@ if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
|
|||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
||||
/>
|
||||
</form>
|
||||
<? endif; ?>
|
||||
<nav>
|
||||
<h1 id="navigation" class="sr-only"><?= t('Navigation'); ?></h1>
|
||||
<?= $menuRenderer; ?>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?= $menuRenderer->setHeading(t('Navigation'))->setElementTag('nav'); ?>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="controls">
|
||||
<?= $tabs->showOnlyCloseButton(); ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?= $form; ?>
|
||||
</div>
|
|
@ -0,0 +1,59 @@
|
|||
<?php if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
<?= $this->sortBox; ?>
|
||||
<?= $this->limiter; ?>
|
||||
<?= $this->paginator; ?>
|
||||
<?= $this->filterEditor; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="content" data-base-target="_next">
|
||||
<a href="<?= $this->href('navigation/add'); ?>">
|
||||
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Navigation Item'); ?>
|
||||
</a>
|
||||
<?php if (count($items) === 0): ?>
|
||||
<p><?= $this->translate('You did not create any navigation item yet'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="action alternating">
|
||||
<thead>
|
||||
<th><?= $this->translate('Navigation'); ?></th>
|
||||
<th style="width: 10em"><?= $this->translate('Type'); ?></th>
|
||||
<th style="width: 5em"><?= $this->translate('Shared'); ?></th>
|
||||
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><?= $this->qlink(
|
||||
$item->name,
|
||||
'navigation/edit',
|
||||
array(
|
||||
'name' => $item->name,
|
||||
'type' => $item->type
|
||||
),
|
||||
array(
|
||||
'title' => sprintf($this->translate('Edit navigation item %s'), $item->name)
|
||||
)
|
||||
); ?></td>
|
||||
<td><?= $item->type && isset($types[$item->type])
|
||||
? $this->escape($types[$item->type])
|
||||
: $this->escape($this->translate('Unknown')); ?></td>
|
||||
<td><?= $item->owner ? $this->translate('Yes') : $this->translate('No'); ?></td>
|
||||
<td><?= $this->qlink(
|
||||
'',
|
||||
'navigation/remove',
|
||||
array(
|
||||
'name' => $item->name,
|
||||
'type' => $item->type
|
||||
),
|
||||
array(
|
||||
'icon' => 'trash',
|
||||
'title' => sprintf($this->translate('Remove navigation item %s'), $item->name)
|
||||
)
|
||||
); ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</div>
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Url;
|
||||
|
||||
if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
<?= $this->sortBox; ?>
|
||||
<?= $this->limiter; ?>
|
||||
<?= $this->paginator; ?>
|
||||
<?= $this->filterEditor; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="content" data-base-target="_next">
|
||||
<?php if (count($items) === 0): ?>
|
||||
<p><?= $this->translate('There are currently no navigation items being shared'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="action alternating">
|
||||
<thead>
|
||||
<th><?= $this->translate('Shared Navigation'); ?></th>
|
||||
<th style="width: 10em"><?= $this->translate('Type'); ?></th>
|
||||
<th style="width: 10em"><?= $this->translate('Owner'); ?></th>
|
||||
<th style="width: 5em"><?= $this->translate('Unshare'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><?= $this->qlink(
|
||||
$item->name,
|
||||
'navigation/edit',
|
||||
array(
|
||||
'name' => $item->name,
|
||||
'type' => $item->type,
|
||||
'owner' => $item->owner,
|
||||
'referrer' => 'shared'
|
||||
),
|
||||
array(
|
||||
'title' => sprintf($this->translate('Edit shared navigation item %s'), $item->name)
|
||||
)
|
||||
); ?></td>
|
||||
<td><?= $item->type && isset($types[$item->type])
|
||||
? $this->escape($types[$item->type])
|
||||
: $this->escape($this->translate('Unknown')); ?></td>
|
||||
<td><?= $this->escape($item->owner); ?></td>
|
||||
<?php if ($item->parent): ?>
|
||||
<td><?= $this->icon(
|
||||
'block',
|
||||
sprintf(
|
||||
$this->translate(
|
||||
'This is a child of the navigation item %1$s. You can'
|
||||
. ' only unshare this item by unsharing %1$s'
|
||||
),
|
||||
$item->parent
|
||||
)
|
||||
); ?></td>
|
||||
<?php else: ?>
|
||||
<td data-base-target="_self"><?= $removeForm
|
||||
->setDefault('name', $item->name)
|
||||
->setAction(Url::fromPath(
|
||||
'navigation/unshare',
|
||||
array('type' => $item->type, 'owner' => $item->owner)
|
||||
)); ?></td>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</div>
|
|
@ -301,3 +301,8 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
|
|||
* The **instances.ini** configuration file provided by the monitoring module
|
||||
has been renamed to **commandtransports.ini**. The content and location of
|
||||
the file remains unchanged.
|
||||
|
||||
* The location of a user's preferences has been changed from
|
||||
**<config-dir>/preferences/<username>.ini** to
|
||||
**<config-dir>/preferences/<username>/config.ini**.
|
||||
The content of the file remains unchanged.
|
||||
|
|
|
@ -19,21 +19,38 @@ to handle authentication and authorization, monitoring data or user preferences.
|
|||
Directive | Description
|
||||
----------------|------------
|
||||
**type** | `db`
|
||||
**db** | Database management system. Either `mysql` or `pgsql`.
|
||||
**host** | Connect to the database server on the given host.
|
||||
**port** | Port number to use for the connection.
|
||||
**db** | Database management system. In most cases `mysql` or `pgsql`.
|
||||
**host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL.
|
||||
**port** | Port number to use. Mandatory for connections to a PostgreSQL database.
|
||||
**username** | The username to use when connecting to the server.
|
||||
**password** | The password to use when connecting to the server.
|
||||
**dbname** | The database to use.
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
[icingaweb]
|
||||
````
|
||||
[icingaweb-mysql-tcp]
|
||||
type = db
|
||||
db = mysql
|
||||
host = 127.0.0.1
|
||||
port = 3306
|
||||
username = icingaweb
|
||||
password = icingaweb
|
||||
dbname = icingaweb
|
||||
|
||||
[icingaweb-mysql-socket]
|
||||
type = db
|
||||
db = mysql
|
||||
host = localhost
|
||||
port = 3306
|
||||
username = icingaweb
|
||||
password = icingaweb
|
||||
dbname = icingaweb
|
||||
|
||||
[icingaweb-pgsql-socket]
|
||||
type = db
|
||||
db = pgsql
|
||||
host = /var/run/postgresql
|
||||
port = 5432
|
||||
username = icingaweb
|
||||
password = icingaweb
|
||||
dbname = icingaweb
|
||||
|
|
|
@ -12,7 +12,10 @@ use Icinga\Data\ConfigObject;
|
|||
use Icinga\Data\Selectable;
|
||||
use Icinga\Data\SimpleQuery;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\File\Ini\IniParser;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
|
||||
/**
|
||||
* Container for INI like configuration and global registry of application and module related configuration.
|
||||
|
@ -40,6 +43,13 @@ class Config implements Countable, Iterator, Selectable
|
|||
*/
|
||||
protected static $modules = array();
|
||||
|
||||
/**
|
||||
* Navigation config instances per type
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $navigation = array();
|
||||
|
||||
/**
|
||||
* The internal ConfigObject
|
||||
*
|
||||
|
@ -313,9 +323,7 @@ class Config implements Countable, Iterator, Selectable
|
|||
if ($filepath === false) {
|
||||
$emptyConfig->setConfigFile($file);
|
||||
} elseif (is_readable($filepath)) {
|
||||
$config = new static(new ConfigObject(parse_ini_file($filepath, true)));
|
||||
$config->setConfigFile($filepath);
|
||||
return $config;
|
||||
return IniParser::parseIniFile($filepath);
|
||||
} elseif (@file_exists($filepath)) {
|
||||
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
|
||||
}
|
||||
|
@ -417,6 +425,60 @@ class Config implements Countable, Iterator, Selectable
|
|||
return $moduleConfigs[$configname];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a navigation config
|
||||
*
|
||||
* @param string $type The type identifier of the navigation item for which to return its config
|
||||
* @param string $username A user's name or null if the shared config is desired
|
||||
* @param bool $fromDisk If true, the configuration will be read from disk
|
||||
*
|
||||
* @return Config The requested configuration
|
||||
*/
|
||||
public static function navigation($type, $username = null, $fromDisk = false)
|
||||
{
|
||||
if (! isset(self::$navigation[$type])) {
|
||||
self::$navigation[$type] = array();
|
||||
}
|
||||
|
||||
$branch = $username ?: 'shared';
|
||||
$typeConfigs = self::$navigation[$type];
|
||||
if (! isset($typeConfigs[$branch]) || $fromDisk) {
|
||||
$typeConfigs[$branch] = static::fromIni(static::getNavigationConfigPath($type, $username));
|
||||
}
|
||||
|
||||
return $typeConfigs[$branch];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the configuration file for the given navigation item type and user
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $username
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws IcingaException In case the given type is unknown
|
||||
*/
|
||||
protected static function getNavigationConfigPath($type, $username = null)
|
||||
{
|
||||
$itemTypeConfig = Navigation::getItemTypeConfiguration();
|
||||
if (! isset($itemTypeConfig[$type])) {
|
||||
throw new IcingaException('Invalid navigation item type %s provided', $type);
|
||||
}
|
||||
|
||||
if (isset($itemTypeConfig[$type]['config'])) {
|
||||
$filename = $itemTypeConfig[$type]['config'] . '.ini';
|
||||
} else {
|
||||
$filename = $type . 's.ini';
|
||||
}
|
||||
|
||||
return static::resolvePath(
|
||||
($username ? 'preferences' . DIRECTORY_SEPARATOR . $username : 'navigation')
|
||||
. DIRECTORY_SEPARATOR
|
||||
. $filename
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this config rendered as a INI structured string
|
||||
*
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
/**
|
||||
* Container for module dashboards
|
||||
*/
|
||||
class DashboardContainer extends NavigationItemContainer
|
||||
{
|
||||
/**
|
||||
* This dashboard's dashlets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dashlets;
|
||||
|
||||
/**
|
||||
* Set this dashboard's dashlets
|
||||
*
|
||||
* @param array $dashlets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDashlets(array $dashlets)
|
||||
{
|
||||
$this->dashlets = $dashlets;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this dashboard's dashlets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDashlets()
|
||||
{
|
||||
return $this->dashlets ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new dashlet
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $url
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add($name, $url)
|
||||
{
|
||||
$this->dashlets[$name] = $url;
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
/**
|
||||
* Container for module menu items
|
||||
*/
|
||||
class MenuItemContainer extends NavigationItemContainer
|
||||
{
|
||||
/**
|
||||
* This menu item's children
|
||||
*
|
||||
* @var MenuItemContainer[]
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* Set this menu item's children
|
||||
*
|
||||
* @param MenuItemContainer[] $children
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChildren(array $children)
|
||||
{
|
||||
$this->children = $children;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this menu item's children
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new sub menu
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $properties
|
||||
*
|
||||
* @return MenuItemContainer The newly added sub menu
|
||||
*/
|
||||
public function add($name, array $properties = array())
|
||||
{
|
||||
$child = new MenuItemContainer($name, $properties);
|
||||
$this->children[] = $child;
|
||||
return $child;
|
||||
}
|
||||
}
|
|
@ -11,7 +11,8 @@ use Icinga\Application\ApplicationBootstrap;
|
|||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Application\Modules\DashboardContainer;
|
||||
use Icinga\Application\Modules\MenuItemContainer;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
|
@ -19,9 +20,8 @@ use Icinga\Util\File;
|
|||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Controller\Dispatcher;
|
||||
use Icinga\Web\Hook;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Web\Widget\Dashboard\Pane;
|
||||
|
||||
/**
|
||||
* Module handling
|
||||
|
@ -189,7 +189,7 @@ class Module
|
|||
/**
|
||||
* A set of menu elements
|
||||
*
|
||||
* @var Menu[]
|
||||
* @var MenuItemContainer[]
|
||||
*/
|
||||
protected $menuItems = array();
|
||||
|
||||
|
@ -221,6 +221,13 @@ class Module
|
|||
*/
|
||||
protected $userGroupBackends = array();
|
||||
|
||||
/**
|
||||
* This module's configurable navigation items
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $navigationItems = array();
|
||||
|
||||
/**
|
||||
* Create a new module object
|
||||
*
|
||||
|
@ -277,38 +284,98 @@ class Module
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all pane items
|
||||
* Return this module's dashboard
|
||||
*
|
||||
* @return array
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getPaneItems()
|
||||
public function getDashboard()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return $this->paneItems;
|
||||
return $this->createDashboard($this->paneItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pane to dashboard
|
||||
* Create and return a new navigation for the given dashboard panes
|
||||
*
|
||||
* @param string $name
|
||||
* @param DashboardContainer[] $panes
|
||||
*
|
||||
* @return Pane
|
||||
* @return Navigation
|
||||
*/
|
||||
protected function dashboard($name)
|
||||
public function createDashboard(array $panes)
|
||||
{
|
||||
$this->paneItems[$name] = new Pane($name);
|
||||
$navigation = new Navigation();
|
||||
foreach ($panes as $pane) {
|
||||
/** @var DashboardContainer $pane */
|
||||
$dashlets = array();
|
||||
foreach ($pane->getDashlets() as $dashletName => $dashletUrl) {
|
||||
$dashlets[$this->translate($dashletName)] = $dashletUrl;
|
||||
}
|
||||
|
||||
$navigation->addItem(
|
||||
$pane->getName(),
|
||||
array_merge(
|
||||
$pane->getProperties(),
|
||||
array(
|
||||
'label' => $this->translate($pane->getName()),
|
||||
'type' => 'dashboard-pane',
|
||||
'dashlets' => $dashlets
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or get a dashboard pane
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $properties
|
||||
*
|
||||
* @return DashboardContainer
|
||||
*/
|
||||
protected function dashboard($name, array $properties = array())
|
||||
{
|
||||
if (array_key_exists($name, $this->paneItems)) {
|
||||
$this->paneItems[$name]->setProperties($properties);
|
||||
} else {
|
||||
$this->paneItems[$name] = new DashboardContainer($name, $properties);
|
||||
}
|
||||
|
||||
return $this->paneItems[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all menu items
|
||||
* Return this module's menu
|
||||
*
|
||||
* @return array
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getMenuItems()
|
||||
public function getMenu()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return $this->menuItems;
|
||||
return $this->createMenu($this->menuItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new navigation for the given menu items
|
||||
*
|
||||
* @param MenuItemContainer[] $items
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
private function createMenu(array $items)
|
||||
{
|
||||
$navigation = new Navigation();
|
||||
foreach ($items as $item) {
|
||||
/** @var MenuItemContainer $item */
|
||||
$navigationItem = $navigation->createItem($item->getName(), $item->getProperties());
|
||||
$navigationItem->setChildren($this->createMenu($item->getChildren()));
|
||||
$navigationItem->setLabel($this->translate($item->getName()));
|
||||
$navigation->addItem($navigationItem);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -317,14 +384,14 @@ class Module
|
|||
* @param string $name
|
||||
* @param array $properties
|
||||
*
|
||||
* @return Menu
|
||||
* @return MenuItemContainer
|
||||
*/
|
||||
protected function menuSection($name, array $properties = array())
|
||||
{
|
||||
if (array_key_exists($name, $this->menuItems)) {
|
||||
$this->menuItems[$name]->setProperties($properties);
|
||||
} else {
|
||||
$this->menuItems[$name] = new Menu($name, new ConfigObject($properties));
|
||||
$this->menuItems[$name] = new MenuItemContainer($name, $properties);
|
||||
}
|
||||
|
||||
return $this->menuItems[$name];
|
||||
|
@ -831,6 +898,17 @@ class Module
|
|||
return $this->userGroupBackends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this module's configurable navigation items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNavigationItems()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return $this->navigationItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a named permission
|
||||
*
|
||||
|
@ -935,6 +1013,25 @@ class Module
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a new type of configurable navigation item with a optional label and config filename
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $label
|
||||
* @param string $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function provideNavigationItem($type, $label = null, $config = null)
|
||||
{
|
||||
$this->navigationItems[$type] = array(
|
||||
'label' => $label,
|
||||
'config' => $config
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register module namespaces on our class loader
|
||||
*
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Container for module navigation items
|
||||
*/
|
||||
abstract class NavigationItemContainer
|
||||
{
|
||||
/**
|
||||
* This navigation item's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* This navigation item's properties
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $properties;
|
||||
|
||||
/**
|
||||
* Create a new NavigationItemContainer
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $properties
|
||||
*/
|
||||
public function __construct($name, array $properties = array())
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->properties = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this menu item's name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this menu item's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this menu item's properties
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperties(array $properties)
|
||||
{
|
||||
$this->properties = $properties;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this menu item's properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow dynamic setters and getters for properties
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ProgrammingError In case the called method is not supported
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (method_exists($this, $name)) {
|
||||
return call_user_method_array($name, $this, $arguments);
|
||||
}
|
||||
|
||||
$type = substr($name, 0, 3);
|
||||
if ($type !== 'set' && $type !== 'get') {
|
||||
throw new ProgrammingError(
|
||||
'Dynamic method %s is not supported. Only getters (get*) and setters (set*) are.',
|
||||
$name
|
||||
);
|
||||
}
|
||||
|
||||
$propertyName = strtolower(join('_', preg_split('~(?=[A-Z])~', lcfirst(substr($name, 3)))));
|
||||
if ($type === 'set') {
|
||||
$this->properties[$propertyName] = $arguments[0];
|
||||
return $this;
|
||||
} else { // $type === 'get'
|
||||
return array_key_exists($propertyName, $this->properties) ? $this->properties[$propertyName] : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ use Icinga\User;
|
|||
use Icinga\Util\TimezoneDetect;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Controller\Dispatcher;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\Web\Session\Session as BaseSession;
|
||||
|
@ -139,6 +140,203 @@ class Web extends EmbeddedWeb
|
|||
return $this->viewRenderer;
|
||||
}
|
||||
|
||||
private function hasAccessToSharedNavigationItem(& $config)
|
||||
{
|
||||
// TODO: Provide a more sophisticated solution
|
||||
|
||||
if (isset($config['owner']) && $config['owner'] === $this->user->getUsername()) {
|
||||
unset($config['owner']);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($config['users'])) {
|
||||
$users = array_map('trim', explode(',', strtolower($config['users'])));
|
||||
if (in_array($this->user->getUsername(), $users, true)) {
|
||||
unset($config['users']);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['groups'])) {
|
||||
$groups = array_map('trim', explode(',', strtolower($config['groups'])));
|
||||
$userGroups = array_map('strtolower', $this->user->getGroups());
|
||||
$matches = array_intersect($userGroups, $groups);
|
||||
if (! empty($matches)) {
|
||||
unset($config['groups']);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return the shared navigation of the given type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getSharedNavigation($type)
|
||||
{
|
||||
$config = Config::navigation($type === 'dashboard-pane' ? 'dashlet' : $type);
|
||||
|
||||
if ($type === 'dashboard-pane') {
|
||||
$panes = array();
|
||||
foreach ($config as $dashletName => $dashletConfig) {
|
||||
if ($this->hasAccessToSharedNavigationItem($dashletConfig)) {
|
||||
// TODO: Throw ConfigurationError if pane or url is missing
|
||||
$panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url;
|
||||
}
|
||||
}
|
||||
|
||||
$navigation = new Navigation();
|
||||
foreach ($panes as $paneName => $dashlets) {
|
||||
$navigation->addItem(
|
||||
$paneName,
|
||||
array(
|
||||
'type' => 'dashboard-pane',
|
||||
'dashlets' => $dashlets
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$items = array();
|
||||
foreach ($config as $name => $typeConfig) {
|
||||
if ($this->hasAccessToSharedNavigationItem($typeConfig)) {
|
||||
$items[$name] = $typeConfig;
|
||||
}
|
||||
}
|
||||
|
||||
$navigation = Navigation::fromConfig($items);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the app's menu
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getMenu()
|
||||
{
|
||||
if ($this->user !== null) {
|
||||
$menu = array(
|
||||
'dashboard' => array(
|
||||
'label' => t('Dashboard'),
|
||||
'url' => 'dashboard',
|
||||
'icon' => 'dashboard',
|
||||
'priority' => 10
|
||||
),
|
||||
'system' => array(
|
||||
'label' => t('System'),
|
||||
'icon' => 'services',
|
||||
'priority' => 700,
|
||||
'renderer' => array(
|
||||
'SummaryNavigationItemRenderer',
|
||||
'state' => 'critical'
|
||||
),
|
||||
'children' => array(
|
||||
'about' => array(
|
||||
'label' => t('About'),
|
||||
'url' => 'about',
|
||||
'priority' => 701
|
||||
)
|
||||
)
|
||||
),
|
||||
'configuration' => array(
|
||||
'label' => t('Configuration'),
|
||||
'icon' => 'wrench',
|
||||
'permission' => 'config/*',
|
||||
'priority' => 800,
|
||||
'children' => array(
|
||||
'application' => array(
|
||||
'label' => t('Application'),
|
||||
'url' => 'config/general',
|
||||
'permission' => 'config/application/*',
|
||||
'priority' => 810
|
||||
),
|
||||
'navigation' => array(
|
||||
'label' => t('Shared Navigation'),
|
||||
'url' => 'navigation/shared',
|
||||
'permission' => 'config/application/navigation',
|
||||
'priority' => 820,
|
||||
),
|
||||
'authentication' => array(
|
||||
'label' => t('Authentication'),
|
||||
'url' => 'config/userbackend',
|
||||
'permission' => 'config/authentication/*',
|
||||
'priority' => 830
|
||||
),
|
||||
'roles' => array(
|
||||
'label' => t('Roles'),
|
||||
'url' => 'role/list',
|
||||
'permission' => 'config/authentication/roles/show',
|
||||
'priority' => 840
|
||||
),
|
||||
'users' => array(
|
||||
'label' => t('Users'),
|
||||
'url' => 'user/list',
|
||||
'permission' => 'config/authentication/users/show',
|
||||
'priority' => 850
|
||||
),
|
||||
'groups' => array(
|
||||
'label' => t('Usergroups'),
|
||||
'url' => 'group/list',
|
||||
'permission' => 'config/authentication/groups/show',
|
||||
'priority' => 860
|
||||
),
|
||||
'modules' => array(
|
||||
'label' => t('Modules'),
|
||||
'url' => 'config/modules',
|
||||
'permission' => 'config/modules',
|
||||
'priority' => 890
|
||||
)
|
||||
)
|
||||
),
|
||||
'user' => array(
|
||||
'label' => $this->user->getUsername(),
|
||||
'icon' => 'user',
|
||||
'priority' => 900,
|
||||
'children' => array(
|
||||
'preferences' => array(
|
||||
'label' => t('Preferences'),
|
||||
'url' => 'preference',
|
||||
'priority' => 910
|
||||
),
|
||||
'navigation' => array(
|
||||
'label' => t('Navigation'),
|
||||
'url' => 'navigation',
|
||||
'priority' => 920
|
||||
),
|
||||
'logout' => array(
|
||||
'label' => t('Logout'),
|
||||
'url' => 'authentication/logout',
|
||||
'priority' => 990,
|
||||
'renderer' => array(
|
||||
'NavigationItemRenderer',
|
||||
'target' => '_self'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (Logger::writesToFile()) {
|
||||
$menu['system']['children']['application_log'] = array(
|
||||
'label' => t('Application Log'),
|
||||
'url' => 'list/applicationlog',
|
||||
'priority' => 710
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$menu = array();
|
||||
}
|
||||
|
||||
return Navigation::fromArray($menu)->load('menu-item');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch public interface
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,23 @@
|
|||
|
||||
use Icinga\Util\Translator;
|
||||
|
||||
|
||||
/**
|
||||
* No-op translate
|
||||
*
|
||||
* Supposed to be used for marking a string as available for translation without actually translating it immediately.
|
||||
* The returned string is the one given in the input. This does only work with the standard gettext macros t() and mt().
|
||||
*
|
||||
* @param string $messageId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function N_($messageId)
|
||||
{
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
|
||||
if (extension_loaded('gettext')) {
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,10 +12,16 @@ use Icinga\Protocol\Ldap\Expression;
|
|||
use Icinga\Repository\LdapRepository;
|
||||
use Icinga\Repository\RepositoryQuery;
|
||||
use Icinga\User;
|
||||
use Icinga\Application\Logger;
|
||||
|
||||
class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBackendInterface
|
||||
class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInterface
|
||||
{
|
||||
/**
|
||||
* The user backend being associated with this user group backend
|
||||
*
|
||||
* @var LdapUserBackend
|
||||
*/
|
||||
protected $userBackend;
|
||||
|
||||
/**
|
||||
* The base DN to use for a user query
|
||||
*
|
||||
|
@ -105,84 +111,26 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
);
|
||||
|
||||
/**
|
||||
* Normed attribute names based on known LDAP environments
|
||||
* Set the user backend to be associated with this user group backend
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $normedAttributes = array(
|
||||
'uid' => 'uid',
|
||||
'gid' => 'gid',
|
||||
'user' => 'user',
|
||||
'group' => 'group',
|
||||
'member' => 'member',
|
||||
'inetorgperson' => 'inetOrgPerson',
|
||||
'samaccountname' => 'sAMAccountName'
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of this repository
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The datasource being used
|
||||
*
|
||||
* @var Connection
|
||||
*/
|
||||
protected $ds;
|
||||
|
||||
/**
|
||||
* Create a new LDAP repository object
|
||||
*
|
||||
* @param Connection $ds The data source to use
|
||||
*/
|
||||
public function __construct($ds)
|
||||
{
|
||||
$this->ds = $ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this repository's name
|
||||
*
|
||||
* @param string $name
|
||||
* @param LdapUserBackend $backend
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
public function setUserBackend(LdapUserBackend $backend)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->userBackend = $backend;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this repository's name
|
||||
* Return the user backend being associated with this user group backend
|
||||
*
|
||||
* In case no name has been explicitly set yet, the class name is returned.
|
||||
*
|
||||
* @return string
|
||||
* @return LdapUserBackend
|
||||
*/
|
||||
public function getName()
|
||||
public function getUserBackend()
|
||||
{
|
||||
return $this->name;
|
||||
return $this->userBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -453,7 +401,6 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
$lastModifiedAttribute = 'modifyTimestamp';
|
||||
}
|
||||
|
||||
// TODO(jom): Fetching memberships does not work currently, we'll need some aggregate functionality!
|
||||
$columns = array(
|
||||
'group' => $this->groupNameAttribute,
|
||||
'group_name' => $this->groupNameAttribute,
|
||||
|
@ -492,13 +439,37 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
if ($this->groupClass === null) {
|
||||
throw new ProgrammingError('It is required to set the objectClass where to look for groups first');
|
||||
}
|
||||
if ($this->groupMemberAttribute === null) {
|
||||
throw new ProgrammingError('It is required to set a attribute name where to find a group\'s members first');
|
||||
}
|
||||
|
||||
return array(
|
||||
$rules = array(
|
||||
$this->groupClass => array(
|
||||
'created_at' => 'generalized_time',
|
||||
'last_modified' => 'generalized_time'
|
||||
)
|
||||
);
|
||||
if (! $this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) {
|
||||
$rules[$this->groupClass][] = 'user_name';
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the uid for the given distinguished name
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
protected function retrieveUserName($dn)
|
||||
{
|
||||
return $this->ds
|
||||
->select()
|
||||
->from('*', array($this->userNameAttribute))
|
||||
->setBase($dn)
|
||||
->fetchOne();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -524,6 +495,27 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the given column is a valid query target and return it or the actual name if it's an alias
|
||||
*
|
||||
* @param string $table The table where to look for the column or alias
|
||||
* @param string $name The name or alias of the column to validate
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
*
|
||||
* @return string The given column's name
|
||||
*
|
||||
* @throws QueryException In case the given column is not a valid query column
|
||||
*/
|
||||
public function requireQueryColumn($table, $name, RepositoryQuery $query = null)
|
||||
{
|
||||
$column = parent::requireQueryColumn($table, $name, $query);
|
||||
if ($name === 'user_name' && $query !== null) {
|
||||
$query->getQuery()->setUnfoldAttribute('user_name');
|
||||
}
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the groups the given user is a member of
|
||||
*
|
||||
|
@ -533,43 +525,37 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
*/
|
||||
public function getMemberships(User $user)
|
||||
{
|
||||
if ($this->groupClass === 'posixGroup') {
|
||||
// Posix group only uses simple user name
|
||||
$userDn = $user->getUsername();
|
||||
} else {
|
||||
// LDAP groups use the complete DN
|
||||
if (($userDn = $user->getAdditional('ldap_dn')) === null) {
|
||||
$userQuery = $this->ds
|
||||
->select()
|
||||
->from($this->userClass)
|
||||
->where($this->userNameAttribute, $user->getUsername())
|
||||
->setBase($this->userBaseDn)
|
||||
->setUsePagedResults(false);
|
||||
if ($this->userFilter) {
|
||||
$userQuery->where(new Expression($this->userFilter));
|
||||
}
|
||||
if ($this->isAmbiguous($this->groupClass, $this->groupMemberAttribute)) {
|
||||
$queryValue = $user->getUsername();
|
||||
} elseif (($queryValue = $user->getAdditional('ldap_dn')) === null) {
|
||||
$userQuery = $this->ds
|
||||
->select()
|
||||
->from($this->userClass)
|
||||
->where($this->userNameAttribute, $user->getUsername())
|
||||
->setBase($this->userBaseDn)
|
||||
->setUsePagedResults(false);
|
||||
if ($this->userFilter) {
|
||||
$userQuery->where(new Expression($this->userFilter));
|
||||
}
|
||||
|
||||
if (($userDn = $userQuery->fetchDn()) === null) {
|
||||
return array();
|
||||
}
|
||||
if (($queryValue = $userQuery->fetchDn()) === null) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
$groupQuery = $this->ds
|
||||
->select()
|
||||
->from($this->groupClass, array($this->groupNameAttribute))
|
||||
->where($this->groupMemberAttribute, $userDn)
|
||||
->where($this->groupMemberAttribute, $queryValue)
|
||||
->setBase($this->groupBaseDn);
|
||||
if ($this->groupFilter) {
|
||||
$groupQuery->where(new Expression($this->groupFilter));
|
||||
}
|
||||
|
||||
Logger::debug('Fetching groups for user %s using filter %s.', $user->getUsername(), $groupQuery->__toString());
|
||||
$groups = array();
|
||||
foreach ($groupQuery as $row) {
|
||||
$groups[] = $row->{$this->groupNameAttribute};
|
||||
}
|
||||
Logger::debug('Fetched %d groups: %s.', count($groups), join(', ', $groups));
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
@ -610,6 +596,7 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
);
|
||||
}
|
||||
|
||||
$this->setUserBackend($userBackend);
|
||||
$defaults->merge(array(
|
||||
'user_base_dn' => $userBackend->getBaseDn(),
|
||||
'user_class' => $userBackend->getUserClass(),
|
||||
|
@ -661,4 +648,4 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
|
|||
'group_member_attribute' => 'member'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,7 +80,7 @@ class ArrayDatasource implements Selectable
|
|||
*/
|
||||
public function select()
|
||||
{
|
||||
return new SimpleQuery($this);
|
||||
return new SimpleQuery(clone $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -97,18 +97,41 @@ class FilterExpression extends Filter
|
|||
|
||||
public function matches($row)
|
||||
{
|
||||
if (! isset($row->{$this->column})) {
|
||||
// TODO: REALLY? Exception?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($this->expression)) {
|
||||
return in_array($row->{$this->column}, $this->expression);
|
||||
} elseif (strpos($this->expression, '*') === false) {
|
||||
return (string) $row->{$this->column} === (string) $this->expression;
|
||||
} else {
|
||||
$parts = preg_split('~\*~', $this->expression);
|
||||
foreach ($parts as & $part) {
|
||||
$part = preg_quote($part);
|
||||
}
|
||||
$pattern = '/^' . implode('.*', $parts) . '$/';
|
||||
return (bool) preg_match($pattern, $row->{$this->column});
|
||||
}
|
||||
|
||||
$expression = (string) $this->expression;
|
||||
if (strpos($expression, '*') === false) {
|
||||
if (is_array($row->{$this->column})) {
|
||||
return in_array($expression, $row->{$this->column});
|
||||
}
|
||||
|
||||
return (string) $row->{$this->column} === $expression;
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
foreach (preg_split('~\*~', $expression) as $part) {
|
||||
$parts[] = preg_quote($part);
|
||||
}
|
||||
$pattern = '/^' . implode('.*', $parts) . '$/';
|
||||
|
||||
if (is_array($row->{$this->column})) {
|
||||
foreach ($row->{$this->column} as $candidate) {
|
||||
if (preg_match($pattern, $candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) preg_match($pattern, $row->{$this->column});
|
||||
}
|
||||
|
||||
public function andFilter(Filter $filter)
|
||||
|
|
|
@ -5,21 +5,4 @@ namespace Icinga\Data\Filter;
|
|||
|
||||
class FilterMatch extends FilterExpression
|
||||
{
|
||||
public function matches($row)
|
||||
{
|
||||
if (! isset($row->{$this->column})) {
|
||||
// TODO: REALLY? Exception?
|
||||
return false;
|
||||
}
|
||||
$expression = (string) $this->expression;
|
||||
if (strpos($expression, '*') === false) {
|
||||
return (string) $row->{$this->column} === $expression;
|
||||
} else {
|
||||
$parts = array();
|
||||
foreach (preg_split('/\*/', $expression) as $part) {
|
||||
$parts[] = preg_quote($part);
|
||||
}
|
||||
return preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,6 @@ class FilterMatchNot extends FilterExpression
|
|||
{
|
||||
public function matches($row)
|
||||
{
|
||||
$expression = (string) $this->expression;
|
||||
if (strpos($expression, '*') === false) {
|
||||
return (string) $row->{$this->column} !== $expression;
|
||||
} else {
|
||||
$parts = array();
|
||||
foreach (preg_split('/\*/', $expression) as $part) {
|
||||
$parts[] = preg_quote($part);
|
||||
}
|
||||
return ! preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
|
||||
}
|
||||
return !parent::matches($row);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,4 +115,18 @@ class Document
|
|||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert $this to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$a = array();
|
||||
foreach ($this->sections as $section) {
|
||||
$a[$section->getName()] = $section->toArray();
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,4 +169,18 @@ class Section
|
|||
$str = str_replace(';', '\\;', $str);
|
||||
return str_replace(PHP_EOL, ' ', $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert $this to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$a = array();
|
||||
foreach ($this->directives as $directive) {
|
||||
$a[$directive->getKey()] = $directive->getValue();
|
||||
}
|
||||
return $a;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ use Icinga\File\Ini\Dom\Document;
|
|||
use Icinga\File\Ini\Dom\Directive;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Application\Config;
|
||||
|
||||
class IniParser
|
||||
{
|
||||
|
@ -239,4 +241,25 @@ class IniParser
|
|||
}
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the ini file and parse it with ::parseIni()
|
||||
*
|
||||
* @param string $file The ini file to read
|
||||
*
|
||||
* @return Config
|
||||
* @throws NotReadableError When the file cannot be read
|
||||
*/
|
||||
public static function parseIniFile($file)
|
||||
{
|
||||
if (($path = realpath($file)) === false) {
|
||||
throw new NotReadableError('Couldn\'t compute the absolute path of `%s\'', $file);
|
||||
}
|
||||
|
||||
if (($content = file_get_contents($path)) === false) {
|
||||
throw new NotReadableError('Couldn\'t read the file `%s\'', $path);
|
||||
}
|
||||
|
||||
return Config::fromArray(self::parseIni($content)->toArray())->setConfigFile($file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,9 +358,25 @@ class LdapConnection implements Selectable, Inspectable
|
|||
*/
|
||||
public function count(LdapQuery $query)
|
||||
{
|
||||
$ds = $this->getConnection();
|
||||
$this->bind();
|
||||
|
||||
if (($unfoldAttribute = $query->getUnfoldAttribute()) !== null) {
|
||||
$desiredColumns = $query->getColumns();
|
||||
if (isset($desiredColumns[$unfoldAttribute])) {
|
||||
$fields = array($unfoldAttribute => $desiredColumns[$unfoldAttribute]);
|
||||
} elseif (in_array($unfoldAttribute, $desiredColumns, true)) {
|
||||
$fields = array($unfoldAttribute);
|
||||
} else {
|
||||
throw new ProgrammingError(
|
||||
'The attribute used to unfold a query\'s result must be selected'
|
||||
);
|
||||
}
|
||||
|
||||
$res = $this->runQuery($query, $fields);
|
||||
return count($res);
|
||||
}
|
||||
|
||||
$ds = $this->getConnection();
|
||||
$results = @ldap_search(
|
||||
$ds,
|
||||
$query->getBase() ?: $this->getDn(),
|
||||
|
@ -658,7 +674,7 @@ class LdapConnection implements Selectable, Inspectable
|
|||
protected function runQuery(LdapQuery $query, array $fields = null)
|
||||
{
|
||||
$limit = $query->getLimit();
|
||||
$offset = $query->hasOffset() ? $query->getOffset() - 1 : 0;
|
||||
$offset = $query->hasOffset() ? $query->getOffset() : 0;
|
||||
|
||||
if ($fields === null) {
|
||||
$fields = $query->getColumns();
|
||||
|
@ -711,13 +727,41 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$count = 0;
|
||||
$entries = array();
|
||||
$entry = ldap_first_entry($ds, $results);
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
do {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
if ($unfoldAttribute) {
|
||||
$rows = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
array_flip($fields),
|
||||
$unfoldAttribute
|
||||
);
|
||||
|
||||
if (is_array($rows)) {
|
||||
// TODO: Register the DN the same way as a section name in the ArrayDatasource!
|
||||
foreach ($rows as $row) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $rows;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
);
|
||||
}
|
||||
}
|
||||
} while ((! $serverSorting || $limit === 0 || $limit !== count($entries))
|
||||
&& ($entry = ldap_next_entry($ds, $entry))
|
||||
|
@ -754,7 +798,7 @@ class LdapConnection implements Selectable, Inspectable
|
|||
}
|
||||
|
||||
$limit = $query->getLimit();
|
||||
$offset = $query->hasOffset() ? $query->getOffset() - 1 : 0;
|
||||
$offset = $query->hasOffset() ? $query->getOffset() : 0;
|
||||
$queryString = (string) $query;
|
||||
$base = $query->getBase() ?: $this->rootDn;
|
||||
|
||||
|
@ -776,6 +820,7 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$count = 0;
|
||||
$cookie = '';
|
||||
$entries = array();
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
do {
|
||||
// Do not request the pagination control as a critical extension, as we want the
|
||||
// server to return results even if the paged search request cannot be satisfied
|
||||
|
@ -826,12 +871,39 @@ class LdapConnection implements Selectable, Inspectable
|
|||
|
||||
$entry = ldap_first_entry($ds, $results);
|
||||
do {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
if ($unfoldAttribute) {
|
||||
$rows = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
array_flip($fields),
|
||||
$unfoldAttribute
|
||||
);
|
||||
|
||||
if (is_array($rows)) {
|
||||
// TODO: Register the DN the same way as a section name in the ArrayDatasource!
|
||||
foreach ($rows as $row) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $rows;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
);
|
||||
}
|
||||
}
|
||||
} while (
|
||||
(! $serverSorting || $limit === 0 || $limit !== count($entries))
|
||||
|
@ -861,9 +933,6 @@ class LdapConnection implements Selectable, Inspectable
|
|||
// the server: https://www.ietf.org/rfc/rfc2696.txt
|
||||
ldap_control_paged_result($ds, 0, false, $cookie);
|
||||
ldap_search($ds, $base, $queryString); // Returns no entries, due to the page size
|
||||
} else {
|
||||
// Reset the paged search request so that subsequent requests succeed
|
||||
ldap_control_paged_result($ds, 0);
|
||||
}
|
||||
|
||||
if (! $serverSorting && $query->hasOrder()) {
|
||||
|
@ -879,14 +948,16 @@ class LdapConnection implements Selectable, Inspectable
|
|||
/**
|
||||
* Clean up the given attributes and return them as simple object
|
||||
*
|
||||
* Applies column aliases, aggregates multi-value attributes as array and sets null for each missing attribute.
|
||||
* Applies column aliases, aggregates/unfolds multi-value attributes
|
||||
* as array and sets null for each missing attribute.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param array $requestedFields
|
||||
* @param string $unfoldAttribute
|
||||
*
|
||||
* @return object
|
||||
* @return object|array An array in case the object has been unfolded
|
||||
*/
|
||||
public function cleanupAttributes($attributes, array $requestedFields)
|
||||
public function cleanupAttributes($attributes, array $requestedFields, $unfoldAttribute = null)
|
||||
{
|
||||
// In case the result contains attributes with a differing case than the requested fields, it is
|
||||
// necessary to create another array to map attributes case insensitively to their requested counterparts.
|
||||
|
@ -927,6 +998,24 @@ class LdapConnection implements Selectable, Inspectable
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$unfoldAttribute !== null
|
||||
&& isset($cleanedAttributes[$unfoldAttribute])
|
||||
&& is_array($cleanedAttributes[$unfoldAttribute])
|
||||
) {
|
||||
$values = $cleanedAttributes[$unfoldAttribute];
|
||||
unset($cleanedAttributes[$unfoldAttribute]);
|
||||
$baseRow = (object) $cleanedAttributes;
|
||||
$rows = array();
|
||||
foreach ($values as $value) {
|
||||
$row = clone $baseRow;
|
||||
$row->{$unfoldAttribute} = $value;
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
return (object) $cleanedAttributes;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,13 @@ class LdapQuery extends SimpleQuery
|
|||
*/
|
||||
protected $usePagedResults;
|
||||
|
||||
/**
|
||||
* The name of the attribute used to unfold the result
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $unfoldAttribute;
|
||||
|
||||
/**
|
||||
* Initialize this query
|
||||
*/
|
||||
|
@ -90,6 +97,29 @@ class LdapQuery extends SimpleQuery
|
|||
return $this->usePagedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute to be used to unfold the result
|
||||
*
|
||||
* @param string $attributeName
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUnfoldAttribute($attributeName)
|
||||
{
|
||||
$this->unfoldAttribute = $attributeName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the attribute to use to unfold the result
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUnfoldAttribute()
|
||||
{
|
||||
return $this->unfoldAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an objectClass and the columns you are interested in
|
||||
*
|
||||
|
|
|
@ -28,13 +28,27 @@ abstract class LdapRepository extends Repository
|
|||
* @var array
|
||||
*/
|
||||
protected $normedAttributes = array(
|
||||
'uid' => 'uid',
|
||||
'gid' => 'gid',
|
||||
'user' => 'user',
|
||||
'group' => 'group',
|
||||
'member' => 'member',
|
||||
'inetorgperson' => 'inetOrgPerson',
|
||||
'samaccountname' => 'sAMAccountName'
|
||||
'uid' => 'uid',
|
||||
'gid' => 'gid',
|
||||
'user' => 'user',
|
||||
'group' => 'group',
|
||||
'member' => 'member',
|
||||
'memberuid' => 'memberUid',
|
||||
'posixgroup' => 'posixGroup',
|
||||
'uniquemember' => 'uniqueMember',
|
||||
'groupofnames' => 'groupOfNames',
|
||||
'inetorgperson' => 'inetOrgPerson',
|
||||
'samaccountname' => 'sAMAccountName',
|
||||
'groupofuniquenames' => 'groupOfUniqueNames'
|
||||
);
|
||||
|
||||
/**
|
||||
* Object attributes whose value is not distinguished name
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $ambiguousAttributes = array(
|
||||
'posixGroup' => 'memberUid'
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -63,4 +77,17 @@ abstract class LdapRepository extends Repository
|
|||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given object attribute's value is not a distinguished name
|
||||
*
|
||||
* @param string $objectClass
|
||||
* @param string $attributeName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAmbiguous($objectClass, $attributeName)
|
||||
{
|
||||
return isset($this->ambiguousAttributes[$objectClass][$attributeName]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ namespace Icinga;
|
|||
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\User\Preferences;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
|
||||
/**
|
||||
* This class represents an authorized user
|
||||
|
@ -476,4 +478,39 @@ class User
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return this user's configured navigation of the given type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getNavigation($type)
|
||||
{
|
||||
$config = Config::navigation($type === 'dashboard-pane' ? 'dashlet' : $type, $this->getUsername());
|
||||
|
||||
if ($type === 'dashboard-pane') {
|
||||
$panes = array();
|
||||
foreach ($config as $dashletName => $dashletConfig) {
|
||||
// TODO: Throw ConfigurationError if pane or url is missing
|
||||
$panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url;
|
||||
}
|
||||
|
||||
$navigation = new Navigation();
|
||||
foreach ($panes as $paneName => $dashlets) {
|
||||
$navigation->addItem(
|
||||
$paneName,
|
||||
array(
|
||||
'type' => 'dashboard-pane',
|
||||
'dashlets' => $dashlets
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$navigation = Navigation::fromConfig($config);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use Icinga\Exception\NotReadableError;
|
|||
use Icinga\Exception\NotWritableError;
|
||||
use Icinga\User\Preferences;
|
||||
use Icinga\User\Preferences\PreferencesStore;
|
||||
use Icinga\File\Ini\IniParser;
|
||||
|
||||
/**
|
||||
* Load and save user preferences from and to INI files
|
||||
|
@ -34,7 +35,7 @@ class IniStore extends PreferencesStore
|
|||
protected function init()
|
||||
{
|
||||
$this->preferencesFile = sprintf(
|
||||
'%s/%s.ini',
|
||||
'%s/%s/config.ini',
|
||||
$this->getStoreConfig()->location,
|
||||
strtolower($this->getUser()->getUsername())
|
||||
);
|
||||
|
@ -57,7 +58,7 @@ class IniStore extends PreferencesStore
|
|||
$this->getUser()->getUsername()
|
||||
);
|
||||
} else {
|
||||
$this->preferences = parse_ini_file($this->preferencesFile, true);
|
||||
$this->preferences = IniParser::parseIniFile($this->preferencesFile)->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class String
|
|||
/**
|
||||
* Uppercase the first character of each word in a string
|
||||
*
|
||||
* Converts 'first_name' to 'firstName' for example.
|
||||
* Converts 'first_name' to 'FirstName' for example.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $separator Word separator
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
class FileCache
|
||||
|
@ -191,7 +192,7 @@ class FileCache
|
|||
* Whether the given ETag matchesspecific file(s) on disk
|
||||
*
|
||||
* If no ETag is given we'll try to fetch the one from the current
|
||||
* HTTP request.
|
||||
* HTTP request. Respects HTTP Cache-Control: no-cache, if set.
|
||||
*
|
||||
* @param string|array $files file(s) to check
|
||||
* @param string $match ETag to match against
|
||||
|
@ -208,6 +209,9 @@ class FileCache
|
|||
if (! $match) {
|
||||
return false;
|
||||
}
|
||||
if (isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] === 'no-cache') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$etag = self::etagForFiles($files);
|
||||
return $match === $etag ? $etag : false;
|
||||
|
|
|
@ -65,6 +65,15 @@ class Form extends Zend_Form
|
|||
*/
|
||||
protected $created = false;
|
||||
|
||||
/**
|
||||
* This form's parent
|
||||
*
|
||||
* Gets automatically set upon calling addSubForm().
|
||||
*
|
||||
* @var Form
|
||||
*/
|
||||
protected $_parent;
|
||||
|
||||
/**
|
||||
* Whether the form is an API target
|
||||
*
|
||||
|
@ -243,6 +252,29 @@ class Form extends Zend_Form
|
|||
parent::__construct($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this form's parent
|
||||
*
|
||||
* @param Form $form
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(Form $form)
|
||||
{
|
||||
$this->_parent = $form;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this form's parent
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that is called instead of this form's onSuccess method
|
||||
*
|
||||
|
@ -844,6 +876,7 @@ class Form extends Zend_Form
|
|||
$form->setSubmitLabel('');
|
||||
$form->setTokenDisabled();
|
||||
$form->setUidDisabled();
|
||||
$form->setParent($this);
|
||||
}
|
||||
|
||||
if ($name === null) {
|
||||
|
|
|
@ -27,7 +27,8 @@ class JavaScript
|
|||
'js/icinga/behavior/tristate.js',
|
||||
'js/icinga/behavior/navigation.js',
|
||||
'js/icinga/behavior/form.js',
|
||||
'js/icinga/behavior/actiontable.js'
|
||||
'js/icinga/behavior/actiontable.js',
|
||||
'js/icinga/behavior/selectable.js'
|
||||
);
|
||||
|
||||
protected static $vendorFiles = array(
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation;
|
||||
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* A dashboard pane
|
||||
*/
|
||||
class DashboardPane extends NavigationItem
|
||||
{
|
||||
/**
|
||||
* This pane's dashlets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dashlets;
|
||||
|
||||
/**
|
||||
* Set this pane's dashlets
|
||||
*
|
||||
* @param array $dashlets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDashlets(array $dashlets)
|
||||
{
|
||||
$this->dashlets = $dashlets;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this pane's dashlets
|
||||
*
|
||||
* @param bool $ordered Whether to order the dashlets first
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDashlets($ordered = true)
|
||||
{
|
||||
if ($this->dashlets === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($ordered) {
|
||||
ksort($this->dashlets);
|
||||
}
|
||||
|
||||
return $this->dashlets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->setUrl(Url::fromPath('dashboard', array('pane' => $this->getName())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function merge(NavigationItem $item)
|
||||
{
|
||||
parent::merge($item);
|
||||
|
||||
$this->setDashlets(array_merge(
|
||||
$this->getDashlets(false),
|
||||
$item->getDashlets(false)
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation;
|
||||
|
||||
/**
|
||||
* Dropdown navigation item
|
||||
*
|
||||
* @see \Icinga\Web\Navigation\Navigation For a usage example.
|
||||
*/
|
||||
class DropdownItem extends NavigationItem
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->children->setLayout(Navigation::LAYOUT_DROPDOWN);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,587 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Exception;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Util\String;
|
||||
use Icinga\Web\Navigation\Renderer\RecursiveNavigationRenderer;
|
||||
|
||||
/**
|
||||
* Container for navigation items
|
||||
*/
|
||||
class Navigation implements ArrayAccess, Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* The class namespace where to locate navigation type classes
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NAVIGATION_NS = 'Web\\Navigation';
|
||||
|
||||
/**
|
||||
* Flag for dropdown layout
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const LAYOUT_DROPDOWN = 1;
|
||||
|
||||
/**
|
||||
* Flag for tabs layout
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const LAYOUT_TABS = 2;
|
||||
|
||||
/**
|
||||
* Known navigation types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $types;
|
||||
|
||||
/**
|
||||
* This navigation's items
|
||||
*
|
||||
* @var NavigationItem[]
|
||||
*/
|
||||
protected $items = array();
|
||||
|
||||
/**
|
||||
* This navigation's layout
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $layout;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return isset($this->items[$offset]) ? $this->items[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$this->order();
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new navigation item for the given configuration
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|ConfigObject $properties
|
||||
*
|
||||
* @return NavigationItem
|
||||
*
|
||||
* @throws InvalidArgumentException If the $properties argument is neither an array nor a ConfigObject
|
||||
*/
|
||||
public function createItem($name, $properties)
|
||||
{
|
||||
if ($properties instanceof ConfigObject) {
|
||||
$properties = $properties->toArray();
|
||||
} elseif (! is_array($properties)) {
|
||||
throw new InvalidArgumentException('Argument $properties must be of type array or ConfigObject');
|
||||
}
|
||||
|
||||
$itemType = isset($properties['type']) ? String::cname($properties['type'], '-') : 'NavigationItem';
|
||||
if (! empty(static::$types) && isset(static::$types[$itemType])) {
|
||||
return new static::$types[$itemType]($name, $properties);
|
||||
}
|
||||
|
||||
$item = null;
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
|
||||
$classPath = 'Icinga\\Module\\'
|
||||
. ucfirst($module->getName())
|
||||
. '\\'
|
||||
. static::NAVIGATION_NS
|
||||
. '\\'
|
||||
. $itemType;
|
||||
if (class_exists($classPath)) {
|
||||
$item = new $classPath($name, $properties);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($item === null) {
|
||||
$classPath = 'Icinga\\' . static::NAVIGATION_NS . '\\' . $itemType;
|
||||
if (class_exists($classPath)) {
|
||||
$item = new $classPath($name, $properties);
|
||||
}
|
||||
}
|
||||
|
||||
if ($item === null) {
|
||||
Logger::debug(
|
||||
'Failed to find custom navigation item class %s for item %s. Using base class NavigationItem now',
|
||||
$itemType,
|
||||
$name
|
||||
);
|
||||
|
||||
$item = new NavigationItem($name, $properties);
|
||||
static::$types[$itemType] = 'Icinga\\Web\\Navigation\\NavigationItem';
|
||||
} elseif (! $item instanceof NavigationItem) {
|
||||
throw new ProgrammingError('Class %s must inherit from NavigationItem', $classPath);
|
||||
} else {
|
||||
static::$types[$itemType] = $classPath;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a navigation item
|
||||
*
|
||||
* If you do not pass an instance of NavigationItem, this will only add the item
|
||||
* if it does not require a permission or the current user has the permission.
|
||||
*
|
||||
* @param string|NavigationItem $name The name of the item or an instance of NavigationItem
|
||||
* @param array $properties The properties of the item to add (Ignored if $name is not a string)
|
||||
*
|
||||
* @return bool Whether the item was added or not
|
||||
*
|
||||
* @throws InvalidArgumentException In case $name is neither a string nor an instance of NavigationItem
|
||||
*/
|
||||
public function addItem($name, array $properties = array())
|
||||
{
|
||||
if (is_string($name)) {
|
||||
if (isset($properties['permission'])) {
|
||||
if (! Auth::getInstance()->hasPermission($properties['permission'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($properties['permission']);
|
||||
}
|
||||
|
||||
$item = $this->createItem($name, $properties);
|
||||
} elseif (! $name instanceof NavigationItem) {
|
||||
throw new InvalidArgumentException('Argument $name must be of type string or NavigationItem');
|
||||
} else {
|
||||
$item = $name;
|
||||
}
|
||||
|
||||
$this->items[$item->getName()] = $item;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the item with the given name
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return NavigationItem|mixed
|
||||
*/
|
||||
public function getItem($name, $default = null)
|
||||
{
|
||||
return isset($this->items[$name]) ? $this->items[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently active item or the first one if none is active
|
||||
*
|
||||
* @return NavigationItem
|
||||
*/
|
||||
public function getActiveItem()
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($item->getActive()) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
$firstItem = reset($this->items);
|
||||
return $firstItem ? $firstItem->setActive() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this navigation's items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getItems()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this navigation is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this navigation has any renderable items
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRenderableItems()
|
||||
{
|
||||
foreach ($this->getItems() as $item) {
|
||||
if ($item->shouldRender()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this navigation's layout
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLayout()
|
||||
{
|
||||
return $this->layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this navigation's layout
|
||||
*
|
||||
* @param int $layout
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLayout($layout)
|
||||
{
|
||||
$this->layout = (int) $layout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the renderer for this navigation
|
||||
*
|
||||
* @return RecursiveNavigationRenderer
|
||||
*/
|
||||
public function getRenderer()
|
||||
{
|
||||
return new RecursiveNavigationRenderer($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this navigation rendered to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return $this->getRenderer()->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Order this navigation's items
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order()
|
||||
{
|
||||
uasort($this->items, array($this, 'compareItems'));
|
||||
foreach ($this->items as $item) {
|
||||
if ($item->hasChildren()) {
|
||||
$item->getChildren()->order();
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the first item is less than, more than or equal to the second one
|
||||
*
|
||||
* @param NavigationItem $a
|
||||
* @param NavigationItem $b
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function compareItems(NavigationItem $a, NavigationItem $b)
|
||||
{
|
||||
if ($a->getPriority() === $b->getPriority()) {
|
||||
return strcasecmp($a->getLabel(), $b->getLabel());
|
||||
}
|
||||
|
||||
return $a->getPriority() > $b->getPriority() ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find and return a item with the given or a similar name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return NavigationItem
|
||||
*/
|
||||
protected function findItem($name)
|
||||
{
|
||||
$item = $this->getItem($name);
|
||||
if ($item !== null) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
$loweredName = strtolower($name);
|
||||
foreach ($this->getItems() as $item) {
|
||||
if (strtolower($item->getName()) === $loweredName) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this navigation with the given one
|
||||
*
|
||||
* Any duplicate items of this navigation will be overwritten by the given navigation's items.
|
||||
*
|
||||
* @param Navigation $navigation
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(Navigation $navigation)
|
||||
{
|
||||
foreach ($navigation as $item) {
|
||||
/** @var $item NavigationItem */
|
||||
if (($existingItem = $this->findItem($item->getName())) !== null) {
|
||||
if ($existingItem->conflictsWith($item)) {
|
||||
$name = $item->getName();
|
||||
do {
|
||||
if (preg_match('~_(\d+)$~', $name, $matches)) {
|
||||
$name = preg_replace('~_\d+$~', $matches[1] + 1, $name);
|
||||
} else {
|
||||
$name .= '_2';
|
||||
}
|
||||
} while ($this->getItem($name) !== null);
|
||||
|
||||
$this->addItem($item->setName($name));
|
||||
} else {
|
||||
$existingItem->merge($item);
|
||||
}
|
||||
} else {
|
||||
$this->addItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this navigation set with all additional items of the given type
|
||||
*
|
||||
* This will fetch navigation items from the following sources:
|
||||
* * User Shareables
|
||||
* * User Preferences
|
||||
* * Modules
|
||||
* Any existing entry will be overwritten by one that is coming later in order.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function load($type)
|
||||
{
|
||||
$user = Auth::getInstance()->getUser();
|
||||
if ($type !== 'dashboard-pane') {
|
||||
// Shareables
|
||||
$this->merge(Icinga::app()->getSharedNavigation($type));
|
||||
|
||||
// User Preferences
|
||||
$this->merge($user->getNavigation($type));
|
||||
}
|
||||
|
||||
// Modules
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
foreach ($moduleManager->getLoadedModules() as $module) {
|
||||
if ($user->can($moduleManager::MODULE_PERMISSION_NS . $module->getName())) {
|
||||
if ($type === 'menu-item') {
|
||||
$this->merge($module->getMenu());
|
||||
} elseif ($type === 'dashboard-pane') {
|
||||
$this->merge($module->getDashboard());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the global navigation item type configuration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getItemTypeConfiguration()
|
||||
{
|
||||
$defaultItemTypes = array(
|
||||
'menu-item' => array(
|
||||
'label' => t('Menu Entry'),
|
||||
'config' => 'menu'
|
||||
)/*, // Disabled, until it is able to fully replace the old implementation
|
||||
'dashlet' => array(
|
||||
'label' => 'Dashlet',
|
||||
'config' => 'dashboard'
|
||||
)*/
|
||||
);
|
||||
|
||||
$moduleItemTypes = array();
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
foreach ($moduleManager->getLoadedModules() as $module) {
|
||||
if (Auth::getInstance()->hasPermission($moduleManager::MODULE_PERMISSION_NS . $module->getName())) {
|
||||
foreach ($module->getNavigationItems() as $type => $options) {
|
||||
if (! isset($moduleItemTypes[$type])) {
|
||||
$moduleItemTypes[$type] = $options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($defaultItemTypes, $moduleItemTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new set of navigation items for the given configuration
|
||||
*
|
||||
* Note that this is supposed to be utilized for one dimensional structures
|
||||
* only. Multi dimensional structures can be processed by fromArray().
|
||||
*
|
||||
* @param Traversable|array $config
|
||||
*
|
||||
* @return Navigation
|
||||
*
|
||||
* @throws InvalidArgumentException In case the given configuration is invalid
|
||||
* @throws ConfigurationError In case a referenced parent does not exist
|
||||
*/
|
||||
public static function fromConfig($config)
|
||||
{
|
||||
if (! is_array($config) && !$config instanceof Traversable) {
|
||||
throw new InvalidArgumentException('Argument $config must be an array or a instance of Traversable');
|
||||
}
|
||||
|
||||
$flattened = $orphans = $topLevel = array();
|
||||
foreach ($config as $sectionName => $sectionConfig) {
|
||||
$parentName = $sectionConfig->parent;
|
||||
unset($sectionConfig->parent);
|
||||
|
||||
if (! $parentName) {
|
||||
$topLevel[$sectionName] = $sectionConfig->toArray();
|
||||
$flattened[$sectionName] = & $topLevel[$sectionName];
|
||||
} elseif (isset($flattened[$parentName])) {
|
||||
$flattened[$parentName]['children'][$sectionName] = $sectionConfig->toArray();
|
||||
$flattened[$sectionName] = & $flattened[$parentName]['children'][$sectionName];
|
||||
} else {
|
||||
$orphans[$parentName][$sectionName] = $sectionConfig->toArray();
|
||||
$flattened[$sectionName] = & $orphans[$parentName][$sectionName];
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
$match = false;
|
||||
foreach ($orphans as $parentName => $children) {
|
||||
if (isset($flattened[$parentName])) {
|
||||
if (isset($flattened[$parentName]['children'])) {
|
||||
$flattened[$parentName]['children'] = array_merge(
|
||||
$flattened[$parentName]['children'],
|
||||
$children
|
||||
);
|
||||
} else {
|
||||
$flattened[$parentName]['children'] = $children;
|
||||
}
|
||||
|
||||
unset($orphans[$parentName]);
|
||||
$match = true;
|
||||
}
|
||||
}
|
||||
} while ($match && !empty($orphans));
|
||||
|
||||
if (! empty($orphans)) {
|
||||
throw new ConfigurationError(
|
||||
t(
|
||||
'Failed to fully parse navigation configuration. Ensure that'
|
||||
. ' all referenced parents are existing navigation items: %s'
|
||||
),
|
||||
join(', ', array_keys($orphans))
|
||||
);
|
||||
}
|
||||
|
||||
return static::fromArray($topLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new set of navigation items for the given array
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
public static function fromArray(array $array)
|
||||
{
|
||||
$navigation = new static();
|
||||
foreach ($array as $name => $properties) {
|
||||
$navigation->addItem($name, $properties);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this navigation rendered to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,791 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Web\Navigation\Renderer\NavigationItemRenderer;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* A navigation item
|
||||
*/
|
||||
class NavigationItem implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Alternative markup element for items without a url
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const LINK_ALTERNATIVE = 'span';
|
||||
|
||||
/**
|
||||
* The class namespace where to locate navigation type renderer classes
|
||||
*/
|
||||
const RENDERER_NS = 'Web\\Navigation\\Renderer';
|
||||
|
||||
/**
|
||||
* Whether this item is active
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $active;
|
||||
|
||||
/**
|
||||
* This item's priority
|
||||
*
|
||||
* The priority defines when the item is rendered in relation to its parent's childs.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority;
|
||||
|
||||
/**
|
||||
* The attributes of this item's element
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* This item's children
|
||||
*
|
||||
* @var Navigation
|
||||
*/
|
||||
protected $children;
|
||||
|
||||
/**
|
||||
* This item's icon
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $icon;
|
||||
|
||||
/**
|
||||
* This item's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* This item's label
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* This item's parent
|
||||
*
|
||||
* @var NavigationItem
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* This item's url
|
||||
*
|
||||
* @var Url
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* This item's url target
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target;
|
||||
|
||||
/**
|
||||
* Additional parameters for this item's url
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $urlParameters;
|
||||
|
||||
/**
|
||||
* This item's renderer
|
||||
*
|
||||
* @var NavigationItemRenderer
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* Whether to render this item
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $render;
|
||||
|
||||
/**
|
||||
* Create a new NavigationItem
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $properties
|
||||
*/
|
||||
public function __construct($name, array $properties = null)
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->priority = 100;
|
||||
$this->children = new Navigation();
|
||||
|
||||
if (! empty($properties)) {
|
||||
$this->setProperties($properties);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this NavigationItem
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return $this->getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this item is active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getActive()
|
||||
{
|
||||
if ($this->active === null) {
|
||||
$this->active = false;
|
||||
if ($this->getUrl() !== null && Icinga::app()->getRequest()->getUrl()->matches($this->getUrl())) {
|
||||
$this->setActive();
|
||||
} elseif ($this->hasChildren()) {
|
||||
foreach ($this->getChildren() as $item) {
|
||||
/** @var NavigationItem $item */
|
||||
if ($item->getActive()) {
|
||||
// Do nothing, a true active state is automatically passed to all parents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this item is active
|
||||
*
|
||||
* If it's active and has a parent, the parent gets activated as well.
|
||||
*
|
||||
* @param bool $active
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setActive($active = true)
|
||||
{
|
||||
$this->active = (bool) $active;
|
||||
if ($this->active && $this->getParent() !== null) {
|
||||
$this->getParent()->setActive();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's priority
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPriority()
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's priority
|
||||
*
|
||||
* @param int $priority
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPriority($priority)
|
||||
{
|
||||
$this->priority = (int) $priority;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the given element attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($name, $default = null)
|
||||
{
|
||||
$attributes = $this->getAttributes();
|
||||
return array_key_exists($name, $attributes) ? $attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the given element attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the attributes of this item's element
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->attributes ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attributes of this item's element
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttributes(array $attributes)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child to this item
|
||||
*
|
||||
* If the child is active this item gets activated as well.
|
||||
*
|
||||
* @param NavigationItem $child
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addChild(NavigationItem $child)
|
||||
{
|
||||
$this->getChildren()->addItem($child->setParent($this));
|
||||
if ($child->getActive()) {
|
||||
$this->setActive();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's children
|
||||
*
|
||||
* @return Navigation
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this item has any children
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
return !$this->getChildren()->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's children
|
||||
*
|
||||
* @param array|Navigation $children
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChildren($children)
|
||||
{
|
||||
if (is_array($children)) {
|
||||
$children = Navigation::fromArray($children);
|
||||
} elseif (! $children instanceof Navigation) {
|
||||
throw new InvalidArgumentException('Argument $children must be of type array or Navigation');
|
||||
}
|
||||
|
||||
foreach ($children as $item) {
|
||||
$item->setParent($this);
|
||||
}
|
||||
|
||||
$this->children = $children;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's icon
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIcon()
|
||||
{
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's icon
|
||||
*
|
||||
* @param string $icon
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIcon($icon)
|
||||
{
|
||||
$this->icon = $icon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's name escaped with only ASCII chars and/or digits
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEscapedName()
|
||||
{
|
||||
return preg_replace('~[^a-zA-Z0-9]~', '_', $this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique version of this item's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUniqueName()
|
||||
{
|
||||
if ($this->getParent() === null) {
|
||||
return 'navigation-' . $this->getEscapedName();
|
||||
}
|
||||
|
||||
return $this->getParent()->getUniqueName() . '-' . $this->getEscapedName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's parent
|
||||
*
|
||||
* @param NavigationItem $parent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(NavigationItem $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's parent
|
||||
*
|
||||
* @return NavigationItem
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's label
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label ?: $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's label
|
||||
*
|
||||
* @param string $label
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's url target
|
||||
*
|
||||
* @param string $target
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTarget($target)
|
||||
{
|
||||
$this->target = $target;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's url target
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTarget()
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's url
|
||||
*
|
||||
* @return Url
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
if ($this->url === null && $this->hasChildren()) {
|
||||
$this->setUrl(Url::fromPath('#'));
|
||||
}
|
||||
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's url
|
||||
*
|
||||
* @param Url|string $url
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException If the given url is neither of type
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
if (is_string($url)) {
|
||||
$url = Url::fromPath($url);
|
||||
} elseif (! $url instanceof Url) {
|
||||
throw new InvalidArgumentException('Argument $url must be of type string or Url');
|
||||
}
|
||||
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the given url parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUrlParameter($name, $default = null)
|
||||
{
|
||||
$parameters = $this->getUrlParameters();
|
||||
return isset($parameters[$name]) ? $parameters[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the given url parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrlParameter($name, $value)
|
||||
{
|
||||
$this->urlParameters[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all additional parameters for this item's url
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUrlParameters()
|
||||
{
|
||||
return $this->urlParameters ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional parameters for this item's url
|
||||
*
|
||||
* @param array $urlParameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUrlParameters(array $urlParameters)
|
||||
{
|
||||
$this->urlParameters = $urlParameters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's properties
|
||||
*
|
||||
* Unknown properties (no matching setter) are considered as element attributes.
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperties(array $properties)
|
||||
{
|
||||
foreach ($properties as $name => $value) {
|
||||
$setter = 'set' . ucfirst($name);
|
||||
if (method_exists($this, $setter)) {
|
||||
$this->$setter($value);
|
||||
} else {
|
||||
$this->setAttribute($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this item with the given one
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(NavigationItem $item)
|
||||
{
|
||||
if ($this->conflictsWith($item)) {
|
||||
throw new ProgrammingError('Cannot merge, conflict detected.');
|
||||
}
|
||||
|
||||
if ($item->getActive()) {
|
||||
$this->setActive();
|
||||
}
|
||||
|
||||
if (! $this->getIcon()) {
|
||||
$this->setIcon($item->getIcon());
|
||||
}
|
||||
|
||||
if ($this->getLabel() === $this->getName()) {
|
||||
$this->setLabel($item->getLabel());
|
||||
}
|
||||
|
||||
foreach ($item->getAttributes() as $name => $value) {
|
||||
$this->setAttribute($name, $value);
|
||||
}
|
||||
|
||||
foreach ($item->getUrlParameters() as $name => $value) {
|
||||
$this->setUrlParameter($name, $value);
|
||||
}
|
||||
|
||||
if ($item->hasChildren()) {
|
||||
$this->getChildren()->merge($item->getChildren());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether it's possible to merge this item with the given one
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function conflictsWith(NavigationItem $item)
|
||||
{
|
||||
if (! $item instanceof $this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->getUrl() === null || $item->getUrl() === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !$this->getUrl()->matches($item->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the given renderer
|
||||
*
|
||||
* @param string|array $name
|
||||
*
|
||||
* @return NavigationItemRenderer
|
||||
*/
|
||||
protected function createRenderer($name)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$options = array_splice($name, 1);
|
||||
$name = $name[0];
|
||||
} else {
|
||||
$options = array();
|
||||
}
|
||||
|
||||
$renderer = null;
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
|
||||
$classPath = 'Icinga\\Module\\' . ucfirst($module->getName()) . '\\' . static::RENDERER_NS . '\\' . $name;
|
||||
if (class_exists($classPath)) {
|
||||
$renderer = new $classPath($options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($renderer === null) {
|
||||
$classPath = 'Icinga\\' . static::RENDERER_NS . '\\' . $name;
|
||||
if (class_exists($classPath)) {
|
||||
$renderer = new $classPath($options);
|
||||
}
|
||||
}
|
||||
|
||||
if ($renderer === null) {
|
||||
throw new ProgrammingError(
|
||||
'Cannot find renderer "%s" for navigation item "%s"',
|
||||
$name,
|
||||
$this->getName()
|
||||
);
|
||||
} elseif (! $renderer instanceof NavigationItemRenderer) {
|
||||
throw new ProgrammingError('Class %s must inherit from NavigationItemRenderer', $classPath);
|
||||
}
|
||||
|
||||
return $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this item's renderer
|
||||
*
|
||||
* @param string|array|NavigationItemRenderer $renderer
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException If the $renderer argument is neither a string nor a NavigationItemRenderer
|
||||
*/
|
||||
public function setRenderer($renderer)
|
||||
{
|
||||
if (is_string($renderer) || is_array($renderer)) {
|
||||
$renderer = $this->createRenderer($renderer);
|
||||
} elseif (! $renderer instanceof NavigationItemRenderer) {
|
||||
throw new InvalidArgumentException(
|
||||
'Argument $renderer must be of type string, array or NavigationItemRenderer'
|
||||
);
|
||||
}
|
||||
|
||||
$this->renderer = $renderer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item's renderer
|
||||
*
|
||||
* @return NavigationItemRenderer
|
||||
*/
|
||||
public function getRenderer()
|
||||
{
|
||||
if ($this->renderer === null) {
|
||||
$this->setRenderer('NavigationItemRenderer');
|
||||
}
|
||||
|
||||
return $this->renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this item should be rendered
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRender($state = true)
|
||||
{
|
||||
$this->render = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this item should be rendered
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getRender()
|
||||
{
|
||||
if ($this->render === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this item should be rendered
|
||||
*
|
||||
* Alias for NavigationItem::getRender().
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldRender()
|
||||
{
|
||||
return $this->getRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item rendered to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return $this->getRenderer()->setItem($this)->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this item rendered to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
use Icinga\Web\Navigation\NavigationItem;
|
||||
|
||||
/**
|
||||
* Abstract base class for a NavigationItem with a status badge
|
||||
*/
|
||||
abstract class BadgeNavigationItemRenderer extends NavigationItemRenderer
|
||||
{
|
||||
const STATE_OK = 'ok';
|
||||
const STATE_CRITICAL = 'critical';
|
||||
const STATE_WARNING = 'warning';
|
||||
const STATE_PENDING = 'pending';
|
||||
const STATE_UNKNOWN = 'unknown';
|
||||
|
||||
/**
|
||||
* The tooltip text for the badge
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The state identifier being used
|
||||
*
|
||||
* The state identifier defines the background color of the badge.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Set the tooltip text for the badge
|
||||
*
|
||||
* @param string $title
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tooltip text for the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state identifier to use
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
$this->state = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state identifier to use
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the amount of items represented by the badge
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
abstract public function getCount();
|
||||
|
||||
/**
|
||||
* Render the given navigation item as HTML anchor with a badge
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(NavigationItem $item = null)
|
||||
{
|
||||
return $this->renderBadge() . parent::render($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderBadge()
|
||||
{
|
||||
if (($count = $this->getCount()) > 0) {
|
||||
return sprintf(
|
||||
'<div title="%s" class="badge-container"><span class="badge badge-%s">%s</span></div>',
|
||||
$this->view()->escape($this->getTitle()),
|
||||
$this->view()->escape($this->getState()),
|
||||
$count
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Util\String;
|
||||
use Icinga\Web\Navigation\NavigationItem;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\View;
|
||||
|
||||
/**
|
||||
* NavigationItemRenderer
|
||||
*/
|
||||
class NavigationItemRenderer
|
||||
{
|
||||
/**
|
||||
* View
|
||||
*
|
||||
* @var View
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* The item being rendered
|
||||
*
|
||||
* @var NavigationItem
|
||||
*/
|
||||
protected $item;
|
||||
|
||||
/**
|
||||
* Internal link targets provided by Icinga Web 2
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $internalLinkTargets;
|
||||
|
||||
/**
|
||||
* Whether to escape the label
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeLabel;
|
||||
|
||||
/**
|
||||
* Create a new NavigationItemRenderer
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = null)
|
||||
{
|
||||
if (! empty($options)) {
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
$this->internalLinkTargets = array('_main', '_self', '_next');
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this renderer
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the given options
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
foreach ($options as $name => $value) {
|
||||
$setter = 'set' . String::cname($name);
|
||||
if (method_exists($this, $setter)) {
|
||||
$this->$setter($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view
|
||||
*
|
||||
* @param View $view
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setView(View $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the view
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function view()
|
||||
{
|
||||
if ($this->view === null) {
|
||||
$this->setView(Icinga::app()->getViewRenderer()->view);
|
||||
}
|
||||
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the navigation item to render
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setItem(NavigationItem $item)
|
||||
{
|
||||
$this->item = $item;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the navigation item being rendered
|
||||
*
|
||||
* @return NavigationItem
|
||||
*/
|
||||
public function getItem()
|
||||
{
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to escape the label
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEscapeLabel($state = true)
|
||||
{
|
||||
$this->escapeLabel = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to escape the label
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getEscapeLabel()
|
||||
{
|
||||
return $this->escapeLabel !== null ? $this->escapeLabel : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the given navigation item as HTML anchor
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(NavigationItem $item = null)
|
||||
{
|
||||
if ($item !== null) {
|
||||
$this->setItem($item);
|
||||
} elseif (($item = $this->getItem()) === null) {
|
||||
throw new ProgrammingError(
|
||||
'Cannot render nothing. Pass the item to render as part'
|
||||
. ' of the call to render() or set it with setItem()'
|
||||
);
|
||||
}
|
||||
|
||||
$label = $this->getEscapeLabel()
|
||||
? $this->view()->escape($item->getLabel())
|
||||
: $item->getLabel();
|
||||
if (($icon = $item->getIcon()) !== null) {
|
||||
$label = $this->view()->icon($icon) . $label;
|
||||
}
|
||||
|
||||
if (($url = $item->getUrl()) !== null) {
|
||||
$url->overwriteParams($item->getUrlParameters());
|
||||
|
||||
$target = $item->getTarget();
|
||||
if ($url->isExternal() && (!$target || in_array($target, $this->internalLinkTargets, true))) {
|
||||
$url = Url::fromPath('iframe', array('url' => $url));
|
||||
}
|
||||
|
||||
$content = sprintf(
|
||||
'<a%s href="%s"%s>%s</a>',
|
||||
$this->view()->propertiesToString($item->getAttributes()),
|
||||
$url,
|
||||
$this->renderTargetAttribute(),
|
||||
$label
|
||||
);
|
||||
} else {
|
||||
$content = sprintf(
|
||||
'<%1$s%2$s>%3$s</%1$s>',
|
||||
$item::LINK_ALTERNATIVE,
|
||||
$this->view()->propertiesToString($item->getAttributes()),
|
||||
$label
|
||||
);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render and return the attribute to provide a non-default target for the url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderTargetAttribute()
|
||||
{
|
||||
$target = $this->getItem()->getTarget();
|
||||
if ($target === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (! in_array($target, $this->internalLinkTargets, true)) {
|
||||
return ' target="' . $this->view()->escape($target) . '"';
|
||||
}
|
||||
|
||||
return ' data-base-target="' . $target . '"';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
use ArrayIterator;
|
||||
use Exception;
|
||||
use RecursiveIterator;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Navigation\NavigationItem;
|
||||
use Icinga\Web\View;
|
||||
|
||||
/**
|
||||
* Renderer for single level navigation
|
||||
*/
|
||||
class NavigationRenderer implements RecursiveIterator, NavigationRendererInterface
|
||||
{
|
||||
/**
|
||||
* The tag used for the outer element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $elementTag;
|
||||
|
||||
/**
|
||||
* The CSS class used for the outer element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cssClass;
|
||||
|
||||
/**
|
||||
* The navigation's heading text
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $heading;
|
||||
|
||||
/**
|
||||
* The content rendered so far
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Whether to skip rendering the outer element
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $skipOuterElement;
|
||||
|
||||
/**
|
||||
* The navigation's iterator
|
||||
*
|
||||
* @var ArrayIterator
|
||||
*/
|
||||
protected $iterator;
|
||||
|
||||
/**
|
||||
* The navigation
|
||||
*
|
||||
* @var Navigation
|
||||
*/
|
||||
protected $navigation;
|
||||
|
||||
/**
|
||||
* View
|
||||
*
|
||||
* @var View
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Create a new NavigationRenderer
|
||||
*
|
||||
* @param Navigation $navigation
|
||||
* @param bool $skipOuterElement
|
||||
*/
|
||||
public function __construct(Navigation $navigation, $skipOuterElement = false)
|
||||
{
|
||||
$this->skipOuterElement = $skipOuterElement;
|
||||
$this->iterator = $navigation->getIterator();
|
||||
$this->navigation = $navigation;
|
||||
$this->content = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setElementTag($tag)
|
||||
{
|
||||
$this->elementTag = $tag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getElementTag()
|
||||
{
|
||||
return $this->elementTag ?: static::OUTER_ELEMENT_TAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCssClass($class)
|
||||
{
|
||||
$this->cssClass = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCssClass()
|
||||
{
|
||||
return $this->cssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setHeading($heading)
|
||||
{
|
||||
$this->heading = $heading;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHeading()
|
||||
{
|
||||
return $this->heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the view
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function view()
|
||||
{
|
||||
if ($this->view === null) {
|
||||
$this->setView(Icinga::app()->getViewRenderer()->view);
|
||||
}
|
||||
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view
|
||||
*
|
||||
* @param View $view
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setView(View $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return new static($this->current()->getChildren(), $this->skipOuterElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
return $this->current()->hasChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return NavigationItem
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->iterator->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->iterator->key();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->iterator->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->iterator->rewind();
|
||||
if (! $this->skipOuterElement) {
|
||||
$this->content[] = $this->beginMarkup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$valid = $this->iterator->valid();
|
||||
if (! $this->skipOuterElement && !$valid) {
|
||||
$this->content[] = $this->endMarkup();
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the opening markup for the navigation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function beginMarkup()
|
||||
{
|
||||
$content = array();
|
||||
$content[] = sprintf(
|
||||
'<%s%s role="navigation">',
|
||||
$this->getElementTag(),
|
||||
$this->getCssClass() !== null ? ' class="' . $this->getCssClass() . '"' : ''
|
||||
);
|
||||
if (($heading = $this->getHeading()) !== null) {
|
||||
$content[] = sprintf(
|
||||
'<h%1$d id="navigation" class="sr-only" tabindex="-1">%2$s</h%1$d>',
|
||||
static::HEADING_RANK,
|
||||
$this->view()->escape($heading)
|
||||
);
|
||||
}
|
||||
$content[] = $this->beginChildrenMarkup();
|
||||
return join("\n", $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the closing markup for the navigation
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function endMarkup()
|
||||
{
|
||||
$content = array();
|
||||
$content[] = $this->endChildrenMarkup();
|
||||
$content[] = '</' . $this->getElementTag() . '>';
|
||||
return join("\n", $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the opening markup for multiple navigation items
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function beginChildrenMarkup()
|
||||
{
|
||||
$cssClass = array(static::CSS_CLASS_NAV);
|
||||
if ($this->navigation->getLayout() === Navigation::LAYOUT_TABS) {
|
||||
$cssClass[] = static::CSS_CLASS_NAV_TABS;
|
||||
} elseif ($this->navigation->getLayout() === Navigation::LAYOUT_DROPDOWN) {
|
||||
$cssClass[] = static::CSS_CLASS_NAV_DROPDOWN;
|
||||
}
|
||||
|
||||
return '<ul class="' . join(' ', $cssClass) . '">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the closing markup for multiple navigation items
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function endChildrenMarkup()
|
||||
{
|
||||
return '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the opening markup for the given navigation item
|
||||
*
|
||||
* @param NavigationItem $item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function beginItemMarkup(NavigationItem $item)
|
||||
{
|
||||
$cssClass = array(static::CSS_CLASS_ITEM);
|
||||
|
||||
if ($item->hasChildren() && $item->getChildren()->getLayout() === Navigation::LAYOUT_DROPDOWN) {
|
||||
$cssClass[] = static::CSS_CLASS_DROPDOWN;
|
||||
$item
|
||||
->setAttribute('class', static::CSS_CLASS_DROPDOWN_TOGGLE)
|
||||
->setIcon(static::DROPDOWN_TOGGLE_ICON)
|
||||
->setUrl('#');
|
||||
}
|
||||
|
||||
if ($item->getActive()) {
|
||||
$cssClass[] = static::CSS_CLASS_ACTIVE;
|
||||
}
|
||||
|
||||
$content = sprintf(
|
||||
'<li id="%s" class="%s">',
|
||||
$this->view()->escape($item->getUniqueName()),
|
||||
join(' ', $cssClass)
|
||||
);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the closing markup for a navigation item
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function endItemMarkup()
|
||||
{
|
||||
return '</li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
foreach ($this as $item) {
|
||||
/** @var NavigationItem $item */
|
||||
if ($item->shouldRender()) {
|
||||
$this->content[] = $this->beginItemMarkup($item);
|
||||
$this->content[] = $item->render();
|
||||
$this->content[] = $this->endItemMarkup();
|
||||
}
|
||||
}
|
||||
|
||||
return join("\n", $this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
/**
|
||||
* Interface for navigation renderers
|
||||
*/
|
||||
interface NavigationRendererInterface
|
||||
{
|
||||
/**
|
||||
* CSS class for items
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_ITEM = 'nav-item';
|
||||
|
||||
/**
|
||||
* CSS class for active items
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_ACTIVE = 'active';
|
||||
|
||||
/**
|
||||
* CSS class for dropdown items
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_DROPDOWN = 'dropdown-nav-item';
|
||||
|
||||
/**
|
||||
* CSS class for a dropdown item's trigger
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_DROPDOWN_TOGGLE = 'dropdown-toggle';
|
||||
|
||||
/**
|
||||
* CSS class for the ul element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_NAV = 'nav';
|
||||
|
||||
/**
|
||||
* CSS class for the ul element with dropdown layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_NAV_DROPDOWN = 'dropdown-nav';
|
||||
|
||||
/**
|
||||
* CSS class for the ul element with tabs layout
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CSS_CLASS_NAV_TABS = 'tab-nav';
|
||||
|
||||
/**
|
||||
* Icon for a dropdown item's trigger
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DROPDOWN_TOGGLE_ICON = 'menu';
|
||||
|
||||
/**
|
||||
* Default tag for the outer element the navigation will be wrapped with
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OUTER_ELEMENT_TAG = 'div';
|
||||
|
||||
/**
|
||||
* The heading's rank
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const HEADING_RANK = 1;
|
||||
|
||||
/**
|
||||
* Set the tag for the outer element the navigation is wrapped with
|
||||
*
|
||||
* @param string $tag
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setElementTag($tag);
|
||||
|
||||
/**
|
||||
* Return the tag for the outer element the navigation is wrapped with
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getElementTag();
|
||||
|
||||
/**
|
||||
* Set the CSS class to use for the outer element
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCssClass($class);
|
||||
|
||||
/**
|
||||
* Get the CSS class used for the outer element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCssClass();
|
||||
|
||||
/**
|
||||
* Set the navigation's heading text
|
||||
*
|
||||
* @param string $heading
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeading($heading);
|
||||
|
||||
/**
|
||||
* Return the navigation's heading text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeading();
|
||||
|
||||
/**
|
||||
* Return the navigation rendered to HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render();
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
use Exception;
|
||||
use RecursiveIteratorIterator;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Navigation\NavigationItem;
|
||||
|
||||
/**
|
||||
* Renderer for multi level navigation
|
||||
*
|
||||
* @method NavigationRenderer getInnerIterator() {
|
||||
* {@inheritdoc}
|
||||
* }
|
||||
*/
|
||||
class RecursiveNavigationRenderer extends RecursiveIteratorIterator implements NavigationRendererInterface
|
||||
{
|
||||
/**
|
||||
* The content rendered so far
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Create a new RecursiveNavigationRenderer
|
||||
*
|
||||
* @param Navigation $navigation
|
||||
*/
|
||||
public function __construct(Navigation $navigation)
|
||||
{
|
||||
$this->content = array();
|
||||
parent::__construct(
|
||||
new NavigationRenderer($navigation, true),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setElementTag($tag)
|
||||
{
|
||||
$this->getInnerIterator()->setElementTag($tag);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getElementTag()
|
||||
{
|
||||
return $this->getInnerIterator()->getElementTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCssClass($class)
|
||||
{
|
||||
$this->getInnerIterator()->setCssClass($class);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCssClass()
|
||||
{
|
||||
return $this->getInnerIterator()->getCssClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setHeading($heading)
|
||||
{
|
||||
$this->getInnerIterator()->setHeading($heading);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHeading()
|
||||
{
|
||||
return $this->getInnerIterator()->getHeading();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function beginIteration()
|
||||
{
|
||||
$this->content[] = $this->getInnerIterator()->beginMarkup();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endIteration()
|
||||
{
|
||||
$this->content[] = $this->getInnerIterator()->endMarkup();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function beginChildren()
|
||||
{
|
||||
$this->content[] = $this->getInnerIterator()->beginChildrenMarkup();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endChildren()
|
||||
{
|
||||
$this->content[] = $this->getInnerIterator()->endChildrenMarkup();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
foreach ($this as $item) {
|
||||
/** @var NavigationItem $item */
|
||||
if ($item->shouldRender()) {
|
||||
$this->content[] = $this->getInnerIterator()->beginItemMarkup($item);
|
||||
$this->content[] = $item->render();
|
||||
if (! $item->hasChildren()) {
|
||||
$this->content[] = $this->getInnerIterator()->endItemMarkup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return join("\n", $this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (Exception $e) {
|
||||
return IcingaException::describe($e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Navigation\Renderer;
|
||||
|
||||
use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer;
|
||||
|
||||
/**
|
||||
* Summary badge adding up all badges in the navigation's children that have the same state
|
||||
*/
|
||||
class SummaryNavigationItemRenderer extends BadgeNavigationItemRenderer
|
||||
{
|
||||
/**
|
||||
* The title of each summarized child
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $titles;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->getItem()->getChildren() as $child) {
|
||||
$renderer = $child->getRenderer();
|
||||
if ($renderer instanceof BadgeNavigationItemRenderer) {
|
||||
if ($renderer->getState() === $this->getState()) {
|
||||
$this->titles[] = $renderer->getTitle();
|
||||
$count += $renderer->getCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return !empty($this->titles) ? join(', ', $this->titles) : '';
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ class StyleSheet
|
|||
'css/icinga/pagination.less',
|
||||
'css/icinga/selection-toolbar.less',
|
||||
'css/icinga/login.less',
|
||||
'css/icinga/logo.less',
|
||||
'css/icinga/controls.less'
|
||||
);
|
||||
|
||||
|
|
|
@ -13,12 +13,17 @@ use Icinga\Data\Filter\Filter;
|
|||
* returns Urls reflecting all changes made to the url and to the parameters.
|
||||
*
|
||||
* Direct instantiation is prohibited and should be done either with @see Url::fromRequest() or
|
||||
* @see Url::fromUrlString()
|
||||
*
|
||||
* Currently, protocol, host and port are ignored and will be implemented when required
|
||||
* @see Url::fromPath()
|
||||
*/
|
||||
class Url
|
||||
{
|
||||
/**
|
||||
* Whether this url points to an external resource
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $external;
|
||||
|
||||
/**
|
||||
* An array of all parameters stored in this Url
|
||||
*
|
||||
|
@ -41,12 +46,11 @@ class Url
|
|||
protected $path = '';
|
||||
|
||||
/**
|
||||
* The baseUrl that will be appended to @see Url::$path in order to
|
||||
* create an absolute Url
|
||||
* The baseUrl that will be appended to @see Url::$path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = '/';
|
||||
protected $baseUrl = '';
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
|
@ -71,7 +75,7 @@ class Url
|
|||
}
|
||||
|
||||
$url = new Url();
|
||||
$url->setPath($request->getPathInfo());
|
||||
$url->setPath(ltrim($request->getPathInfo(), '/'));
|
||||
|
||||
// $urlParams = UrlParams::fromQueryString($request->getQuery());
|
||||
if (isset($_SERVER['QUERY_STRING'])) {
|
||||
|
@ -124,7 +128,7 @@ class Url
|
|||
$request = self::getRequest();
|
||||
}
|
||||
|
||||
if (!is_string($url)) {
|
||||
if (! is_string($url)) {
|
||||
throw new ProgrammingError(
|
||||
'url "%s" is not a string',
|
||||
$url
|
||||
|
@ -132,17 +136,44 @@ class Url
|
|||
}
|
||||
|
||||
$urlObject = new Url();
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
$urlObject->setBaseUrl($baseUrl);
|
||||
|
||||
if ($url === '#') {
|
||||
$urlObject->setPath($url);
|
||||
return $urlObject;
|
||||
}
|
||||
|
||||
$urlParts = parse_url($url);
|
||||
if (isset($urlParts['path'])) {
|
||||
if ($baseUrl !== '' && strpos($urlParts['path'], $baseUrl) === 0) {
|
||||
$urlObject->setPath(substr($urlParts['path'], strlen($baseUrl)));
|
||||
} else {
|
||||
$urlObject->setPath($urlParts['path']);
|
||||
}
|
||||
if (isset($urlParts['scheme']) && (
|
||||
$urlParts['scheme'] !== $request->getScheme()
|
||||
|| (isset($urlParts['host']) && $urlParts['host'] !== $request->getServer('SERVER_NAME'))
|
||||
|| (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT')))
|
||||
) {
|
||||
$baseUrl = $urlParts['scheme'] . '://' . $urlParts['host'] . (isset($urlParts['port'])
|
||||
? (':' . $urlParts['port'])
|
||||
: '');
|
||||
$urlObject->setIsExternal();
|
||||
} else {
|
||||
$baseUrl = '';
|
||||
}
|
||||
|
||||
if (isset($urlParts['path'])) {
|
||||
$urlPath = $urlParts['path'];
|
||||
if ($urlPath && $urlPath[0] === '/') {
|
||||
if ($baseUrl) {
|
||||
$urlPath = substr($urlPath, 1);
|
||||
} elseif (strpos($urlPath, $request->getBaseUrl()) === 0) {
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
$urlPath = substr($urlPath, strlen($baseUrl) + 1);
|
||||
}
|
||||
} elseif (! $baseUrl) {
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
}
|
||||
|
||||
$urlObject->setPath($urlPath);
|
||||
} elseif (! $baseUrl) {
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
}
|
||||
|
||||
// TODO: This has been used by former filter implementation, remove it:
|
||||
if (isset($urlParts['query'])) {
|
||||
$params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params);
|
||||
|
@ -152,6 +183,7 @@ class Url
|
|||
$urlObject->setAnchor($urlParts['fragment']);
|
||||
}
|
||||
|
||||
$urlObject->setBaseUrl($baseUrl);
|
||||
$urlObject->setParams($params);
|
||||
return $urlObject;
|
||||
}
|
||||
|
@ -179,19 +211,13 @@ class Url
|
|||
/**
|
||||
* Overwrite the baseUrl
|
||||
*
|
||||
* If an empty Url is given '/' is used as the base
|
||||
*
|
||||
* @param string $baseUrl The url path to use as the Url Base
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setBaseUrl($baseUrl)
|
||||
{
|
||||
if (($baseUrl = rtrim($baseUrl, '/ ')) === '') {
|
||||
$baseUrl = '/';
|
||||
}
|
||||
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->baseUrl = rtrim($baseUrl, '/ ');
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -214,7 +240,7 @@ class Url
|
|||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = ltrim($path, '/');
|
||||
$this->path = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -231,17 +257,61 @@ class Url
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the relative url with query parameters as a string
|
||||
* Set whether this url points to an external resource
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsExternal($state = true)
|
||||
{
|
||||
$this->external = (bool) $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this url points to an external resource
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExternal()
|
||||
{
|
||||
return $this->external;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeUrl($separator = '&')
|
||||
{
|
||||
if ($this->params->isEmpty()) {
|
||||
return $this->path . $this->anchor;
|
||||
} else {
|
||||
return $this->path . '?' . $this->params->toString($separator) . $this->anchor;
|
||||
$path = $this->buildPathQueryAndFragment($separator);
|
||||
if ($path && $path[0] === '/') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this url's path with its query parameters and fragment as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildPathQueryAndFragment($querySeparator)
|
||||
{
|
||||
$anchor = $this->getAnchor();
|
||||
if ($anchor) {
|
||||
$anchor = '#' . $anchor;
|
||||
}
|
||||
|
||||
$query = $this->getQueryString($querySeparator);
|
||||
if ($query) {
|
||||
$query = '?' . $query;
|
||||
}
|
||||
|
||||
return $this->getPath() . $query . $anchor;
|
||||
}
|
||||
|
||||
public function setQueryString($queryString)
|
||||
|
@ -250,9 +320,9 @@ class Url
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getQueryString()
|
||||
public function getQueryString($separator = null)
|
||||
{
|
||||
return (string) $this->params;
|
||||
return $this->params->toString($separator);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,7 +332,17 @@ class Url
|
|||
*/
|
||||
public function getAbsoluteUrl($separator = '&')
|
||||
{
|
||||
return $this->baseUrl . ($this->baseUrl !== '/' ? '/' : '') . $this->getRelativeUrl($separator);
|
||||
$path = $this->buildPathQueryAndFragment($separator);
|
||||
if ($path && ($path === '#' || $path[0] === '/')) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$baseUrl = $this->getBaseUrl();
|
||||
if (! $baseUrl) {
|
||||
$baseUrl = '/';
|
||||
}
|
||||
|
||||
return $baseUrl . ($baseUrl !== '/' && $path ? '/' : '') . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -380,10 +460,20 @@ class Url
|
|||
*/
|
||||
public function setAnchor($anchor)
|
||||
{
|
||||
$this->anchor = '#' . $anchor;
|
||||
$this->anchor = $anchor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the url anchor-part
|
||||
*
|
||||
* @return string The site's anchor string without the '#'
|
||||
*/
|
||||
public function getAnchor()
|
||||
{
|
||||
return $this->anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove provided key (if string) or keys (if array of string) from the query parameter array
|
||||
*
|
||||
|
|
|
@ -9,6 +9,8 @@ use Icinga\Exception\ConfigurationError;
|
|||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\User;
|
||||
use Icinga\Web\Navigation\DashboardPane;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Widget\Dashboard\Pane;
|
||||
use Icinga\Web\Widget\Dashboard\Dashlet as DashboardDashlet;
|
||||
use Icinga\Web\Url;
|
||||
|
@ -68,16 +70,22 @@ class Dashboard extends AbstractWidget
|
|||
*/
|
||||
public function load()
|
||||
{
|
||||
$manager = Icinga::app()->getModuleManager();
|
||||
foreach ($manager->getLoadedModules() as $module) {
|
||||
if ($this->getUser()->can($manager::MODULE_PERMISSION_NS . $module->getName())) {
|
||||
$this->mergePanes($module->getPaneItems());
|
||||
$navigation = new Navigation();
|
||||
$navigation->load('dashboard-pane');
|
||||
|
||||
$panes = array();
|
||||
foreach ($navigation as $dashboardPane) {
|
||||
/** @var DashboardPane $dashboardPane */
|
||||
$pane = new Pane($dashboardPane->getLabel());
|
||||
foreach ($dashboardPane->getDashlets() as $title => $url) {
|
||||
$pane->addDashlet($title, $url);
|
||||
}
|
||||
|
||||
$panes[] = $pane;
|
||||
}
|
||||
|
||||
$this->mergePanes($panes);
|
||||
$this->loadUserDashboards();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Widget\Tabextension;
|
||||
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabs;
|
||||
|
||||
/**
|
||||
* Tabextension that allows to add the current URL as menu entry
|
||||
*
|
||||
* Displayed as a dropdown field in the tabs
|
||||
*/
|
||||
class MenuAction implements Tabextension
|
||||
{
|
||||
/**
|
||||
* Applies the menu actions to the provided tabset
|
||||
*
|
||||
* @param Tabs $tabs The tabs object to extend with
|
||||
*/
|
||||
public function apply(Tabs $tabs)
|
||||
{
|
||||
$tabs->addAsDropdown(
|
||||
'menu-entry',
|
||||
array(
|
||||
'icon' => 'menu',
|
||||
'label' => t('Add To Menu'),
|
||||
'url' => Url::fromPath('navigation/add'),
|
||||
'urlParams' => array(
|
||||
'url' => rawurlencode(Url::fromRequest()->getRelativeUrl())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ use Icinga\Module\Monitoring\Controller;
|
|||
use Icinga\Module\Monitoring\Web\Widget\SelectBox;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
class AlertsummaryController extends Controller
|
||||
{
|
||||
|
@ -53,7 +54,7 @@ class AlertsummaryController extends Controller
|
|||
'label' => $this->translate('Alert Summary'),
|
||||
'url' => Url::fromRequest()
|
||||
)
|
||||
)->extend(new DashboardAction())->activate('alertsummary');
|
||||
)->extend(new DashboardAction())->extend(new MenuAction())->activate('alertsummary');
|
||||
$this->view->title = $this->translate('Alert Summary');
|
||||
|
||||
$this->view->intervalBox = $this->createIntervalBox();
|
||||
|
|
|
@ -7,6 +7,7 @@ use Icinga\Module\Monitoring\Controller;
|
|||
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
/**
|
||||
* Display detailed information about a comment
|
||||
|
@ -55,7 +56,7 @@ class CommentController extends Controller
|
|||
'title' => $this->translate('Display detailed information about a comment.'),
|
||||
'url' =>'monitoring/comments/show'
|
||||
)
|
||||
)->activate('comment')->extend(new DashboardAction());
|
||||
)->activate('comment')->extend(new DashboardAction())->extend(new MenuAction());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ use Icinga\Module\Monitoring\Object\Host;
|
|||
use Icinga\Module\Monitoring\Object\Service;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
/**
|
||||
* Display detailed information about a downtime
|
||||
|
@ -65,7 +66,7 @@ class DowntimeController extends Controller
|
|||
'title' => $this->translate('Display detailed information about a downtime.'),
|
||||
'url' =>'monitoring/downtimes/show'
|
||||
)
|
||||
)->activate('downtime')->extend(new DashboardAction());
|
||||
)->activate('downtime')->extend(new DashboardAction())->extend(new MenuAction());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@ use Icinga\Module\Monitoring\Controller;
|
|||
use Icinga\Module\Monitoring\Forms\Command\Instance\DisableNotificationsExpireCommandForm;
|
||||
use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
/**
|
||||
* Display process and performance information of the monitoring host and program-wide commands
|
||||
|
@ -43,7 +44,7 @@ class HealthController extends Controller
|
|||
'url' =>'monitoring/health/stats'
|
||||
)
|
||||
)
|
||||
->extend(new DashboardAction());
|
||||
->extend(new DashboardAction())->extend(new MenuAction());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -77,6 +77,7 @@ class HostController extends MonitoredObjectController
|
|||
'host_state_type',
|
||||
'host_last_state_change',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_handled',
|
||||
'service_description',
|
||||
'service_display_name',
|
||||
|
|
|
@ -18,6 +18,7 @@ use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandF
|
|||
use Icinga\Module\Monitoring\Object\HostList;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
class HostsController extends Controller
|
||||
{
|
||||
|
@ -44,7 +45,7 @@ class HostsController extends Controller
|
|||
'url' => Url::fromRequest(),
|
||||
'icon' => 'host'
|
||||
)
|
||||
)->extend(new DashboardAction())->activate('show');
|
||||
)->extend(new DashboardAction())->extend(new MenuAction())->activate('show');
|
||||
$this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/hosts');
|
||||
}
|
||||
|
||||
|
@ -55,6 +56,7 @@ class HostsController extends Controller
|
|||
'host_icon_image_alt',
|
||||
'host_name',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_state',
|
||||
'host_problem',
|
||||
'host_handled',
|
||||
|
@ -92,6 +94,7 @@ class HostsController extends Controller
|
|||
'host_icon_image_alt',
|
||||
'host_name',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_state',
|
||||
'host_problem',
|
||||
'host_handled',
|
||||
|
|
|
@ -13,6 +13,7 @@ use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
|
|||
use Icinga\Module\Monitoring\Forms\StatehistoryForm;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
use Icinga\Web\Widget\Tabextension\OutputFormat;
|
||||
use Icinga\Web\Widget\Tabs;
|
||||
|
||||
|
@ -58,7 +59,6 @@ class ListController extends Controller
|
|||
'host_name',
|
||||
'host_display_name',
|
||||
'host_state' => $stateColumn,
|
||||
'host_address',
|
||||
'host_acknowledged',
|
||||
'host_output',
|
||||
'host_attempt',
|
||||
|
@ -66,15 +66,10 @@ class ListController extends Controller
|
|||
'host_is_flapping',
|
||||
'host_state_type',
|
||||
'host_handled',
|
||||
'host_last_check',
|
||||
'host_last_state_change' => $stateChangeColumn,
|
||||
'host_notifications_enabled',
|
||||
'host_action_url',
|
||||
'host_notes_url',
|
||||
'host_active_checks_enabled',
|
||||
'host_passive_checks_enabled',
|
||||
'host_current_check_attempt',
|
||||
'host_max_check_attempts'
|
||||
'host_passive_checks_enabled'
|
||||
), $this->addColumns()));
|
||||
$this->applyRestriction('monitoring/filter/objects', $query);
|
||||
$this->filterQuery($query);
|
||||
|
@ -132,10 +127,6 @@ class ListController extends Controller
|
|||
'host_name',
|
||||
'host_display_name',
|
||||
'host_state',
|
||||
'host_state_type',
|
||||
'host_last_state_change',
|
||||
'host_address',
|
||||
'host_handled',
|
||||
'service_description',
|
||||
'service_display_name',
|
||||
'service_state' => $stateColumn,
|
||||
|
@ -152,14 +143,9 @@ class ListController extends Controller
|
|||
'service_state_type',
|
||||
'service_handled',
|
||||
'service_severity',
|
||||
'service_last_check',
|
||||
'service_notifications_enabled',
|
||||
'service_action_url',
|
||||
'service_notes_url',
|
||||
'service_active_checks_enabled',
|
||||
'service_passive_checks_enabled',
|
||||
'current_check_attempt' => 'service_current_check_attempt',
|
||||
'max_check_attempts' => 'service_max_check_attempts'
|
||||
'service_passive_checks_enabled'
|
||||
), $this->addColumns());
|
||||
$query = $this->backend->select()->from('servicestatus', $columns);
|
||||
$this->applyRestriction('monitoring/filter/objects', $query);
|
||||
|
@ -640,6 +626,6 @@ class ListController extends Controller
|
|||
*/
|
||||
private function createTabs()
|
||||
{
|
||||
$this->getTabs()->extend(new OutputFormat())->extend(new DashboardAction());
|
||||
$this->getTabs()->extend(new OutputFormat())->extend(new DashboardAction())->extend(new MenuAction());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandF
|
|||
use Icinga\Module\Monitoring\Object\ServiceList;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
class ServicesController extends Controller
|
||||
{
|
||||
|
@ -46,7 +47,7 @@ class ServicesController extends Controller
|
|||
'url' => Url::fromRequest(),
|
||||
'icon' => 'services'
|
||||
)
|
||||
)->extend(new DashboardAction())->activate('show');
|
||||
)->extend(new DashboardAction())->extend(new MenuAction())->activate('show');
|
||||
}
|
||||
|
||||
protected function handleCommandForm(ObjectsCommandForm $form)
|
||||
|
@ -56,6 +57,7 @@ class ServicesController extends Controller
|
|||
'host_icon_image_alt',
|
||||
'host_name',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_output',
|
||||
'host_state',
|
||||
'host_problem',
|
||||
|
@ -101,6 +103,7 @@ class ServicesController extends Controller
|
|||
'host_icon_image_alt',
|
||||
'host_name',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_output',
|
||||
'host_state',
|
||||
'host_problem',
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring\Controllers;
|
|||
use Icinga\Module\Monitoring\Controller;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
class TacticalController extends Controller
|
||||
{
|
||||
|
@ -22,7 +23,7 @@ class TacticalController extends Controller
|
|||
'label' => $this->translate('Tactical Overview'),
|
||||
'url' => Url::fromRequest()
|
||||
)
|
||||
)->extend(new DashboardAction())->activate('tactical_overview');
|
||||
)->extend(new DashboardAction())->extend(new MenuAction())->activate('tactical_overview');
|
||||
$stats = $this->backend->select()->from(
|
||||
'statussummary',
|
||||
array(
|
||||
|
|
|
@ -12,6 +12,7 @@ use Icinga\Module\Monitoring\Web\Widget\SelectBox;
|
|||
use Icinga\Util\Format;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
class TimelineController extends Controller
|
||||
{
|
||||
|
@ -24,7 +25,7 @@ class TimelineController extends Controller
|
|||
'label' => $this->translate('Timeline'),
|
||||
'url' => Url::fromRequest()
|
||||
)
|
||||
)->extend(new DashboardAction())->activate('timeline');
|
||||
)->extend(new DashboardAction())->extend(new MenuAction())->activate('timeline');
|
||||
$this->view->title = $this->translate('Timeline');
|
||||
|
||||
// TODO: filter for hard_states (precedence adjustments necessary!)
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Forms\Navigation;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\QueryException;
|
||||
use Icinga\Forms\Navigation\NavigationItemForm;
|
||||
|
||||
class ActionForm extends NavigationItemForm
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createElements(array $formData)
|
||||
{
|
||||
parent::createElements($formData);
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'filter',
|
||||
array(
|
||||
'allowEmpty' => true,
|
||||
'label' => $this->translate('Filter'),
|
||||
'description' => $this->translate(
|
||||
'Display this action only for objects matching this filter. Leave it blank'
|
||||
. ' if you want this action being displayed regardless of the object'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($formData)
|
||||
{
|
||||
if (! parent::isValid($formData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (($filterString = $this->getValue('filter')) !== null) {
|
||||
$filter = Filter::matchAll();
|
||||
$filter->setAllowedFilterColumns(array(
|
||||
'host_name',
|
||||
'hostgroup_name',
|
||||
'instance_name',
|
||||
'service_description',
|
||||
'servicegroup_name',
|
||||
function ($c) {
|
||||
return preg_match('/^_(?:host|service)_/', $c);
|
||||
}
|
||||
));
|
||||
|
||||
try {
|
||||
$filter->addFilter(Filter::fromQueryString($filterString));
|
||||
} catch (QueryException $_) {
|
||||
$this->getElement('filter')->addError(sprintf(
|
||||
$this->translate('Invalid filter provided. You can only use the following columns: %s'),
|
||||
implode(', ', array(
|
||||
'instance_name',
|
||||
'host_name',
|
||||
'hostgroup_name',
|
||||
'service_description',
|
||||
'servicegroup_name',
|
||||
'_(host|service)_<customvar-name>'
|
||||
))
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Forms\Navigation;
|
||||
|
||||
class HostActionForm extends ActionForm
|
||||
{
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Forms\Navigation;
|
||||
|
||||
class ServiceActionForm extends ActionForm
|
||||
{
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<div class="content" data-base-target="_next">
|
||||
<p>
|
||||
<a href="<?= $this->href('/monitoring/config/createbackend'); ?>">
|
||||
<a href="<?= $this->href('monitoring/config/createbackend'); ?>">
|
||||
<?= $this->icon('plus'); ?> <?= $this->translate('Create New Monitoring Backend'); ?>
|
||||
</a>
|
||||
</p>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<td>
|
||||
<?= $this->qlink(
|
||||
$backendName,
|
||||
'/monitoring/config/editbackend',
|
||||
'monitoring/config/editbackend',
|
||||
array('backend-name' => $backendName),
|
||||
array(
|
||||
'icon' => 'edit',
|
||||
|
@ -35,7 +35,7 @@
|
|||
<td>
|
||||
<?= $this->qlink(
|
||||
'',
|
||||
'/monitoring/config/removebackend',
|
||||
'monitoring/config/removebackend',
|
||||
array('backend-name' => $backendName),
|
||||
array(
|
||||
'icon' => 'trash',
|
||||
|
@ -49,7 +49,7 @@
|
|||
</table>
|
||||
<h1><?= $this->translate('Command Transports') ?></h1>
|
||||
<p>
|
||||
<a href="<?= $this->href('/monitoring/config/createtransport'); ?>">
|
||||
<a href="<?= $this->href('monitoring/config/createtransport'); ?>">
|
||||
<?= $this->icon('plus'); ?> <?= $this->translate('Create New Transport'); ?>
|
||||
</a>
|
||||
</p>
|
||||
|
@ -64,7 +64,7 @@
|
|||
<td>
|
||||
<?= $this->qlink(
|
||||
$transportName,
|
||||
'/monitoring/config/edittransport',
|
||||
'monitoring/config/edittransport',
|
||||
array('transport' => $transportName),
|
||||
array(
|
||||
'icon' => 'edit',
|
||||
|
@ -79,7 +79,7 @@
|
|||
<td>
|
||||
<?= $this->qlink(
|
||||
'',
|
||||
'/monitoring/config/removetransport',
|
||||
'monitoring/config/removetransport',
|
||||
array('transport' => $transportName),
|
||||
array(
|
||||
'icon' => 'trash',
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="content">
|
||||
<table data-base-target="_next"
|
||||
class="action comments multiselect"
|
||||
data-icinga-multiselect-url="/icingaweb2/monitoring/comments/show"
|
||||
data-icinga-multiselect-url="<?= $this->href('monitoring/comments/show'); ?>"
|
||||
data-icinga-multiselect-related="<?= $this->href("monitoring/comments") ?>"
|
||||
data-icinga-multiselect-data="comment_id">
|
||||
<tbody>
|
||||
|
|
|
@ -27,7 +27,7 @@ if (count($groupData) === 0) {
|
|||
<div class="box contents">
|
||||
<?php foreach ($groupInfo['contacts'] as $c): ?>
|
||||
<div class="box entry">
|
||||
<?= $this->img('/static/gravatar', array('email' => $c->contact_email)); ?>
|
||||
<?= $this->img('static/gravatar', array('email' => $c->contact_email)); ?>
|
||||
<?= $this->qlink(
|
||||
$c->contact_alias,
|
||||
'monitoring/show/contact',
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<div data-base-target="_next" class="content contacts">
|
||||
<?php foreach ($contacts->peekAhead($this->compact) as $contact): ?>
|
||||
<div class="contact">
|
||||
<?= $this->img('/static/gravatar', array('email' => $contact->contact_email)); ?>
|
||||
<?= $this->img('static/gravatar', array('email' => $contact->contact_email)); ?>
|
||||
<strong><?= $this->qlink(
|
||||
$contact->contact_name,
|
||||
'monitoring/show/contact',
|
||||
|
|
|
@ -20,7 +20,7 @@ if (! $this->compact): ?>
|
|||
<div class="content">
|
||||
<table data-base-target="_next"
|
||||
class="action multiselect"
|
||||
data-icinga-multiselect-url="/icingaweb2/monitoring/downtimes/show"
|
||||
data-icinga-multiselect-url="<?= $this->href('monitoring/downtimes/show'); ?>"
|
||||
data-icinga-multiselect-controllers="<?= $this->href("monitoring/downtimes") ?>"
|
||||
data-icinga-multiselect-data="downtime_id">
|
||||
<tbody>
|
||||
|
|
|
@ -10,15 +10,18 @@ use Icinga\Module\Monitoring\Object\Host;
|
|||
</td>
|
||||
<td>
|
||||
<?= $this->iconImage()->host($object) ?>
|
||||
<strong><?= $this->escape($object->host_display_name); ?></strong>
|
||||
<strong class="selectable"><?= $this->escape($object->host_display_name); ?></strong>
|
||||
<?php if ($object->host_display_name !== $object->host_name): ?>
|
||||
<small>(<?= $this->escape($object->host_name); ?>)</small>
|
||||
<small class="selectable">(<?= $this->escape($object->host_name); ?>)</small>
|
||||
<?php endif ?>
|
||||
<?= $this->render('partials/host/statusicons.phtml'); ?>
|
||||
<br/>
|
||||
<?php if ($object->host_address6 && $object->host_address6 !== $object->host_name): ?>
|
||||
<span class="selectable" title="IPv6 address"><?= $this->escape($object->host_address6); ?></span>
|
||||
<?php endif ?>
|
||||
<?php if ($object->host_address && $object->host_address !== $object->host_name): ?>
|
||||
<br>
|
||||
<?= $this->escape($object->host_address); ?>
|
||||
<span class="selectable" title="IPv4 address"><?= $this->escape($object->host_address); ?></span>
|
||||
<?php endif ?>
|
||||
<?= $this->render('partials/host/statusicons.phtml'); ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -11,15 +11,17 @@ use Icinga\Module\Monitoring\Object\Service;
|
|||
</td>
|
||||
<td>
|
||||
<?= $this->iconImage()->service($object) ?>
|
||||
<strong><?= $this->escape($object->host_display_name); ?></strong>
|
||||
<strong class="selectable"><?= $this->escape($object->host_display_name); ?></strong>
|
||||
<?php if ($object->host_display_name !== $object->host_name): ?>
|
||||
<small>(<?= $this->escape($object->host_name); ?>)</small>
|
||||
<small class="selectable">(<?= $this->escape($object->host_name); ?>)</small>
|
||||
<?php endif ?>
|
||||
<br/>
|
||||
<?php if ($object->host_address6 && $object->host_address6 !== $object->host_name): ?>
|
||||
<span class="selectable" title="IPv6 address"><?= $this->escape($object->host_address6); ?></span>
|
||||
<?php endif ?>
|
||||
<?php if ($object->host_address && $object->host_address !== $object->host_name): ?>
|
||||
<br>
|
||||
<?= $this->escape($object->host_address); ?>
|
||||
<span class="selectable" title="IPv4 address"><?= $this->escape($object->host_address); ?></span>
|
||||
<?php endif ?>
|
||||
<?= $this->render('partials/host/statusicons.phtml'); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="state <?= Service::getStateText($object->service_state); ?><?= $object->service_handled ? ' handled' : ''; ?>">
|
||||
|
@ -29,7 +31,7 @@ use Icinga\Module\Monitoring\Object\Service;
|
|||
</td>
|
||||
<td>
|
||||
<?= $this->iconImage()->host($object) ?>
|
||||
<strong><?= $this->translate('Service'); ?>: <?= $this->escape($object->service_display_name); ?></strong>
|
||||
<strong><?= $this->translate('Service'); ?>: <span class="selectable"><?= $this->escape($object->service_display_name); ?></span></strong>
|
||||
<?php if ($object->service_display_name !== $object->service_description): ?>
|
||||
<small>(<?= $this->escape($object->service_description); ?>)</small>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -1,25 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
|
||||
$navigation = new Navigation();
|
||||
$navigation->load($object->getType() . '-action');
|
||||
foreach ($navigation as $item) {
|
||||
$item->setObject($object);
|
||||
}
|
||||
|
||||
// add warning to links that open in new tabs to improve accessibility, as recommended by WCAG20 G201
|
||||
$newTabInfo = sprintf('<span class="info-box display-on-hover"> %s </span>', $this->translate('opens in new window'));
|
||||
|
||||
$links = $object->getActionUrls();
|
||||
foreach ($links as $i => $link) {
|
||||
$links[$i] = sprintf('<a href="%s" target="_blank">%s ' . $newTabInfo . '</a>', $link, 'Action');
|
||||
foreach ($object->getActionUrls() as $i => $link) {
|
||||
$navigation->addItem(
|
||||
'Action ' . ($i + 1) . $newTabInfo,
|
||||
array(
|
||||
'url' => $link,
|
||||
'target' => '_blank',
|
||||
'renderer' => array(
|
||||
'NavigationItemRenderer',
|
||||
'escape_label' => false
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->actions)) {
|
||||
foreach ($this->actions as $id => $action) {
|
||||
$links[] = sprintf('<a href="%s">%s</a>', $action, $id);
|
||||
$navigation->addItem($id, array('url' => $action));
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($links)) {
|
||||
if ($navigation->isEmpty() || !$navigation->hasRenderableItems()) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<th><?= $this->translate('Actions') ?></th>
|
||||
<td><?= implode("<br>", $links) ?></td>
|
||||
</tr>
|
||||
<th><?= $this->translate('Actions'); ?></th>
|
||||
<?= $navigation->getRenderer()->setElementTag('td')->setCssClass('actions'); ?>
|
||||
</tr>
|
|
@ -10,7 +10,7 @@ foreach ($object->customvars as $name => $value) {
|
|||
|
||||
printf(
|
||||
'<tr><th>%s</th><td class="customvar">%s</td></tr>' . "\n",
|
||||
$this->escape($name),
|
||||
$this->escape(ucwords(str_replace('_', ' ', strtolower($name)))),
|
||||
$this->customvar($value)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,26 +1,46 @@
|
|||
<?php
|
||||
$notes = trim($object->getNotes());
|
||||
$links = $object->getNotesUrls();
|
||||
|
||||
if (! empty($links) || ! empty($notes)): ?>
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
|
||||
$navigation = new Navigation();
|
||||
//$navigation->load($object->getType() . '-note');
|
||||
foreach ($navigation as $item) {
|
||||
$item->setObject($object);
|
||||
}
|
||||
|
||||
$notes = trim($object->getNotes());
|
||||
if ($notes) {
|
||||
$navigation->addItem($notes);
|
||||
}
|
||||
|
||||
$links = $object->getNotesUrls();
|
||||
if (! empty($links)) {
|
||||
// add warning to links that open in new tabs to improve accessibility, as recommended by WCAG20 G201
|
||||
$newTabInfo = sprintf(
|
||||
'<span class="info-box display-on-hover"> %s </span>',
|
||||
$this->translate('opens in new window')
|
||||
);
|
||||
|
||||
foreach ($links as $link) {
|
||||
$navigation->addItem(
|
||||
$this->escape($link) . $newTabInfo,
|
||||
array(
|
||||
'url' => $link,
|
||||
'target' => '_blank',
|
||||
'renderer' => array(
|
||||
'NavigationItemRenderer',
|
||||
'escape_label' => false
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($navigation->isEmpty() || !$navigation->hasRenderableItems()) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<th><?= $this->translate('Notes') ?></th>
|
||||
<td>
|
||||
<?php
|
||||
if (! empty($notes)) {
|
||||
echo $notes . '<br>';
|
||||
}
|
||||
// add warning to links that open in new tabs to improve accessibility, as recommended by WCAG20 G201
|
||||
$newTabInfo = sprintf(
|
||||
'<span class="info-box display-on-hover"> %s </span>',
|
||||
$this->translate('opens in new window')
|
||||
);
|
||||
$linkText = '<a href="%s" target="_blank">%s ' . $newTabInfo . '</a>';
|
||||
foreach ($links as $i => $link) {
|
||||
$links[$i] = sprintf($linkText, $this->escape($link), $this->escape($link));
|
||||
}
|
||||
echo implode('<br>', $links);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
<th><?= $this->translate('Notes'); ?></th>
|
||||
<?= $navigation->getRenderer()->setElementTag('td')->setCssClass('notes'); ?>
|
||||
</tr>
|
|
@ -65,7 +65,7 @@ if (! $beingExtended && !$this->compact): ?>
|
|||
<div class="timeframe">
|
||||
<span><?= $this->qlink(
|
||||
$timeInfo[0]->end->format($intervalFormat),
|
||||
'/monitoring/list/eventhistory',
|
||||
'monitoring/list/eventhistory',
|
||||
array(
|
||||
'timestamp<' => $timeInfo[0]->start->getTimestamp(),
|
||||
'timestamp>' => $timeInfo[0]->end->getTimestamp()
|
||||
|
|
|
@ -85,20 +85,29 @@ $this->provideSearchUrl($this->translate('Services'), 'monitoring/list/services?
|
|||
$this->provideSearchUrl($this->translate('Hostgroups'), 'monitoring/list/hostgroups?limit=10', 97);
|
||||
$this->provideSearchUrl($this->translate('Servicegroups'), 'monitoring/list/servicegroups?limit=10', 96);
|
||||
|
||||
/*
|
||||
* Available navigation items
|
||||
*/
|
||||
$this->provideNavigationItem('host-action', $this->translate('Host Action'));
|
||||
$this->provideNavigationItem('service-action', $this->translate('Service Action'));
|
||||
// Notes are disabled as we're not sure whether to really make a difference between actions and notes
|
||||
//$this->provideNavigationItem('host-note', $this->translate('Host Note'));
|
||||
//$this->provideNavigationItem('service-note', $this->translate('Service Note'));
|
||||
|
||||
/*
|
||||
* Problems Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('Problems'), array(
|
||||
$section = $this->menuSection(N_('Problems'), array(
|
||||
'renderer' => array(
|
||||
'SummaryMenuItemRenderer',
|
||||
'SummaryNavigationItemRenderer',
|
||||
'state' => 'critical'
|
||||
),
|
||||
'icon' => 'block',
|
||||
'priority' => 20
|
||||
));
|
||||
$section->add($this->translate('Unhandled Hosts'), array(
|
||||
$section->add(N_('Unhandled Hosts'), array(
|
||||
'renderer' => array(
|
||||
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
|
||||
'MonitoringBadgeNavigationItemRenderer',
|
||||
'columns' => array(
|
||||
'hosts_down_unhandled' => $this->translate('%d unhandled hosts down')
|
||||
),
|
||||
|
@ -108,9 +117,9 @@ $section->add($this->translate('Unhandled Hosts'), array(
|
|||
'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0',
|
||||
'priority' => 30
|
||||
));
|
||||
$section->add($this->translate('Unhandled Services'), array(
|
||||
$section->add(N_('Unhandled Services'), array(
|
||||
'renderer' => array(
|
||||
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
|
||||
'MonitoringBadgeNavigationItemRenderer',
|
||||
'columns' => array(
|
||||
'services_critical_unhandled' => $this->translate('%d unhandled services critical')
|
||||
),
|
||||
|
@ -120,19 +129,19 @@ $section->add($this->translate('Unhandled Services'), array(
|
|||
'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity',
|
||||
'priority' => 40
|
||||
));
|
||||
$section->add($this->translate('Host Problems'), array(
|
||||
$section->add(N_('Host Problems'), array(
|
||||
'url' => 'monitoring/list/hosts?host_problem=1&sort=host_severity',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add($this->translate('Service Problems'), array(
|
||||
$section->add(N_('Service Problems'), array(
|
||||
'url' => 'monitoring/list/services?service_problem=1&sort=service_severity&dir=desc',
|
||||
'priority' => 60
|
||||
));
|
||||
$section->add($this->translate('Service Grid'), array(
|
||||
$section->add(N_('Service Grid'), array(
|
||||
'url' => 'monitoring/list/servicegrid?problems',
|
||||
'priority' => 70
|
||||
));
|
||||
$section->add($this->translate('Current Downtimes'), array(
|
||||
$section->add(N_('Current Downtimes'), array(
|
||||
'url' => 'monitoring/list/downtimes?downtime_is_in_effect=1',
|
||||
'priority' => 80
|
||||
));
|
||||
|
@ -140,43 +149,43 @@ $section->add($this->translate('Current Downtimes'), array(
|
|||
/*
|
||||
* Overview Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('Overview'), array(
|
||||
$section = $this->menuSection(N_('Overview'), array(
|
||||
'icon' => 'sitemap',
|
||||
'priority' => 30
|
||||
));
|
||||
$section->add($this->translate('Tactical Overview'), array(
|
||||
$section->add(N_('Tactical Overview'), array(
|
||||
'url' => 'monitoring/tactical',
|
||||
'priority' => 40
|
||||
));
|
||||
$section->add($this->translate('Hosts'), array(
|
||||
$section->add(N_('Hosts'), array(
|
||||
'url' => 'monitoring/list/hosts',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add($this->translate('Services'), array(
|
||||
$section->add(N_('Services'), array(
|
||||
'url' => 'monitoring/list/services',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add($this->translate('Servicegroups'), array(
|
||||
$section->add(N_('Servicegroups'), array(
|
||||
'url' => 'monitoring/list/servicegroups',
|
||||
'priority' => 60
|
||||
));
|
||||
$section->add($this->translate('Hostgroups'), array(
|
||||
$section->add(N_('Hostgroups'), array(
|
||||
'url' => 'monitoring/list/hostgroups',
|
||||
'priority' => 60
|
||||
));
|
||||
$section->add($this->translate('Contacts'), array(
|
||||
$section->add(N_('Contacts'), array(
|
||||
'url' => 'monitoring/list/contacts',
|
||||
'priority' => 70
|
||||
));
|
||||
$section->add($this->translate('Contactgroups'), array(
|
||||
$section->add(N_('Contactgroups'), array(
|
||||
'url' => 'monitoring/list/contactgroups',
|
||||
'priority' => 70
|
||||
));
|
||||
$section->add($this->translate('Comments'), array(
|
||||
$section->add(N_('Comments'), array(
|
||||
'url' => 'monitoring/list/comments?comment_type=(comment|ack)',
|
||||
'priority' => 80
|
||||
));
|
||||
$section->add($this->translate('Downtimes'), array(
|
||||
$section->add(N_('Downtimes'), array(
|
||||
'url' => 'monitoring/list/downtimes',
|
||||
'priority' => 80
|
||||
));
|
||||
|
@ -184,22 +193,23 @@ $section->add($this->translate('Downtimes'), array(
|
|||
/*
|
||||
* History Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('History'), array(
|
||||
'icon' => 'rewind'
|
||||
$section = $this->menuSection(N_('History'), array(
|
||||
'icon' => 'rewind',
|
||||
'priority' => 90
|
||||
));
|
||||
$section->add($this->translate('Event Grid'), array(
|
||||
$section->add(N_('Event Grid'), array(
|
||||
'priority' => 10,
|
||||
'url' => 'monitoring/list/eventgrid'
|
||||
));
|
||||
$section->add($this->translate('Event Overview'), array(
|
||||
$section->add(N_('Event Overview'), array(
|
||||
'priority' => 20,
|
||||
'url' => 'monitoring/list/eventhistory?timestamp>=-7%20days'
|
||||
));
|
||||
$section->add($this->translate('Notifications'), array(
|
||||
$section->add(N_('Notifications'), array(
|
||||
'priority' => 30,
|
||||
'url' => 'monitoring/list/notifications',
|
||||
));
|
||||
$section->add($this->translate('Timeline'), array(
|
||||
$section->add(N_('Timeline'), array(
|
||||
'priority' => 40,
|
||||
'url' => 'monitoring/timeline'
|
||||
));
|
||||
|
@ -207,144 +217,144 @@ $section->add($this->translate('Timeline'), array(
|
|||
/*
|
||||
* Reporting Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('Reporting'), array(
|
||||
$section = $this->menuSection(N_('Reporting'), array(
|
||||
'icon' => 'barchart',
|
||||
'priority' => 100
|
||||
));
|
||||
|
||||
$section->add($this->translate('Alert Summary'), array(
|
||||
$section->add(N_('Alert Summary'), array(
|
||||
'url' => 'monitoring/alertsummary/index'
|
||||
));
|
||||
|
||||
/*
|
||||
* System Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('System'));
|
||||
$section->add($this->translate('Monitoring Health'), array(
|
||||
$section = $this->menuSection(N_('System'));
|
||||
$section->add(N_('Monitoring Health'), array(
|
||||
'url' => 'monitoring/health/info',
|
||||
'priority' => 720,
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer'
|
||||
'renderer' => 'BackendAvailabilityNavigationItemRenderer'
|
||||
));
|
||||
|
||||
/*
|
||||
* Current Incidents
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Current Incidents'));
|
||||
$dashboard = $this->dashboard(N_('Current Incidents'), array('priority' => 50));
|
||||
$dashboard->add(
|
||||
$this->translate('Service Problems'),
|
||||
N_('Service Problems'),
|
||||
'monitoring/list/services?service_problem=1&limit=10&sort=service_severity'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Recently Recovered Services'),
|
||||
N_('Recently Recovered Services'),
|
||||
'monitoring/list/services?service_state=0&limit=10&sort=service_last_state_change&dir=desc'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Host Problems'),
|
||||
N_('Host Problems'),
|
||||
'monitoring/list/hosts?host_problem=1&sort=host_severity'
|
||||
);
|
||||
|
||||
/*
|
||||
* Overview
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Overview'));
|
||||
$dashboard = $this->dashboard(N_('Overview'), array('priority' => 60));
|
||||
$dashboard->add(
|
||||
$this->translate('Service Grid'),
|
||||
N_('Service Grid'),
|
||||
'monitoring/list/servicegrid?limit=15,18'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Service Groups'),
|
||||
'/monitoring/list/servicegroups'
|
||||
N_('Service Groups'),
|
||||
'monitoring/list/servicegroups'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Host Groups'),
|
||||
'/monitoring/list/hostgroups'
|
||||
N_('Host Groups'),
|
||||
'monitoring/list/hostgroups'
|
||||
);
|
||||
|
||||
/*
|
||||
* Most Overdue
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Overdue'));
|
||||
$dashboard = $this->dashboard(N_('Overdue'), array('priority' => 70));
|
||||
$dashboard->add(
|
||||
$this->translate('Late Host Check Results'),
|
||||
N_('Late Host Check Results'),
|
||||
'monitoring/list/hosts?host_next_update<now'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Late Service Check Results'),
|
||||
N_('Late Service Check Results'),
|
||||
'monitoring/list/services?service_next_update<now'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Acknowledgements Active For At Least Three Days'),
|
||||
N_('Acknowledgements Active For At Least Three Days'),
|
||||
'monitoring/list/comments?comment_type=Ack&comment_timestamp<-3 days&sort=comment_timestamp&dir=asc'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Downtimes Active For More Than Three Days'),
|
||||
N_('Downtimes Active For More Than Three Days'),
|
||||
'monitoring/list/downtimes?downtime_is_in_effect=1&downtime_scheduled_start<-3%20days&sort=downtime_start&dir=asc'
|
||||
);
|
||||
|
||||
/*
|
||||
* Muted Objects
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Muted'));
|
||||
$dashboard = $this->dashboard(N_('Muted'), array('priority' => 80));
|
||||
$dashboard->add(
|
||||
$this->translate('Disabled Service Notifications'),
|
||||
N_('Disabled Service Notifications'),
|
||||
'monitoring/list/services?service_notifications_enabled=0&limit=10'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Disabled Host Notifications'),
|
||||
N_('Disabled Host Notifications'),
|
||||
'monitoring/list/hosts?host_notifications_enabled=0&limit=10'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Disabled Service Checks'),
|
||||
N_('Disabled Service Checks'),
|
||||
'monitoring/list/services?service_active_checks_enabled=0&limit=10'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Disabled Host Checks'),
|
||||
N_('Disabled Host Checks'),
|
||||
'monitoring/list/hosts?host_active_checks_enabled=0&limit=10'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Acknowledged Problem Services'),
|
||||
N_('Acknowledged Problem Services'),
|
||||
'monitoring/list/services?service_acknowledgement_type=2&service_problem=1&sort=service_state&limit=10'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Acknowledged Problem Hosts'),
|
||||
N_('Acknowledged Problem Hosts'),
|
||||
'monitoring/list/hosts?host_acknowledgement_type=2&host_problem=1&sort=host_severity&limit=10'
|
||||
);
|
||||
|
||||
/*
|
||||
* Activity Stream
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Activity Stream'));
|
||||
$dashboard = $this->dashboard(N_('Activity Stream'), array('priority' => 90));
|
||||
$dashboard->add(
|
||||
$this->translate('Recent Events'),
|
||||
N_('Recent Events'),
|
||||
'monitoring/list/eventhistory?timestamp>=-3%20days&sort=timestamp&dir=desc&limit=8'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Recent Hard State Changes'),
|
||||
N_('Recent Hard State Changes'),
|
||||
'monitoring/list/eventhistory?timestamp>=-3%20days&type=hard_state&sort=timestamp&dir=desc&limit=8'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Recent Notifications'),
|
||||
N_('Recent Notifications'),
|
||||
'monitoring/list/eventhistory?timestamp>=-3%20days&type=notify&sort=timestamp&dir=desc&limit=8'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Downtimes Recently Started'),
|
||||
N_('Downtimes Recently Started'),
|
||||
'monitoring/list/eventhistory?timestamp>=-3%20days&type=dt_start&sort=timestamp&dir=desc&limit=8'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Downtimes Recently Ended'),
|
||||
N_('Downtimes Recently Ended'),
|
||||
'monitoring/list/eventhistory?timestamp>=-3%20days&type=dt_end&sort=timestamp&dir=desc&limit=8'
|
||||
);
|
||||
|
||||
/*
|
||||
* Stats
|
||||
*/
|
||||
$dashboard = $this->dashboard($this->translate('Stats'));
|
||||
$dashboard = $this->dashboard(N_('Stats'), array('priority' => 99));
|
||||
$dashboard->add(
|
||||
$this->translate('Check Stats'),
|
||||
N_('Check Stats'),
|
||||
'monitoring/health/stats'
|
||||
);
|
||||
$dashboard->add(
|
||||
$this->translate('Process Information'),
|
||||
N_('Process Information'),
|
||||
'monitoring/health/info'
|
||||
);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class HoststatusQuery extends IdoQuery
|
|||
'host' => 'ho.name1 COLLATE latin1_general_ci',
|
||||
'host_action_url' => 'h.action_url',
|
||||
'host_address' => 'h.address',
|
||||
'host_address6' => 'h.address6',
|
||||
'host_alias' => 'h.alias',
|
||||
'host_display_name' => 'h.display_name COLLATE latin1_general_ci',
|
||||
'host_icon_image' => 'h.icon_image',
|
||||
|
|
|
@ -832,7 +832,7 @@ abstract class IdoQuery extends DbQuery
|
|||
list($type, $name) = $this->customvarNameToTypeName($customvar);
|
||||
$alias = ($type === 'host' ? 'hcv_' : 'scv_') . $name;
|
||||
|
||||
$this->customVars[$customvar] = $alias;
|
||||
$this->customVars[strtolower($customvar)] = $alias;
|
||||
|
||||
if ($this->hasJoinedVirtualTable('services')) {
|
||||
$leftcol = 's.' . $type . '_object_id';
|
||||
|
|
|
@ -27,6 +27,7 @@ class ServicestatusQuery extends IdoQuery
|
|||
'hosts' => array(
|
||||
'host_action_url' => 'h.action_url',
|
||||
'host_address' => 'h.address',
|
||||
'host_address6' => 'h.address6',
|
||||
'host_alias' => 'h.alias COLLATE latin1_general_ci',
|
||||
'host_display_name' => 'h.display_name COLLATE latin1_general_ci',
|
||||
'host_icon_image' => 'h.icon_image',
|
||||
|
|
|
@ -82,7 +82,7 @@ class Controller extends IcingaWebController
|
|||
'service_description',
|
||||
'servicegroup_name',
|
||||
function ($c) {
|
||||
return preg_match('/^_(?:host|service)_/', $c);
|
||||
return preg_match('/^_(?:host|service)_/i', $c);
|
||||
}
|
||||
));
|
||||
foreach ($this->getRestrictions($name) as $filter) {
|
||||
|
|
|
@ -185,7 +185,11 @@ abstract class DataView implements QueryInterface, SortRules, FilterColumns, Ite
|
|||
*/
|
||||
public function isValidFilterTarget($column)
|
||||
{
|
||||
return in_array($column, $this->getFilterColumns());
|
||||
// Customvar
|
||||
if ($column[0] === '_' && preg_match('/^_(?:host|service)_/i', $column)) {
|
||||
return true;
|
||||
}
|
||||
return in_array($column, $this->getColumns()) || in_array($column, $this->getStaticFilterColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@ class HostStatus extends DataView
|
|||
'host_display_name',
|
||||
'host_alias',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_state',
|
||||
'host_state_type',
|
||||
'host_handled',
|
||||
|
|
|
@ -18,6 +18,7 @@ class ServiceStatus extends DataView
|
|||
'host_state_type',
|
||||
'host_last_state_change',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_problem',
|
||||
'host_handled',
|
||||
'service_description',
|
||||
|
|
|
@ -95,6 +95,7 @@ class Host extends MonitoredObject
|
|||
'host_active_checks_enabled',
|
||||
'host_active_checks_enabled_changed',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_alias',
|
||||
'host_attempt',
|
||||
'host_check_command',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue