Merge branch 'master' into feature/dope-layout-5543
This commit is contained in:
commit
231c63b703
|
@ -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'],
|
||||
}
|
||||
}
|
||||
|
|
31
RELEASE.md
31
RELEASE.md
|
@ -5,22 +5,47 @@ https://dev.icinga.org/projects/icingaweb2/roadmap
|
|||
|
||||
# Release Workflow
|
||||
|
||||
## Authors
|
||||
|
||||
Update the [.mailmap](.mailmap) and [AUTHORS](AUTHORS) files:
|
||||
|
||||
$ git log --use-mailmap | grep ^Author: | cut -f2- -d' ' | sort | uniq > AUTHORS
|
||||
|
||||
Update the version number in the [icingaweb2.spec] and [VERSION] files.
|
||||
## Version
|
||||
|
||||
Update the version number in the following files:
|
||||
|
||||
* [icingaweb2.spec] (ensure that the revision is properly set)
|
||||
* [VERSION]
|
||||
* Application Version: [library/Icinga/Application/Version.php]
|
||||
* Module Versions in modules/*/module.info
|
||||
|
||||
Commands:
|
||||
|
||||
VERSION=2.0.0
|
||||
|
||||
vim icingaweb2.spec
|
||||
|
||||
echo "v$VERSION" > VERSION
|
||||
|
||||
sed -i '' "s/const VERSION = '.*'/const VERSION = '$VERSION'/g" library/Icinga/Application/Version.php
|
||||
|
||||
find . -type f -name '*.info' -exec sed -i '' "s/Version: .*/Version: $VERSION/g" {} \;
|
||||
|
||||
## Changelog
|
||||
|
||||
Update the [ChangeLog](ChangeLog) file using
|
||||
the changelog.py script.
|
||||
|
||||
Changelog:
|
||||
|
||||
$ ./changelog.py --version 2.0.0-rc1
|
||||
$ ./changelog.py --version 2.0.0
|
||||
|
||||
Wordpress:
|
||||
|
||||
$ ./changelog.py --version 2.0.0-rc1 --html --links
|
||||
$ ./changelog.py --version 2.0.0 --html --links
|
||||
|
||||
## Git Tag
|
||||
|
||||
Commit these changes to the "master" branch:
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
$Format:%H%d %ci$
|
||||
$Format:%H %ci$
|
||||
|
|
|
@ -3,13 +3,23 @@
|
|||
|
||||
namespace Icinga\Controllers;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Version;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Web\Controller;
|
||||
|
||||
class AboutController extends ActionController
|
||||
class AboutController extends Controller
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->version = Version::get();
|
||||
$this->view->modules = Icinga::app()->getModuleManager()->getLoadedModules();
|
||||
$this->view->tabs = $this->getTabs()->add(
|
||||
'about',
|
||||
array(
|
||||
'label' => $this->translate('About'),
|
||||
'title' => $this->translate('About Icinga Web 2'),
|
||||
'url' => 'about'
|
||||
)
|
||||
)->activate('about');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,13 +5,13 @@ namespace Icinga\Controllers;
|
|||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
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;
|
||||
|
||||
|
@ -21,11 +21,11 @@ use Icinga\Web\Url;
|
|||
class NavigationController extends Controller
|
||||
{
|
||||
/**
|
||||
* The default item types provided by Icinga Web 2
|
||||
* The global navigation item type configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultItemTypes;
|
||||
protected $itemTypeConfig;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -33,11 +33,19 @@ class NavigationController extends Controller
|
|||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->itemTypeConfig = Navigation::getItemTypeConfiguration();
|
||||
}
|
||||
|
||||
$this->defaultItemTypes = array(
|
||||
'menu-item' => $this->translate('Menu Entry'),
|
||||
'dashlet' => 'Dashlet'
|
||||
);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,33 +55,71 @@ class NavigationController extends Controller
|
|||
*/
|
||||
protected function listItemTypes()
|
||||
{
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
|
||||
$types = $this->defaultItemTypes;
|
||||
foreach ($moduleManager->getLoadedModules() as $module) {
|
||||
if ($this->hasPermission($moduleManager::MODULE_PERMISSION_NS . $module->getName())) {
|
||||
$moduleTypes = $module->getNavigationItems();
|
||||
if (! empty($moduleTypes)) {
|
||||
$types = array_merge($types, $moduleTypes);
|
||||
}
|
||||
}
|
||||
$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(
|
||||
Config::app('navigation')->select()->where('owner', $user->getUsername())->fetchAll(),
|
||||
iterator_to_array($user->loadNavigationConfig())
|
||||
$this->fetchSharedNavigationItemConfigs($user->getUsername()),
|
||||
$this->fetchUserNavigationItemConfigs($user->getUsername())
|
||||
));
|
||||
$ds->setKeyColumn('name');
|
||||
$query = $ds->select();
|
||||
|
||||
$this->view->types = $this->listItemTypes();
|
||||
|
@ -91,7 +137,7 @@ class NavigationController extends Controller
|
|||
array(
|
||||
'type' => $this->translate('Type'),
|
||||
'owner' => $this->translate('Shared'),
|
||||
'name' => $this->translate('Shared Navigation')
|
||||
'name' => $this->translate('Navigation')
|
||||
),
|
||||
$query
|
||||
);
|
||||
|
@ -103,13 +149,11 @@ class NavigationController extends Controller
|
|||
public function sharedAction()
|
||||
{
|
||||
$this->assertPermission('config/application/navigation');
|
||||
$config = Config::app('navigation');
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
$query = $config->select();
|
||||
$ds = new ArrayDatasource($this->fetchSharedNavigationItemConfigs());
|
||||
$query = $ds->select();
|
||||
|
||||
$removeForm = new Form();
|
||||
$removeForm->setUidDisabled();
|
||||
$removeForm->setAction(Url::fromPath('navigation/unshare'));
|
||||
$removeForm->addElement('hidden', 'name', array(
|
||||
'decorators' => array('ViewHelper')
|
||||
));
|
||||
|
@ -156,11 +200,14 @@ class NavigationController extends Controller
|
|||
{
|
||||
$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.'));
|
||||
$form->setUser($this->Auth()->getUser());
|
||||
$form->setShareConfig(Config::app('navigation'));
|
||||
|
||||
// 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());
|
||||
|
||||
|
@ -172,7 +219,7 @@ class NavigationController extends Controller
|
|||
}
|
||||
|
||||
if ($form->save()) {
|
||||
if (isset($data['type']) && $data['type'] === 'menu-item') {
|
||||
if ($data['type'] === 'menu-item') {
|
||||
$form->getResponse()->setRerenderLayout();
|
||||
}
|
||||
|
||||
|
@ -194,14 +241,22 @@ class NavigationController extends Controller
|
|||
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->setItemTypes($this->listItemTypes());
|
||||
$form->setTitle(sprintf($this->translate('Edit Navigation Item %s'), $itemName));
|
||||
$form->setUser($this->Auth()->getUser());
|
||||
$form->setShareConfig(Config::app('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) {
|
||||
|
@ -248,13 +303,17 @@ class NavigationController extends Controller
|
|||
public function removeAction()
|
||||
{
|
||||
$itemName = $this->params->getRequired('name');
|
||||
$itemType = $this->params->getRequired('type');
|
||||
$user = $this->Auth()->getUser();
|
||||
|
||||
$navigationConfigForm = new NavigationConfigForm();
|
||||
$navigationConfigForm->setUser($this->Auth()->getUser());
|
||||
$navigationConfigForm->setShareConfig(Config::app('navigation'));
|
||||
$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 Navigation Item %s'), $itemName));
|
||||
$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);
|
||||
|
@ -291,9 +350,14 @@ class NavigationController extends Controller
|
|||
$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::app('navigation'));
|
||||
$navigationConfigForm->setShareConfig(Config::navigation($itemType));
|
||||
$navigationConfigForm->setUserConfig(Config::navigation($itemType, $itemOwner));
|
||||
|
||||
$form = new Form(array(
|
||||
'onSuccess' => function ($form) use ($navigationConfigForm) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -123,12 +123,18 @@ class NavigationConfigForm extends ConfigForm
|
|||
/**
|
||||
* Return the user's navigation configuration
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getUserConfig()
|
||||
public function getUserConfig($type = null)
|
||||
{
|
||||
if ($this->userConfig === null) {
|
||||
$this->setUserConfig($this->getUser()->loadNavigationConfig());
|
||||
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;
|
||||
|
@ -151,10 +157,20 @@ class NavigationConfigForm extends ConfigForm
|
|||
/**
|
||||
* Return the shared navigation configuration
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function getShareConfig()
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -194,10 +210,9 @@ class NavigationConfigForm extends ConfigForm
|
|||
$children = $this->itemToLoad ? $this->getFlattenedChildren($this->itemToLoad) : array();
|
||||
|
||||
$names = array();
|
||||
foreach ($this->getShareConfig() as $sectionName => $sectionConfig) {
|
||||
foreach ($this->getShareConfig($type) as $sectionName => $sectionConfig) {
|
||||
if (
|
||||
$sectionName !== $this->itemToLoad
|
||||
&& $sectionConfig->type === $type
|
||||
&& $sectionConfig->owner === ($owner ?: $this->getUser()->getUsername())
|
||||
&& !in_array($sectionName, $children, true)
|
||||
) {
|
||||
|
@ -205,10 +220,9 @@ class NavigationConfigForm extends ConfigForm
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($this->getUserConfig() as $sectionName => $sectionConfig) {
|
||||
foreach ($this->getUserConfig($type) as $sectionName => $sectionConfig) {
|
||||
if (
|
||||
$sectionName !== $this->itemToLoad
|
||||
&& $sectionConfig->type === $type
|
||||
&& !in_array($sectionName, $children, true)
|
||||
) {
|
||||
$names[] = $sectionName;
|
||||
|
@ -271,29 +285,31 @@ class NavigationConfigForm extends ConfigForm
|
|||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException In case $data does not contain a navigation item name
|
||||
* @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();
|
||||
$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();
|
||||
$config = $this->getShareConfig($data['type']);
|
||||
$shared = true;
|
||||
} else {
|
||||
unset($data['users']);
|
||||
unset($data['groups']);
|
||||
}
|
||||
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'])) {
|
||||
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'], $data['type'])) {
|
||||
$data['owner'] = $this->getUser()->getUsername();
|
||||
$config = $this->getShareConfig();
|
||||
$config = $this->getShareConfig($data['type']);
|
||||
$shared = true;
|
||||
}
|
||||
|
||||
|
@ -301,9 +317,9 @@ class NavigationConfigForm extends ConfigForm
|
|||
$exists = $config->hasSection($itemName);
|
||||
if (! $exists) {
|
||||
if ($shared) {
|
||||
$exists = $this->getUserConfig()->hasSection($itemName);
|
||||
$exists = $this->getUserConfig($data['type'])->hasSection($itemName);
|
||||
} else {
|
||||
$exists = (bool) $this->getShareConfig()
|
||||
$exists = (bool) $this->getShareConfig($data['type'])
|
||||
->select()
|
||||
->where('name', $itemName)
|
||||
->where('owner', $this->getUser()->getUsername())
|
||||
|
@ -385,8 +401,7 @@ class NavigationConfigForm extends ConfigForm
|
|||
if ($ownerName === $this->getUser()->getUsername()) {
|
||||
$exists = $this->getUserConfig()->hasSection($name);
|
||||
} else {
|
||||
$owner = new User($ownerName);
|
||||
$exists = $owner->loadNavigationConfig()->hasSection($name);
|
||||
$exists = Config::navigation($itemConfig->type, $ownerName)->hasSection($name);
|
||||
}
|
||||
} else {
|
||||
$exists = (bool) $this->getShareConfig()
|
||||
|
@ -521,8 +536,7 @@ class NavigationConfigForm extends ConfigForm
|
|||
if (! $itemConfig->owner || $itemConfig->owner === $this->getUser()->getUsername()) {
|
||||
$config = $this->getUserConfig();
|
||||
} else {
|
||||
$owner = new User($itemConfig->owner);
|
||||
$config = $owner->loadNavigationConfig();
|
||||
$config = Config::navigation($itemConfig->type, $itemConfig->owner);
|
||||
}
|
||||
|
||||
foreach ($children as $child) {
|
||||
|
@ -549,6 +563,13 @@ class NavigationConfigForm extends ConfigForm
|
|||
$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(
|
||||
|
@ -606,17 +627,27 @@ class NavigationConfigForm extends ConfigForm
|
|||
}
|
||||
}
|
||||
|
||||
$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 (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)) {
|
||||
|
@ -767,12 +798,13 @@ class NavigationConfigForm extends ConfigForm
|
|||
* Return whether the given navigation item has been shared
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasBeenShared($name)
|
||||
protected function hasBeenShared($name, $type = null)
|
||||
{
|
||||
return $this->getConfigForItem($name) === $this->getShareConfig();
|
||||
return $this->getShareConfig($type) === $this->getConfigForItem($name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
namespace Icinga\Forms\Navigation;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
class NavigationItemForm extends Form
|
||||
{
|
||||
|
@ -71,4 +72,20 @@ class NavigationItemForm extends Form
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -1,25 +1,128 @@
|
|||
<div class="content">
|
||||
<h1>Icinga Web 2</h1>
|
||||
<?php
|
||||
$versionInfo = array();
|
||||
if ($version !== false) {
|
||||
foreach (array(
|
||||
'appVersion' => $this->translate('Version: %s'),
|
||||
'gitCommitID' => $this->translate('Git commit ID: %s'),
|
||||
'gitCommitDate' => $this->translate('Git commit date: %s')
|
||||
) as $key => $label) {
|
||||
if (array_key_exists($key, $version) && null !== ($value = $version[$key])) {
|
||||
$versionInfo[] = sprintf($label, htmlspecialchars($value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo (
|
||||
0 === count($versionInfo)
|
||||
? '<p class="message-error">' . $this->translate(
|
||||
'Can\'t determine Icinga Web 2\'s version'
|
||||
)
|
||||
: '<p>' . nl2br(implode("\n", $versionInfo), false)
|
||||
) . '</p>';
|
||||
?>
|
||||
<div class="controls">
|
||||
<?= $tabs; ?>
|
||||
</div>
|
||||
<div class="content about">
|
||||
<?= $this->img(
|
||||
'img/logo_icinga_big_dark.png',
|
||||
null,
|
||||
array(
|
||||
'width' => 400,
|
||||
'class' => 'about-logo'
|
||||
)
|
||||
); ?>
|
||||
<p class="about-version">
|
||||
<?php if (isset($version['appVersion'])): ?>
|
||||
<strong><?= $this->translate('Version'); ?>:</strong> <?= $this->escape($version['appVersion']); ?>
|
||||
<?php endif ?>
|
||||
<?php if (isset($version['gitCommitID'])): ?>
|
||||
<br>
|
||||
<strong><?= $this->translate('Git commit ID'); ?>:</strong> <?= $this->escape($version['gitCommitID']); ?>
|
||||
<?php endif ?>
|
||||
<?php if (isset($version['gitCommitDate'])): ?>
|
||||
<br>
|
||||
<strong><?= $this->translate('Git commit date'); ?>:</strong> <?= $this->escape($version['gitCommitDate']); ?>
|
||||
<?php endif ?>
|
||||
</p>
|
||||
<p class="about-license">
|
||||
<strong><?= $this->translate('Copyright'); ?></strong>: © 2013-<?= date('Y'); ?> <?= $this->qlink(
|
||||
$this->translate('The Icinga Project'),
|
||||
'https://www.icinga.org',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank'
|
||||
)
|
||||
); ?>
|
||||
<br>
|
||||
<strong><?= $this->translate('License'); ?></strong>: GNU GPL v2+
|
||||
</p>
|
||||
<p class="about-social">
|
||||
<?= $this->qlink(
|
||||
null,
|
||||
'https://www.twitter.com/icinga',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'icon' => 'twitter',
|
||||
'title' => $this->translate('Icinga on Twitter')
|
||||
)
|
||||
); ?> <?= $this->qlink(
|
||||
null,
|
||||
'https://www.facebook.com/icinga',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'icon' => 'facebook-squared',
|
||||
'title' => $this->translate('Icinga on Facebook')
|
||||
)
|
||||
); ?>
|
||||
</p>
|
||||
<div class="about-urls">
|
||||
<div><?= $this->qlink(
|
||||
null,
|
||||
'https://dev.icinga.org/projects/icingaweb2',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'img' => 'img/bugreport.png',
|
||||
'title' => $this->translate('Report a bug')
|
||||
)
|
||||
); ?> <?= $this->qlink(
|
||||
null,
|
||||
'https://www.icinga.org/services/support',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'img' => 'img/support.png',
|
||||
'title' => $this->translate('Support / Mailinglists')
|
||||
)
|
||||
); ?></div>
|
||||
<div><?= $this->qlink(
|
||||
null,
|
||||
'https://wiki.icinga.org',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'img' => 'img/wiki.png',
|
||||
'title' => $this->translate('Icinga Wiki')
|
||||
)
|
||||
); ?> <?= $this->qlink(
|
||||
null,
|
||||
'https://docs.icinga.org/',
|
||||
null,
|
||||
array(
|
||||
'target' => '_blank',
|
||||
'img' => 'img/docs.png',
|
||||
'title' => $this->translate('Icinga Documentation')
|
||||
)
|
||||
); ?></div>
|
||||
</div>
|
||||
<h2><?= $this->translate('Loaded modules') ?></h2>
|
||||
<table class="action alternating about-modules" data-base-target="_next">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= $this->translate('Name') ?></th>
|
||||
<th><?= $this->translate('Version') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($modules as $module): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php if ($this->hasPermission('config/modules')): ?>
|
||||
<?= $this->qlink(
|
||||
$module->getName(),
|
||||
'config/module/',
|
||||
array('name' => $module->getName()),
|
||||
array('title' => sprintf($this->translate('Show the overview of the %s module'), $module->getName()))
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<?= $this->escape($module->getName()); ?>
|
||||
<?php endif ?>
|
||||
<td>
|
||||
<?= $this->escape($module->getVersion()); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -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; ?>
|
||||
|
|
|
@ -22,14 +22,17 @@
|
|||
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $name => $item): ?>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><?= $this->qlink(
|
||||
$name,
|
||||
$item->name,
|
||||
'navigation/edit',
|
||||
array('name' => $name),
|
||||
array(
|
||||
'title' => sprintf($this->translate('Edit navigation item %s'), $name)
|
||||
'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])
|
||||
|
@ -39,10 +42,13 @@
|
|||
<td><?= $this->qlink(
|
||||
'',
|
||||
'navigation/remove',
|
||||
array('name' => $name),
|
||||
array(
|
||||
'name' => $item->name,
|
||||
'type' => $item->type
|
||||
),
|
||||
array(
|
||||
'icon' => 'trash',
|
||||
'title' => sprintf($this->translate('Remove navigation item %s'), $name)
|
||||
'title' => sprintf($this->translate('Remove navigation item %s'), $item->name)
|
||||
)
|
||||
); ?></td>
|
||||
</tr>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<?php if (! $this->compact): ?>
|
||||
<?php
|
||||
|
||||
use Icinga\Web\Url;
|
||||
|
||||
if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
<?= $this->sortBox; ?>
|
||||
|
@ -19,17 +23,19 @@
|
|||
<th style="width: 5em"><?= $this->translate('Unshare'); ?></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $name => $item): ?>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><?= $this->qlink(
|
||||
$name,
|
||||
$item->name,
|
||||
'navigation/edit',
|
||||
array(
|
||||
'name' => $name,
|
||||
'name' => $item->name,
|
||||
'type' => $item->type,
|
||||
'owner' => $item->owner,
|
||||
'referrer' => 'shared'
|
||||
),
|
||||
array(
|
||||
'title' => sprintf($this->translate('Edit shared navigation item %s'), $name)
|
||||
'title' => sprintf($this->translate('Edit shared navigation item %s'), $item->name)
|
||||
)
|
||||
); ?></td>
|
||||
<td><?= $item->type && isset($types[$item->type])
|
||||
|
@ -48,7 +54,12 @@
|
|||
)
|
||||
); ?></td>
|
||||
<?php else: ?>
|
||||
<td data-base-target="_self"><?= $removeForm->setDefault('name', $name); ?></td>
|
||||
<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 ?>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,7 +13,9 @@ 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.
|
||||
|
@ -41,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
|
||||
*
|
||||
|
@ -416,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
|
||||
*
|
||||
|
|
|
@ -1014,16 +1014,21 @@ class Module
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide a new type of configurable navigation item with a optional label
|
||||
* 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)
|
||||
protected function provideNavigationItem($type, $label = null, $config = null)
|
||||
{
|
||||
$this->navigationItems[$type] = $label ?: $type;
|
||||
$this->navigationItems[$type] = array(
|
||||
'label' => $label,
|
||||
'config' => $config
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,33 +8,41 @@ namespace Icinga\Application;
|
|||
*/
|
||||
class Version
|
||||
{
|
||||
const VERSION = '2.0.0-rc1';
|
||||
|
||||
/**
|
||||
* Get the version of this instance of Icinga Web 2
|
||||
*
|
||||
* @return array|false array on success, false otherwise
|
||||
* @return array
|
||||
*/
|
||||
public static function get()
|
||||
{
|
||||
if (false === ($appVersion = @file_get_contents(
|
||||
Icinga::app()->getApplicationDir() . DIRECTORY_SEPARATOR . 'VERSION'
|
||||
))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
if (false === ($res = preg_match(
|
||||
'/(?<!.)\s*(?P<gitCommitID>\w+)(?:\s*\(.*?(?:(?<=[\(,])\s*tag\s*:\s*v(?P<appVersion>.+?)\s*(?=[\),]).*?)?\))?\s*(?P<gitCommitDate>\S+)/ms',
|
||||
$appVersion,
|
||||
$matches
|
||||
)) || $res === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($matches as $key => $value) {
|
||||
if (is_int($key) || $value === '') {
|
||||
unset($matches[$key]);
|
||||
$version = array('appVersion' => self::VERSION);
|
||||
if (false !== ($appVersion = @file_get_contents(Icinga::app()->getApplicationDir('VERSION')))) {
|
||||
$matches = array();
|
||||
if (@preg_match('/^(?P<gitCommitID>\w+) (?P<gitCommitDate>\S+)/', $appVersion, $matches)) {
|
||||
return array_merge($version, $matches);
|
||||
}
|
||||
}
|
||||
return $matches;
|
||||
|
||||
$gitDir = Icinga::app()->getBaseDir('.git');
|
||||
$gitHead = @file_get_contents($gitDir . DIRECTORY_SEPARATOR . 'HEAD');
|
||||
if (false !== $gitHead) {
|
||||
$matches = array();
|
||||
if (@preg_match('/(?<!.)ref:\s+(.+?)$/ms', $gitHead, $matches)) {
|
||||
$gitCommitID = @file_get_contents($gitDir . DIRECTORY_SEPARATOR . $matches[1]);
|
||||
} else {
|
||||
$gitCommitID = $gitHead;
|
||||
}
|
||||
|
||||
if (false !== $gitCommitID) {
|
||||
$matches = array();
|
||||
if (@preg_match('/(?<!.)(?P<gitCommitID>[0-9a-f]+)$/ms', $gitCommitID, $matches)) {
|
||||
return array_merge($version, $matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,12 +179,11 @@ class Web extends EmbeddedWeb
|
|||
*/
|
||||
public function getSharedNavigation($type)
|
||||
{
|
||||
$config = Config::app('navigation')->getConfigObject();
|
||||
$config->setKeyColumn('name');
|
||||
$config = Config::navigation($type === 'dashboard-pane' ? 'dashlet' : $type);
|
||||
|
||||
if ($type === 'dashboard-pane') {
|
||||
$panes = array();
|
||||
foreach ($config->select()->where('type', 'dashlet') as $dashletName => $dashletConfig) {
|
||||
foreach ($config as $dashletName => $dashletConfig) {
|
||||
if ($this->hasAccessToSharedNavigationItem($dashletConfig)) {
|
||||
// TODO: Throw ConfigurationError if pane or url is missing
|
||||
$panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url;
|
||||
|
@ -203,7 +202,7 @@ class Web extends EmbeddedWeb
|
|||
}
|
||||
} else {
|
||||
$items = array();
|
||||
foreach ($config->select()->where('type', $type) as $name => $typeConfig) {
|
||||
foreach ($config as $name => $typeConfig) {
|
||||
if ($this->hasAccessToSharedNavigationItem($typeConfig)) {
|
||||
$items[$name] = $typeConfig;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -479,22 +479,6 @@ class User
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return this user's navigation configuration
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
public function loadNavigationConfig()
|
||||
{
|
||||
return Config::fromIni(
|
||||
Config::resolvePath('preferences')
|
||||
. DIRECTORY_SEPARATOR
|
||||
. $this->getUsername()
|
||||
. DIRECTORY_SEPARATOR
|
||||
. 'navigation.ini'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return this user's configured navigation of the given type
|
||||
*
|
||||
|
@ -504,12 +488,11 @@ class User
|
|||
*/
|
||||
public function getNavigation($type)
|
||||
{
|
||||
$config = $this->loadNavigationConfig();
|
||||
$config->getConfigObject()->setKeyColumn('name');
|
||||
$config = Config::navigation($type === 'dashboard-pane' ? 'dashlet' : $type, $this->getUsername());
|
||||
|
||||
if ($type === 'dashboard-pane') {
|
||||
$panes = array();
|
||||
foreach ($config->select()->where('type', 'dashlet') as $dashletName => $dashletConfig) {
|
||||
foreach ($config as $dashletName => $dashletConfig) {
|
||||
// TODO: Throw ConfigurationError if pane or url is missing
|
||||
$panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url;
|
||||
}
|
||||
|
@ -525,7 +508,7 @@ class User
|
|||
);
|
||||
}
|
||||
} else {
|
||||
$navigation = Navigation::fromConfig($config->select()->where('type', $type));
|
||||
$navigation = Navigation::fromConfig($config);
|
||||
}
|
||||
|
||||
return $navigation;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -429,12 +429,14 @@ class Navigation implements ArrayAccess, Countable, IteratorAggregate
|
|||
*/
|
||||
public function load($type)
|
||||
{
|
||||
// Shareables
|
||||
$this->merge(Icinga::app()->getSharedNavigation($type));
|
||||
|
||||
// User Preferences
|
||||
$user = Auth::getInstance()->getUser();
|
||||
$this->merge($user->getNavigation($type));
|
||||
if ($type !== 'dashboard-pane') {
|
||||
// Shareables
|
||||
$this->merge(Icinga::app()->getSharedNavigation($type));
|
||||
|
||||
// User Preferences
|
||||
$this->merge($user->getNavigation($type));
|
||||
}
|
||||
|
||||
// Modules
|
||||
$moduleManager = Icinga::app()->getModuleManager();
|
||||
|
@ -451,6 +453,39 @@ class Navigation implements ArrayAccess, Countable, IteratorAggregate
|
|||
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
|
||||
*
|
||||
|
|
|
@ -36,6 +36,13 @@ class NavigationItemRenderer
|
|||
*/
|
||||
protected $internalLinkTargets;
|
||||
|
||||
/**
|
||||
* Whether to escape the label
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeLabel;
|
||||
|
||||
/**
|
||||
* Create a new NavigationItemRenderer
|
||||
*
|
||||
|
@ -126,6 +133,29 @@ class NavigationItemRenderer
|
|||
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
|
||||
*
|
||||
|
@ -144,7 +174,9 @@ class NavigationItemRenderer
|
|||
);
|
||||
}
|
||||
|
||||
$label = $this->view()->escape($item->getLabel());
|
||||
$label = $this->getEscapeLabel()
|
||||
? $this->view()->escape($item->getLabel())
|
||||
: $item->getLabel();
|
||||
if (($icon = $item->getIcon()) !== null) {
|
||||
$label = $this->view()->icon($icon) . $label;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ class StyleSheet
|
|||
'css/icinga/selection-toolbar.less',
|
||||
'css/icinga/login.less',
|
||||
'css/icinga/controls.less',
|
||||
'css/icinga/dev.less'
|
||||
'css/icinga/dev.less',
|
||||
'css/icinga/logo.less',
|
||||
'css/icinga/about.less'
|
||||
);
|
||||
|
||||
public static function compileForPdf()
|
||||
|
|
|
@ -75,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'])) {
|
||||
|
@ -159,16 +159,17 @@ class Url
|
|||
if (isset($urlParts['path'])) {
|
||||
$urlPath = $urlParts['path'];
|
||||
if ($urlPath && $urlPath[0] === '/') {
|
||||
$baseUrl = '';
|
||||
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();
|
||||
}
|
||||
|
||||
if ($baseUrl && !$urlObject->isExternal() && strpos($urlPath, $baseUrl) === 0) {
|
||||
$urlObject->setPath(substr($urlPath, strlen($baseUrl)));
|
||||
} else {
|
||||
$urlObject->setPath($urlPath);
|
||||
}
|
||||
$urlObject->setPath($urlPath);
|
||||
} elseif (! $baseUrl) {
|
||||
$baseUrl = $request->getBaseUrl();
|
||||
}
|
||||
|
@ -255,7 +256,7 @@ class Url
|
|||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = ltrim($path, '/');
|
||||
$this->path = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,11 @@ $this->addHelperFunction('qlink', function ($title, $url, $params = null, $prope
|
|||
$icon = $view->icon($properties['icon']);
|
||||
unset($properties['icon']);
|
||||
}
|
||||
|
||||
if (array_key_exists('img', $properties)) {
|
||||
$icon = $view->img($properties['img']);
|
||||
unset($properties['img']);
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
|
|
|
@ -85,6 +85,7 @@ class Dashboard extends AbstractWidget
|
|||
}
|
||||
|
||||
$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);
|
||||
|
@ -628,6 +614,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!)
|
||||
|
|
|
@ -16,7 +16,11 @@ foreach ($object->getActionUrls() as $i => $link) {
|
|||
'Action ' . ($i + 1) . $newTabInfo,
|
||||
array(
|
||||
'url' => $link,
|
||||
'target' => '_blank'
|
||||
'target' => '_blank',
|
||||
'renderer' => array(
|
||||
'NavigationItemRenderer',
|
||||
'escape_label' => false
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php foreach ($object->customvars as $name => $value): ?>
|
||||
<tr>
|
||||
<th><?= $this->escape($name) ?></th>
|
||||
<th><?= $this->escape(ucwords(str_replace('_', ' ', strtolower($name)))) ?></th>
|
||||
<td><?= $this->customvar($value) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use Icinga\Web\Navigation\Navigation;
|
||||
|
||||
$navigation = new Navigation();
|
||||
$navigation->load($object->getType() . '-note');
|
||||
//$navigation->load($object->getType() . '-note');
|
||||
foreach ($navigation as $item) {
|
||||
$item->setObject($object);
|
||||
}
|
||||
|
@ -26,7 +26,11 @@ if (! empty($links)) {
|
|||
$this->escape($link) . $newTabInfo,
|
||||
array(
|
||||
'url' => $link,
|
||||
'target' => '_blank'
|
||||
'target' => '_blank',
|
||||
'renderer' => array(
|
||||
'NavigationItemRenderer',
|
||||
'escape_label' => false
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -14,11 +14,13 @@ class Macro
|
|||
* @var array
|
||||
*/
|
||||
private static $icingaMacros = array(
|
||||
'HOSTNAME' => 'host_name',
|
||||
'HOSTADDRESS' => 'host_address',
|
||||
'SERVICEDESC' => 'service_description',
|
||||
'host.name' => 'host_name',
|
||||
'host.address' => 'host_address',
|
||||
'HOSTNAME' => 'host_name',
|
||||
'HOSTADDRESS' => 'host_address',
|
||||
'HOSTADDRESS6' => 'host_address6',
|
||||
'SERVICEDESC' => 'service_description',
|
||||
'host.name' => 'host_name',
|
||||
'host.address' => 'host_address',
|
||||
'host.address6' => 'host_address6',
|
||||
'service.description' => 'service_description'
|
||||
);
|
||||
|
||||
|
@ -58,8 +60,9 @@ class Macro
|
|||
if (isset(self::$icingaMacros[$macro]) && isset($object->{self::$icingaMacros[$macro]})) {
|
||||
return $object->{self::$icingaMacros[$macro]};
|
||||
}
|
||||
if (isset($object->customvars[$macro])) {
|
||||
return $object->customvars[$macro];
|
||||
$customVar = strtolower($macro);
|
||||
if (isset($object->customvars[$customVar])) {
|
||||
return $object->customvars[$customVar];
|
||||
}
|
||||
|
||||
return $macro;
|
||||
|
|
|
@ -239,7 +239,7 @@ abstract class MonitoredObject implements Filterable
|
|||
|
||||
foreach ($this->customvars as $name => $value) {
|
||||
if (! is_object($value)) {
|
||||
$row->{'_' . $this->getType() . '_' . strtolower(str_replace(' ', '_', $name))} = $value;
|
||||
$row->{'_' . $this->getType() . '_' . $name} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -477,8 +477,8 @@ abstract class MonitoredObject implements Filterable
|
|||
$this->customvars = array();
|
||||
|
||||
$customvars = $query->getQuery()->fetchAll();
|
||||
foreach ($customvars as $name => $cv) {
|
||||
$name = ucwords(str_replace('_', ' ', strtolower($cv->varname)));
|
||||
foreach ($customvars as $cv) {
|
||||
$name = strtolower($cv->varname);
|
||||
if ($blacklistPattern && preg_match($blacklistPattern, $cv->varname)) {
|
||||
$this->customvars[$name] = '***';
|
||||
} elseif ($cv->is_json) {
|
||||
|
|
|
@ -112,6 +112,7 @@ class Service extends MonitoredObject
|
|||
'host_acknowledged',
|
||||
'host_active_checks_enabled',
|
||||
'host_address',
|
||||
'host_address6',
|
||||
'host_alias',
|
||||
'host_display_name',
|
||||
'host_handled',
|
||||
|
|
|
@ -13,6 +13,7 @@ use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandFor
|
|||
use Icinga\Web\Hook;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget\Tabextension\DashboardAction;
|
||||
use Icinga\Web\Widget\Tabextension\MenuAction;
|
||||
|
||||
/**
|
||||
* Base class for the host and service controller
|
||||
|
@ -232,6 +233,6 @@ abstract class MonitoredObjectController extends Controller
|
|||
)
|
||||
);
|
||||
}
|
||||
$tabs->extend(new DashboardAction());
|
||||
$tabs->extend(new DashboardAction())->extend(new MenuAction());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,14 @@ class MacroTest extends BaseTestCase
|
|||
$hostMock = Mockery::mock('host');
|
||||
$hostMock->host_name = 'test';
|
||||
$hostMock->host_address = '1.1.1.1';
|
||||
$hostMock->host_address6 = '::1';
|
||||
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTNAME$', $hostMock), $hostMock->host_name);
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTADDRESS$', $hostMock), $hostMock->host_address);
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTADDRESS6$', $hostMock), $hostMock->host_address6);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.name$', $hostMock), $hostMock->host_name);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.address$', $hostMock), $hostMock->host_address);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.address6$', $hostMock), $hostMock->host_address6);
|
||||
}
|
||||
|
||||
public function testServiceMacros()
|
||||
|
@ -28,13 +31,16 @@ class MacroTest extends BaseTestCase
|
|||
$svcMock = Mockery::mock('service');
|
||||
$svcMock->host_name = 'test';
|
||||
$svcMock->host_address = '1.1.1.1';
|
||||
$svcMock->host_address6 = '::1';
|
||||
$svcMock->service_description = 'a service';
|
||||
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTNAME$', $svcMock), $svcMock->host_name);
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTADDRESS$', $svcMock), $svcMock->host_address);
|
||||
$this->assertEquals(Macro::resolveMacros('$HOSTADDRESS6$', $svcMock), $svcMock->host_address6);
|
||||
$this->assertEquals(Macro::resolveMacros('$SERVICEDESC$', $svcMock), $svcMock->service_description);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.name$', $svcMock), $svcMock->host_name);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.address$', $svcMock), $svcMock->host_address);
|
||||
$this->assertEquals(Macro::resolveMacros('$host.address6$', $svcMock), $svcMock->host_address6);
|
||||
$this->assertEquals(Macro::resolveMacros('$service.description$', $svcMock), $svcMock->service_description);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<?php if ($success): ?>
|
||||
<?= $this->qlink(
|
||||
$this->translate('Login to Icinga Web 2'),
|
||||
'authentication/login',
|
||||
'authentication/login?renderLayout',
|
||||
null,
|
||||
array(
|
||||
'class' => 'button-like login',
|
||||
|
@ -30,4 +30,4 @@
|
|||
<pre class="log-output"><?= join("\n\n", array_map(function($a) {
|
||||
return join("\n", $a);
|
||||
}, $report)); ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
div.about {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
|
||||
.about-modules {
|
||||
text-align: initial;
|
||||
}
|
||||
}
|
|
@ -271,14 +271,7 @@ html {
|
|||
}
|
||||
|
||||
#login {
|
||||
.logo .image img {
|
||||
width: 70%;
|
||||
}
|
||||
.form {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.form label {
|
||||
.below-logo label {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
.logo {
|
||||
background-color: @colorPetrol;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 60%;
|
||||
border-bottom: 1px solid #d9d9d9d;
|
||||
text-align: center;
|
||||
-webkit-box-shadow: 0 3px 7px -3px #000;
|
||||
-moz-box-shadow: 0 3px 7px -3px #000;
|
||||
box-shadow: 0 3px 7px -3px #000;
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
width: 375px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.below-logo {
|
||||
position: absolute;
|
||||
font-size: 0.9em;
|
||||
top: 45%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#layout.minimal-layout {
|
||||
.logo .image img {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.below-logo {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,36 @@
|
|||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
;(function(Icinga, $) {
|
||||
'use strict';
|
||||
|
||||
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||
|
||||
var Selectable = function(icinga) {
|
||||
Icinga.EventListener.call(this, icinga);
|
||||
this.on('rendered', this.onRendered, this);
|
||||
};
|
||||
|
||||
$.extend(Selectable.prototype, new Icinga.EventListener(), {
|
||||
onRendered: function(e) {
|
||||
$('.selectable', e.target).on('dblclick', e.data.self.selectText);
|
||||
},
|
||||
|
||||
selectText: function(e) {
|
||||
var b = document.body,
|
||||
r;
|
||||
if (b.createTextRange) {
|
||||
r = b.createTextRange();
|
||||
r.moveToElementText(e.target);
|
||||
r.select();
|
||||
} else if (window.getSelection) {
|
||||
var s = window.getSelection();
|
||||
r = document.createRange();
|
||||
r.selectNodeContents(e.target);
|
||||
s.removeAllRanges();
|
||||
s.addRange(r);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Icinga.Behaviors.Selectable = Selectable;
|
||||
})(Icinga, jQuery);
|
Loading…
Reference in New Issue