Merge branch 'master' into feature/ipv6-addresses-9645-9826

This commit is contained in:
Eric Lippmann 2015-09-28 13:09:30 +02:00
commit 2ff822c2ac
1987 changed files with 92693 additions and 32538 deletions

View File

@ -1,10 +1,10 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;
namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Version;
use Icinga\Web\Controller\ActionController;
class AboutController extends ActionController
{

View File

@ -1,11 +1,9 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;
namespace Icinga\Controllers;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Forms\Authentication\LoginForm;
use Icinga\Web\Controller;
use Icinga\Web\Url;
@ -16,12 +14,15 @@ use Icinga\Web\Url;
class AuthenticationController extends Controller
{
/**
* This controller does not require authentication
*
* @var bool
* {@inheritdoc}
*/
protected $requiresAuthentication = false;
/**
* {@inheritdoc}
*/
protected $innerLayout = 'inline';
/**
* Log into the application
*/
@ -36,6 +37,14 @@ class AuthenticationController extends Controller
$this->redirectNow($form->getRedirectUrl());
}
if (! $requiresSetup) {
if (! $this->getRequest()->hasCookieSupport()) {
$this
->getResponse()
->setBody("Cookies must be enabled to run this application.\n")
->setHttpResponseCode(403)
->sendResponse();
exit();
}
$form->handleRequest();
}
$this->view->form = $form;

View File

@ -1,21 +1,24 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use InvalidArgumentException;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Modules\Module;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserBackendConfigForm;
use Icinga\Forms\Config\UserBackendReorderForm;
use Icinga\Forms\Config\GeneralConfigForm;
use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Forms\Config\UserBackendConfigForm;
use Icinga\Forms\Config\UserBackendReorderForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Security\SecurityException;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget;
/**
@ -332,7 +335,7 @@ class ConfigController extends Controller
public function resourceAction()
{
$this->assertPermission('config/application/resources');
$this->view->resources = Config::app('resources', true)->keys();
$this->view->resources = Config::app('resources', true);
$this->createApplicationTabs()->activate('resource');
}

View File

@ -1,12 +1,16 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Zend_Controller_Action_Exception;
use Icinga\Exception\ProgrammingError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Dashboard\DashletForm;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Url;
use Icinga\Web\Widget\Dashboard;
use Icinga\Web\Widget\Tabextension\DashboardSettings;

View File

@ -1,8 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Web\Response\JsonResponse;
use Zend_Controller_Plugin_ErrorHandler;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Exception\Http\HttpBadRequestException;
use Icinga\Exception\Http\HttpMethodNotAllowedException;
use Icinga\Exception\Http\HttpNotFoundException;
use Icinga\Exception\MissingParameterException;
@ -14,6 +19,9 @@ use Icinga\Web\Controller\ActionController;
*/
class ErrorController extends ActionController
{
/**
* {@inheritdoc}
*/
protected $requiresAuthentication = false;
/**
@ -23,10 +31,14 @@ class ErrorController extends ActionController
{
$error = $this->_getParam('error_handler');
$exception = $error->exception;
/** @var \Exception $exception */
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:
@ -37,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;
@ -61,6 +75,9 @@ class ErrorController extends ActionController
'Missing parameter ' . $exception->getParameter()
);
break;
case $exception instanceof HttpBadRequestException:
$this->getResponse()->setHttpResponseCode(400);
break;
case $exception instanceof SecurityException:
$this->getResponse()->setHttpResponseCode(403);
break;
@ -74,6 +91,14 @@ class ErrorController extends ActionController
}
break;
}
if ($this->getRequest()->isApiRequest()) {
$this->getResponse()->json()
->setErrorMessage($this->view->message)
->sendResponse();
}
$this->view->request = $error->request;
$this->view->hideControls = ! $isAuthenticated;
}
}

View File

@ -1,92 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
use Icinga\Filter\Filter;
use Icinga\Application\Logger;
/**
* Application wide interface for filtering
*/
class FilterController extends ActionController
{
/**
* The current filter registry
*
* @var Filter
*/
private $registry;
private $moduleRegistry;
/**
* Entry point for filtering, uses the filter_domain and filter_module request parameter
* to determine which filter registry should be used
*/
public function indexAction()
{
$this->registry = new Filter();
$query = $this->getRequest()->getParam('query', '');
$target = $this->getRequest()->getParam('filter_domain', '');
if ($this->getRequest()->getHeader('accept') == 'application/json') {
$this->getResponse()->setHeader('Content-Type', 'application/json');
$this->setupQueries(
$target,
$this->getParam('filter_module', '')
);
$this->_helper->json($this->parse($query, $target));
} else {
$this->setupQueries(
$target,
$this->getParam('filter_module')
);
$urlTarget = $this->parse($query, $target);
$this->redirect($urlTarget['urlParam']);
}
}
/**
* Set up the query handler for the given domain and module
*
* @param string $domain The domain to use
* @param string $module The module to use
*/
private function setupQueries($domain, $module = 'default')
{
$class = '\\Icinga\\Module\\' . ucfirst($module) . '\\Filter\\Registry';
$factory = strtolower($domain) . 'Filter';
$this->moduleRegistry = $class;
$this->registry->addDomain($class::$factory());
}
/**
* Parse the given query text and returns the json as expected by the semantic search box
*
* @param String $text The query to parse
* @return array The result structure to be returned in json format
*/
private function parse($text, $target)
{
try {
$queryTree = $this->registry->createQueryTreeForFilter($text);
$registry = $this->moduleRegistry;
return array(
'state' => 'success',
'proposals' => $this->registry->getProposalsForQuery($text),
'urlParam' => $registry::getUrlForTarget($target, $queryTree),
'valid' => count($this->registry->getIgnoredQueryParts()) === 0
);
} catch (\Exception $exc) {
Logger::error($exc);
$this->getResponse()->setHttpResponseCode(500);
return array(
'state' => 'error',
'message' => 'Search service is currently not available'
);
}
}
}

View File

@ -1,10 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Data\Reducible;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Reducible;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\UserGroup\AddMemberForm;
use Icinga\Forms\Config\UserGroup\UserGroupForm;
@ -54,19 +57,12 @@ class GroupController extends AuthBackendController
}
$query = $backend->select(array('group_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('group', 'user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->groups = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupFilterControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
@ -101,15 +97,7 @@ class GroupController extends AuthBackendController
->from('group_membership', array('user_name'))
->where('group_name', $groupName);
$filterEditor = Widget::create('filterEditor')
->setQuery($members)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'group')
->ignoreParams('page')
->handleRequest($this->getRequest());
$members->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupFilterControl($members, null, array('user'));
$this->setupPaginationControl($members);
$this->setupLimitControl();
$this->setupSortControl(
@ -146,7 +134,7 @@ class GroupController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -1,10 +1,9 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
# namespace Icinga\Application\Controllers;
namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController;
use Icinga\Application\Benchmark;
use Icinga\Web\Url;
/**
@ -18,6 +17,7 @@ class IndexController extends ActionController
public function preDispatch()
{
if ($this->getRequest()->getActionName() !== 'welcome') {
// @TODO(el): Avoid landing page redirects: https://dev.icinga.org/issues/9656
$this->redirectNow(Url::fromRequest()->setPath('dashboard'));
}
}

View File

@ -1,11 +1,10 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\MenuRenderer;
namespace Icinga\Controllers;
use Icinga\Application\Icinga;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Hook;
use Icinga\Web\Menu;
use Icinga\Web\Url;
/**
* Create complex layout parts
@ -19,9 +18,6 @@ class LayoutController extends ActionController
{
$this->setAutorefreshInterval(15);
$this->_helper->layout()->disableLayout();
$url = Url::fromRequest();
$menu = new MenuRenderer(Menu::load(), $url->getRelativeUrl());
$this->view->menuRenderer = $menu->useCustomRenderer();
$this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer();
}
}

View File

@ -1,6 +1,8 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
@ -11,8 +13,6 @@ use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
/**
* Class ListController
*
* Application wide controller for various listing actions
*/
class ListController extends Controller

View File

@ -0,0 +1,346 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
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\Notification;
use Icinga\Web\Url;
/**
* Navigation configuration
*/
class NavigationController extends Controller
{
/**
* The default item types provided by Icinga Web 2
*
* @var array
*/
protected $defaultItemTypes;
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
$this->defaultItemTypes = array(
'menu-item' => $this->translate('Menu Entry'),
'dashlet' => 'Dashlet'
);
}
/**
* Return a list of available navigation item types
*
* @return array
*/
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);
}
}
}
return $types;
}
/**
* 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())
));
$ds->setKeyColumn('name');
$query = $ds->select();
$this->view->types = $this->listItemTypes();
$this->view->items = $query;
$this->getTabs()->add(
'navigation',
array(
'title' => $this->translate('List and configure your own navigation items'),
'label' => $this->translate('Navigation'),
'url' => 'navigation'
)
)->activate('navigation');
$this->setupSortControl(
array(
'type' => $this->translate('Type'),
'owner' => $this->translate('Shared'),
'name' => $this->translate('Shared Navigation')
),
$query
);
}
/**
* List all shared navigation items
*/
public function sharedAction()
{
$this->assertPermission('config/application/navigation');
$config = Config::app('navigation');
$config->getConfigObject()->setKeyColumn('name');
$query = $config->select();
$removeForm = new Form();
$removeForm->setUidDisabled();
$removeForm->setAction(Url::fromPath('navigation/unshare'));
$removeForm->addElement('hidden', 'name', array(
'decorators' => array('ViewHelper')
));
$removeForm->addElement('hidden', 'redirect', array(
'value' => Url::fromPath('navigation/shared'),
'decorators' => array('ViewHelper')
));
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),
'title' => $this->translate('Unshare this navigation item')
));
$this->view->removeForm = $removeForm;
$this->view->types = $this->listItemTypes();
$this->view->items = $query;
$this->getTabs()->add(
'navigation/shared',
array(
'title' => $this->translate('List and configure shared navigation items'),
'label' => $this->translate('Shared Navigation'),
'url' => 'navigation/shared'
)
)->activate('navigation/shared');
$this->setupSortControl(
array(
'type' => $this->translate('Type'),
'owner' => $this->translate('Owner'),
'name' => $this->translate('Shared Navigation')
),
$query
);
}
/**
* Add a navigation item
*/
public function addAction()
{
$form = new NavigationConfigForm();
$form->setRedirectUrl('navigation');
$form->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'));
$form->setOnSuccess(function (NavigationConfigForm $form) {
$data = array_filter($form->getValues());
try {
$form->add($data);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
if (isset($data['type']) && $data['type'] === 'menu-item') {
$form->getResponse()->setRerenderLayout();
}
Notification::success(t('Navigation item successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Edit a navigation item
*/
public function editAction()
{
$itemName = $this->params->getRequired('name');
$referrer = $this->params->get('referrer', 'index');
$form = new NavigationConfigForm();
$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->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) {
$data = array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
);
try {
$form->edit($itemName, $data);
} catch (NotFoundError $e) {
throw $e;
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
if (isset($data['type']) && $data['type'] === 'menu-item') {
$form->getResponse()->setRerenderLayout();
}
Notification::success(sprintf(t('Navigation item "%s" successfully updated'), $itemName));
return true;
}
return false;
});
try {
$form->load($itemName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Navigation item "%s" not found'), $itemName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a navigation item
*/
public function removeAction()
{
$itemName = $this->params->getRequired('name');
$navigationConfigForm = new NavigationConfigForm();
$navigationConfigForm->setUser($this->Auth()->getUser());
$navigationConfigForm->setShareConfig(Config::app('navigation'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('navigation');
$form->setTitle(sprintf($this->translate('Remove Navigation Item %s'), $itemName));
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($itemName, $navigationConfigForm) {
try {
$itemConfig = $navigationConfigForm->delete($itemName);
} catch (NotFoundError $e) {
Notification::success(sprintf(t('Navigation Item "%s" not found. No action required'), $itemName));
return true;
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($navigationConfigForm->save()) {
if ($itemConfig->type === 'menu-item') {
$form->getResponse()->setRerenderLayout();
}
Notification::success(sprintf(t('Navigation Item "%s" successfully removed'), $itemName));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Unshare a navigation item
*/
public function unshareAction()
{
$this->assertPermission('config/application/navigation');
$this->assertHttpMethod('POST');
$navigationConfigForm = new NavigationConfigForm();
$navigationConfigForm->setUser($this->Auth()->getUser());
$navigationConfigForm->setShareConfig(Config::app('navigation'));
$form = new Form(array(
'onSuccess' => function ($form) use ($navigationConfigForm) {
$itemName = $form->getValue('name');
try {
$newConfig = $navigationConfigForm->unshare($itemName);
if ($navigationConfigForm->save()) {
if ($newConfig->getSection($itemName)->type === 'menu-item') {
$form->getResponse()->setRerenderLayout();
}
Notification::success(sprintf(
t('Navigation item "%s" has been unshared'),
$form->getValue('name')
));
} else {
// TODO: It failed obviously to write one of the configs, so we're leaving the user in
// a inconsistent state. Luckily, it's nothing lost but possibly duplicated...
Notification::error(sprintf(
t('Failed to unshare navigation item "%s"'),
$form->getValue('name')
));
}
} catch (NotFoundError $e) {
throw $e;
} catch (Exception $e) {
Notification::error($e->getMessage());
}
$redirect = $form->getValue('redirect');
if (! empty($redirect)) {
$form->setRedirectUrl(htmlspecialchars_decode($redirect));
}
return true;
}
));
$form->setUidDisabled();
$form->setSubmitLabel('btn_submit'); // Required to ensure that isSubmitted() is called
$form->addElement('hidden', 'name', array('required' => true));
$form->addElement('hidden', 'redirect');
try {
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Navigation item "%s" not found'), $form->getValue('name')));
}
}
}

View File

@ -1,16 +1,20 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config;
use Icinga\Data\ConfigObject;
use Icinga\Forms\PreferenceForm;
use Icinga\User\Preferences\PreferencesStore;
use Icinga\Web\Controller\BasePreferenceController;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tab;
use Icinga\Application\Config;
use Icinga\Forms\PreferenceForm;
use Icinga\Data\ConfigObject;
use Icinga\User\Preferences\PreferencesStore;
/**
* Application wide preference controller for user preferences
*
* @TODO(el): Rename to PreferencesController: https://dev.icinga.org/issues/10014
*/
class PreferenceController extends BasePreferenceController
{
@ -28,7 +32,7 @@ class PreferenceController extends BasePreferenceController
array(
'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'),
'label' => t('Preferences'),
'url' => Url::fromPath('/preference')
'url' => Url::fromPath('preference')
)
)
);

View File

@ -1,12 +1,21 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Application\Config;
use Icinga\Exception\AlreadyExistsException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Security\RoleForm;
use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Notification;
/**
* Manage user permissions and restrictions based on roles
*
* @TODO(el): Rename to RolesController: https://dev.icinga.org/issues/10015
*/
class RoleController extends AuthBackendController
{
/**
@ -31,7 +40,7 @@ class RoleController extends AuthBackendController
$values = $role->getValues();
try {
$role->add($name, $values);
} catch (InvalidArgumentException $e) {
} catch (AlreadyExistsException $e) {
$role->addError($e->getMessage());
return false;
}
@ -54,19 +63,11 @@ class RoleController extends AuthBackendController
/**
* Update a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/
public function editAction()
{
$this->assertPermission('config/authentication/roles/edit');
$name = $this->_request->getParam('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
sprintf($this->translate('Required parameter \'%s\' missing'), 'role'),
400
);
}
$name = $this->params->getRequired('role');
$role = new RoleForm();
$role->setTitle(sprintf($this->translate('Update Role %s'), $name));
$role->setSubmitLabel($this->translate('Update Role'));
@ -74,11 +75,8 @@ class RoleController extends AuthBackendController
$role
->setIniConfig(Config::app('roles', true))
->load($name);
} catch (InvalidArgumentException $e) {
throw new Zend_Controller_Action_Exception(
$e->getMessage(),
400
);
} catch (NotFoundError $e) {
$this->httpNotFound($e->getMessage());
}
$role
->setOnSuccess(function (RoleForm $role) use ($name) {
@ -87,7 +85,7 @@ class RoleController extends AuthBackendController
$values = $role->getValues();
try {
$role->update($name, $values, $oldName);
} catch (InvalidArgumentException $e) {
} catch (NotFoundError $e) {
$role->addError($e->getMessage());
return false;
}
@ -105,35 +103,24 @@ class RoleController extends AuthBackendController
/**
* Remove a role
*
* @throws Zend_Controller_Action_Exception If the required parameter 'role' is missing or the role does not exist
*/
public function removeAction()
{
$this->assertPermission('config/authentication/roles/remove');
$name = $this->_request->getParam('role');
if (empty($name)) {
throw new Zend_Controller_Action_Exception(
sprintf($this->translate('Required parameter \'%s\' missing'), 'role'),
400
);
}
$name = $this->params->getRequired('role');
$role = new RoleForm();
try {
$role
->setIniConfig(Config::app('roles', true))
->load($name);
} catch (InvalidArgumentException $e) {
throw new Zend_Controller_Action_Exception(
$e->getMessage(),
400
);
} catch (NotFoundError $e) {
$this->httpNotFound($e->getMessage());
}
$confirmation = new ConfirmRemovalForm(array(
'onSuccess' => function (ConfirmRemovalForm $confirmation) use ($name, $role) {
try {
$role->remove($name);
} catch (InvalidArgumentException $e) {
} catch (NotFoundError $e) {
Notification::error($e->getMessage());
return false;
}
@ -162,15 +149,15 @@ class RoleController extends AuthBackendController
$tabs->add(
'role/list',
array(
'title' => $this->translate(
'baseTarget' => '_main',
'label' => $this->translate('Roles'),
'title' => $this->translate(
'Configure roles to permit or restrict users and groups accessing Icinga Web 2'
),
'label' => $this->translate('Roles'),
'url' => 'role/list',
'baseTarget' => '_main'
'url' => 'role/list'
)
);
return $tabs;
}
}

View File

@ -1,6 +1,8 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Widget;
use Icinga\Web\Widget\SearchDashboard;

View File

@ -1,16 +1,18 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
use Icinga\Web\Controller\ActionController;
namespace Icinga\Controllers;
use Icinga\Web\Controller;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Web\FileCache;
use Zend_Controller_Action_Exception as ActionException;
use Icinga\Web\LessCompiler;
/**
* Delivery static content to clients
*/
class StaticController extends ActionController
class StaticController extends Controller
{
/**
* Static routes don't require authentication
@ -57,17 +59,15 @@ class StaticController extends ActionController
*/
public function imgAction()
{
// TODO(el): I think this action only retrieves images from modules
$module = $this->_getParam('module_name');
$file = $this->_getParam('file');
$basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir();
$filePath = realpath($basedir . '/public/img/' . $file);
if (! $filePath || strpos($filePath, $basedir) !== 0) {
throw new ActionException(sprintf(
'%s does not exist',
$filePath
), 404);
if ($filePath === false) {
$this->httpNotFound('%s does not exist', $filePath);
}
if (preg_match('/\.([a-z]+)$/i', $file, $m)) {
$extension = $m[1];
@ -80,10 +80,7 @@ class StaticController extends ActionController
header(sprintf('ETag: "%x-%x-%x"', $s['ino'], $s['size'], (float) str_pad($s['mtime'], 16, '0')));
header('Cache-Control: public, max-age=3600');
header('Pragma: cache');
header('Last-Modified: ' . gmdate(
'D, d M Y H:i:s',
$s['mtime']
) . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $s['mtime']) . ' GMT');
readfile($filePath);
}
@ -100,7 +97,7 @@ class StaticController extends ActionController
$basedir = Icinga::app()->getApplicationDir('../public/js/icinga/components/');
$filePath = $basedir . $file;
} else {
if (!Icinga::app()->getModuleManager()->hasEnabled($module)) {
if (! Icinga::app()->getModuleManager()->hasEnabled($module)) {
Logger::error(
'Non-existing frontend component "' . $module . '/' . $file
. '" was requested. The module "' . $module . '" does not exist or is not active.'
@ -112,7 +109,7 @@ class StaticController extends ActionController
$filePath = $basedir . '/public/js/' . $file;
}
if (!file_exists($filePath)) {
if (! file_exists($filePath)) {
Logger::error(
'Non-existing frontend component "' . $module . '/' . $file
. '" was requested, which would resolve to the the path: ' . $filePath
@ -122,41 +119,41 @@ class StaticController extends ActionController
}
$response = $this->getResponse();
$response->setHeader('Content-Type', 'text/javascript');
$this->setCacheHeader(3600);
$this->setCacheHeader();
$response->setHeader(
'Last-Modified',
gmdate(
'D, d M Y H:i:s',
filemtime($filePath)
) . ' GMT'
gmdate('D, d M Y H:i:s', filemtime($filePath)) . ' GMT'
);
readfile($filePath);
}
/**
* Set cache header for this response
* Set cache header for the response
*
* @param integer $maxAge The maximum age to set
* @param int $maxAge The maximum age to set
*/
private function setCacheHeader($maxAge)
private function setCacheHeader($maxAge = 3600)
{
$this->_response->setHeader('Cache-Control', 'max-age=3600', true);
$this->_response->setHeader('Pragma', 'cache', true);
$this->_response->setHeader(
'Expires',
gmdate(
'D, d M Y H:i:s',
time()+3600
) . ' GMT',
true
);
$maxAge = (int) $maxAge;
$this
->getResponse()
->setHeader('Cache-Control', sprintf('max-age=%d', $maxAge), true)
->setHeader('Pragma', 'cache', true)
->setHeader(
'Expires',
gmdate('D, d M Y H:i:s', time() + $maxAge) . ' GMT',
true
);
}
/**
* Send application's and modules' CSS
*/
public function stylesheetAction()
{
$lessCompiler = new \Icinga\Web\LessCompiler();
$lessCompiler = new LessCompiler();
$moduleManager = Icinga::app()->getModuleManager();
$publicDir = realpath(dirname($_SERVER['SCRIPT_FILENAME']));
@ -172,7 +169,7 @@ class StaticController extends ActionController
}
}
$this->_response->setHeader('Content-Type', 'text/css');
$this->getResponse()->setHeader('Content-Type', 'text/css');
$this->setCacheHeader(3600);
$lessCompiler->printStack();

View File

@ -1,12 +1,15 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Logger;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\Config\User\CreateMembershipForm;
use Icinga\Forms\Config\User\UserForm;
use Icinga\Data\DataArray\ArrayDatasource;
use Icinga\User;
use Icinga\Web\Controller\AuthBackendController;
use Icinga\Web\Form;
@ -54,19 +57,12 @@ class UserController extends AuthBackendController
}
$query = $backend->select(array('user_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->users = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupFilterControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
@ -100,15 +96,11 @@ class UserController extends AuthBackendController
$memberships = $this->loadMemberships(new User($userName))->select();
$filterEditor = Widget::create('filterEditor')
->setQuery($memberships)
->setSearchColumns(array('group_name'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'user')
->ignoreParams('page')
->handleRequest($this->getRequest());
$memberships->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupFilterControl(
$memberships,
array('group_name' => t('User Group')),
array('group_name')
);
$this->setupPaginationControl($memberships);
$this->setupLimitControl();
$this->setupSortControl(
@ -148,7 +140,7 @@ class UserController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -1,13 +1,15 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\Application\Config;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Forms\Config\UserGroup\UserGroupBackendForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Web\Url;
/**
* Controller to configure user group backends
@ -35,7 +37,7 @@ class UsergroupbackendController extends Controller
*/
public function listAction()
{
$this->view->backendNames = Config::app('groups')->keys();
$this->view->backendNames = Config::app('groups');
$this->createListTabs()->activate('usergroupbackend');
}

View File

@ -540,6 +540,12 @@
"code": 59414,
"src": "fontawesome"
},
{
"uid": "130380e481a7defc690dfb24123a1f0c",
"css": "circle",
"code": 59516,
"src": "fontawesome"
},
{
"uid": "266d5d9adf15a61800477a5acf9a4462",
"css": "chart-bar",
@ -672,6 +678,12 @@
"code": 59492,
"src": "entypo"
},
{
"uid": "p57wgnf4glngbchbucdi029iptu8oxb8",
"css": "pin",
"code": 59513,
"src": "typicons"
},
{
"uid": "c16a63e911bc47b46dc2a7129d2f0c46",
"css": "down-small",

View File

@ -119,4 +119,6 @@
.icon-down-small:before { content: '\e875'; } /* '' */
.icon-left-small:before { content: '\e876'; } /* '' */
.icon-right-small:before { content: '\e877'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */
.icon-pin:before { content: '\e879'; } /* '' */
.icon-circle:before { content: '\e87c'; } /* '' */

File diff suppressed because one or more lines are too long

View File

@ -119,4 +119,6 @@
.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe875;&nbsp;'); }
.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe876;&nbsp;'); }
.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe877;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }
.icon-pin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe879;&nbsp;'); }
.icon-circle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe87c;&nbsp;'); }

View File

@ -130,4 +130,6 @@
.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe875;&nbsp;'); }
.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe876;&nbsp;'); }
.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe877;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }
.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe878;&nbsp;'); }
.icon-pin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe879;&nbsp;'); }
.icon-circle { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe87c;&nbsp;'); }

View File

@ -1,10 +1,10 @@
@font-face {
font-family: 'ifont';
src: url('../font/ifont.eot?54745533');
src: url('../font/ifont.eot?54745533#iefix') format('embedded-opentype'),
url('../font/ifont.woff?54745533') format('woff'),
url('../font/ifont.ttf?54745533') format('truetype'),
url('../font/ifont.svg?54745533#ifont') format('svg');
src: url('../font/ifont.eot?82929165');
src: url('../font/ifont.eot?82929165#iefix') format('embedded-opentype'),
url('../font/ifont.woff?82929165') format('woff'),
url('../font/ifont.ttf?82929165') format('truetype'),
url('../font/ifont.svg?82929165#ifont') format('svg');
font-weight: normal;
font-style: normal;
}
@ -14,7 +14,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'ifont';
src: url('../font/ifont.svg?54745533#ifont') format('svg');
src: url('../font/ifont.svg?82929165#ifont') format('svg');
}
}
*/
@ -174,4 +174,6 @@
.icon-down-small:before { content: '\e875'; } /* '' */
.icon-left-small:before { content: '\e876'; } /* '' */
.icon-right-small:before { content: '\e877'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */
.icon-up-small:before { content: '\e878'; } /* '' */
.icon-pin:before { content: '\e879'; } /* '' */
.icon-circle:before { content: '\e87c'; } /* '' */

View File

@ -229,11 +229,11 @@ body {
}
@font-face {
font-family: 'ifont';
src: url('./font/ifont.eot?11424534');
src: url('./font/ifont.eot?11424534#iefix') format('embedded-opentype'),
url('./font/ifont.woff?11424534') format('woff'),
url('./font/ifont.ttf?11424534') format('truetype'),
url('./font/ifont.svg?11424534#ifont') format('svg');
src: url('./font/ifont.eot?83291894');
src: url('./font/ifont.eot?83291894#iefix') format('embedded-opentype'),
url('./font/ifont.woff?83291894') format('woff'),
url('./font/ifont.ttf?83291894') format('truetype'),
url('./font/ifont.svg?83291894#ifont') format('svg');
font-weight: normal;
font-style: normal;
}
@ -482,6 +482,8 @@ body {
</div>
<div class="row">
<div title="Code: 0xe878" class="the-icons span3"><i class="demo-icon icon-up-small">&#xe878;</i> <span class="i-name">icon-up-small</span><span class="i-code">0xe878</span></div>
<div title="Code: 0xe879" class="the-icons span3"><i class="demo-icon icon-pin">&#xe879;</i> <span class="i-name">icon-pin</span><span class="i-code">0xe879</span></div>
<div title="Code: 0xe87c" class="the-icons span3"><i class="demo-icon icon-circle">&#xe87c;</i> <span class="i-name">icon-circle</span><span class="i-code">0xe87c</span></div>
</div>
</div>
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>

View File

@ -27,6 +27,7 @@ class LoginForm extends Form
$this->setRequiredCue(null);
$this->setName('form_login');
$this->setSubmitLabel($this->translate('Login'));
$this->setProgressLabel($this->translate('Logging in'));
}
/**

View File

@ -25,6 +25,20 @@ class ApplicationConfigForm extends Form
*/
public function createElements(array $formData)
{
$this->addElement(
'checkbox',
'global_show_stacktraces',
array(
'required' => true,
'value' => true,
'label' => $this->translate('Show Stacktraces'),
'description' => $this->translate(
'Set whether to show an exception\'s stacktrace by default. This can also'
. ' be set in a user\'s preferences with the appropriate permission.'
)
)
);
$this->addElement(
'text',
'global_module_path',

View File

@ -33,7 +33,23 @@ class DbResourceForm extends Form
if (Platform::hasPostgresqlSupport()) {
$dbChoices['pgsql'] = 'PostgreSQL';
}
if (Platform::hasMssqlSupport()) {
$dbChoices['mssql'] = 'MSSQL';
}
if (Platform::hasOracleSupport()) {
$dbChoices['oracle'] = 'Oracle';
}
if (Platform::hasOciSupport()) {
$dbChoices['oci'] = 'Oracle (OCI8)';
}
$offerPostgres = false;
if (isset($formData['db'])) {
if ($formData['db'] === 'pgsql') {
$offerPostgres = true;
}
} elseif (key($dbChoices) === 'pgsql') {
$offerPostgres = true;
}
$this->addElement(
'text',
'name',
@ -68,11 +84,11 @@ class DbResourceForm extends Form
'number',
'port',
array(
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => ! array_key_exists('db', $formData) || $formData['db'] === 'mysql' ? 3306 : 5432
'label' => $this->translate('Port'),
'preserveDefault' => true,
'required' => $offerPostgres,
'value' => $offerPostgres ? 5432 : null
)
);
$this->addElement(
@ -103,6 +119,17 @@ class DbResourceForm extends Form
'description' => $this->translate('The password to use for authentication')
)
);
$this->addElement(
'checkbox',
'persistent',
array(
'description' => $this->translate(
'Check this box for persistent database connections. Persistent connections are not closed at the'
. ' end of a request, but are cached and re-used. This is experimental'
),
'label' => $this->translate('Persistent')
)
);
return $this;
}

View File

@ -81,22 +81,6 @@ class LdapResourceForm extends Form
)
);
if (isset($formData['encryption']) && $formData['encryption'] !== 'none') {
// TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors
$this->addElement(
'checkbox',
'reqcert',
array(
'required' => true,
'label' => $this->translate('Require Certificate'),
'description' => $this->translate(
'When checked, the LDAP server must provide a valid and known (trusted) certificate.'
),
'value' => 1
)
);
}
$this->addElement(
'text',
'root_dn',

View File

@ -236,10 +236,10 @@ class ResourceConfigForm extends ConfigForm
'livestatus' => 'Livestatus',
'ssh' => $this->translate('SSH Identity'),
);
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
if ($resourceType === 'ldap' || Platform::hasLdapSupport()) {
$resourceTypes['ldap'] = 'LDAP';
}
if ($resourceType === 'db' || Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
if ($resourceType === 'db' || Platform::hasDatabaseSupport()) {
$resourceTypes['db'] = $this->translate('SQL Database');
}
@ -344,13 +344,27 @@ class ResourceConfigForm extends ConfigForm
'submit',
'resource_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'data-progress-label' => $this->translate('Validation In Progress'),
'decorators' => array('ViewHelper')
)
);
$this->setAttrib('data-progress-element', 'resource-progress');
$this->addElement(
'note',
'resource-progress',
array(
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => 'resource-progress'))
)
)
);
$this->addDisplayGroup(
array('btn_submit', 'resource_validation'),
array('btn_submit', 'resource_validation', 'resource-progress'),
'submit_validation',
array(
'decorators' => array(

View File

@ -34,7 +34,7 @@ class UserForm extends RepositoryForm
)
);
$this->addElement(
'text',
'password',
'password',
array(
'required' => true,
@ -56,10 +56,11 @@ class UserForm extends RepositoryForm
$this->createInsertElements($formData);
$this->addElement(
'text',
'password',
'password',
array(
'label' => $this->translate('Password')
'description' => $this->translate('Leave empty for not updating the user\'s password'),
'label' => $this->translate('Password'),
)
);

View File

@ -53,20 +53,6 @@ class DbBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -32,20 +32,6 @@ class ExternalBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The backend name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -57,20 +57,6 @@ class LdapBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others.'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -464,9 +464,10 @@ class UserBackendConfigForm extends ConfigForm
'submit',
'backend_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'data-progress-label' => $this->translate('Validation In Progress'),
'decorators' => array('ViewHelper')
)
);
$this->addDisplayGroup(

View File

@ -3,6 +3,7 @@
namespace Icinga\Forms\Config;
use Icinga\Application\Config;
use Icinga\Forms\ConfigForm;
use Icinga\Exception\NotFoundError;
use Icinga\Web\Notification;
@ -28,6 +29,16 @@ class UserBackendReorderForm extends ConfigForm
return $this->config->keys();
}
/**
* Return the ordered backend configuration
*
* @return Config
*/
public function getConfig()
{
return $this->config;
}
/**
* Create and add elements to this form
*

View File

@ -34,20 +34,6 @@ class DbUserGroupBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -39,20 +39,6 @@ class LdapUserGroupBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
@ -177,13 +163,25 @@ class LdapUserGroupBackendForm extends Form
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP Group Name Attribute'),
'label' => $this->translate('LDAP Group Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing a group\'s name on the LDAP server.'
),
'value' => $defaults->group_name_attribute
)
);
$this->addElement(
'text',
'group_member_attribute',
array(
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP Group Member Attribute'),
'description' => $this->translate('The attribute name used for storing a group\'s members.'),
'value' => $defaults->group_member_attribute
)
);
$this->addElement(
'text',
'base_dn',
@ -258,7 +256,7 @@ class LdapUserGroupBackendForm extends Form
'preserveDefault' => true,
'ignore' => $disabled,
'disabled' => $disabled,
'label' => $this->translate('LDAP User Name Attribute'),
'label' => $this->translate('LDAP User Name Attribute'),
'description' => $this->translate(
'The attribute name used for storing a user\'s name on the LDAP server.'
),

View File

@ -43,7 +43,7 @@ class ConfigForm extends Form
public function save()
{
try {
$this->config->saveIni();
$this->writeConfig($this->config);
} catch (Exception $e) {
$this->addDecorator('ViewScript', array(
'viewModule' => 'default',
@ -58,4 +58,14 @@ class ConfigForm extends Form
return true;
}
/**
* Write the configuration to disk
*
* @param Config $config
*/
protected function writeConfig(Config $config)
{
$config->saveIni();
}
}

View File

@ -0,0 +1,35 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Navigation;
class DashletForm extends NavigationItemForm
{
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'pane',
array(
'required' => true,
'label' => $this->translate('Pane'),
'description' => $this->translate('The name of the dashboard pane in which to display this dashlet')
)
);
$this->addElement(
'text',
'url',
array(
'required' => true,
'label' => $this->translate('Url'),
'description' => $this->translate(
'The url to load in the dashlet. For external urls, make sure to prepend'
. ' an appropriate protocol identifier (e.g. http://example.tld)'
)
)
);
}
}

View File

@ -0,0 +1,31 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Navigation;
class MenuItemForm extends NavigationItemForm
{
/**
* {@inheritdoc}
*/
protected $requiresParentSelection = true;
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
parent::createElements($formData);
// Remove _self and _next as for menu entries only _main is valid
$this->getElement('target')->removeMultiOption('_self');
$this->getElement('target')->removeMultiOption('_next');
$parentElement = $this->getParent()->getElement('parent');
if ($parentElement !== null) {
$parentElement->setDescription($this->translate(
'The parent menu to assign this menu entry to. Select "None" to make this a main menu entry'
));
}
}
}

View File

@ -0,0 +1,824 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Navigation;
use InvalidArgumentException;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Application\Icinga;
use Icinga\Authentication\Auth;
use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Exception\ProgrammingError;
use Icinga\Forms\ConfigForm;
use Icinga\User;
use Icinga\Util\String;
use Icinga\Web\Form;
/**
* Form for managing navigation items
*/
class NavigationConfigForm extends ConfigForm
{
/**
* The class namespace where to locate navigation type forms
*
* @var string
*/
const FORM_NS = 'Forms\\Navigation';
/**
* The secondary configuration to write
*
* This is always the reduced configuration and is only written to
* disk once the main configuration has been successfully written.
*
* @var Config
*/
protected $secondaryConfig;
/**
* The navigation item to load when displaying the form for the first time
*
* @var string
*/
protected $itemToLoad;
/**
* The user for whom to manage navigation items
*
* @var User
*/
protected $user;
/**
* The user's navigation configuration
*
* @var Config
*/
protected $userConfig;
/**
* The shared navigation configuration
*
* @var Config
*/
protected $shareConfig;
/**
* The available navigation item types
*
* @var array
*/
protected $itemTypes;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_navigation');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Set the user for whom to manage navigation items
*
* @param User $user
*
* @return $this
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Return the user for whom to manage navigation items
*
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* Set the user's navigation configuration
*
* @param Config $config
*
* @return $this
*/
public function setUserConfig(Config $config)
{
$config->getConfigObject()->setKeyColumn('name');
$this->userConfig = $config;
return $this;
}
/**
* Return the user's navigation configuration
*
* @return Config
*/
public function getUserConfig()
{
if ($this->userConfig === null) {
$this->setUserConfig($this->getUser()->loadNavigationConfig());
}
return $this->userConfig;
}
/**
* Set the shared navigation configuration
*
* @param Config $config
*
* @return $this
*/
public function setShareConfig(Config $config)
{
$config->getConfigObject()->setKeyColumn('name');
$this->shareConfig = $config;
return $this;
}
/**
* Return the shared navigation configuration
*
* @return Config
*/
public function getShareConfig()
{
return $this->shareConfig;
}
/**
* Set the available navigation item types
*
* @param array $itemTypes
*
* @return $this
*/
public function setItemTypes(array $itemTypes)
{
$this->itemTypes = $itemTypes;
return $this;
}
/**
* Return the available navigation item types
*
* @return array
*/
public function getItemTypes()
{
return $this->itemTypes ?: array();
}
/**
* Return a list of available parent items for the given type of navigation item
*
* @param string $type
* @param string $owner
*
* @return array
*/
public function listAvailableParents($type, $owner = null)
{
$children = $this->itemToLoad ? $this->getFlattenedChildren($this->itemToLoad) : array();
$names = array();
foreach ($this->getShareConfig() as $sectionName => $sectionConfig) {
if (
$sectionName !== $this->itemToLoad
&& $sectionConfig->type === $type
&& $sectionConfig->owner === ($owner ?: $this->getUser()->getUsername())
&& !in_array($sectionName, $children, true)
) {
$names[] = $sectionName;
}
}
foreach ($this->getUserConfig() as $sectionName => $sectionConfig) {
if (
$sectionName !== $this->itemToLoad
&& $sectionConfig->type === $type
&& !in_array($sectionName, $children, true)
) {
$names[] = $sectionName;
}
}
return $names;
}
/**
* Recursively return all children of the given navigation item
*
* @param string $name
*
* @return array
*/
protected function getFlattenedChildren($name)
{
$config = $this->getConfigForItem($name);
if ($config === null) {
return array();
}
$children = array();
foreach ($config->toArray() as $sectionName => $sectionConfig) {
if (isset($sectionConfig['parent']) && $sectionConfig['parent'] === $name) {
$children[] = $sectionName;
$children = array_merge($children, $this->getFlattenedChildren($sectionName));
}
}
return $children;
}
/**
* Populate the form with the given navigation item's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no navigation item with the given name is found
*/
public function load($name)
{
if ($this->getConfigForItem($name) === null) {
throw new NotFoundError('No navigation item called "%s" found', $name);
}
$this->itemToLoad = $name;
return $this;
}
/**
* Add a new navigation item
*
* The navigation item to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a navigation item name
* @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');
}
$shared = false;
$config = $this->getUserConfig();
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();
$shared = true;
} else {
unset($data['users']);
unset($data['groups']);
}
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'])) {
$data['owner'] = $this->getUser()->getUsername();
$config = $this->getShareConfig();
$shared = true;
}
$itemName = $data['name'];
$exists = $config->hasSection($itemName);
if (! $exists) {
if ($shared) {
$exists = $this->getUserConfig()->hasSection($itemName);
} else {
$exists = (bool) $this->getShareConfig()
->select()
->where('name', $itemName)
->where('owner', $this->getUser()->getUsername())
->count();
}
}
if ($exists) {
throw new IcingaException(
$this->translate('A navigation item with the name "%s" does already exist'),
$itemName
);
}
unset($data['name']);
$config->setSection($itemName, $data);
$this->setIniConfig($config);
return $this;
}
/**
* Edit a navigation item
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no navigation item with the given name is found
* @throws IcingaException In case a navigation item with the same name already exists
*/
public function edit($name, array $data)
{
$config = $this->getConfigForItem($name);
if ($config === null) {
throw new NotFoundError('No navigation item called "%s" found', $name);
} else {
$itemConfig = $config->getSection($name);
}
$shared = false;
if ($this->hasBeenShared($name)) {
if (isset($data['parent']) && $data['parent']
? !$this->hasBeenShared($data['parent'])
: ((! isset($data['users']) || !$data['users']) && (! isset($data['groups']) || !$data['groups']))
) {
// It is shared but shouldn't anymore
$config = $this->unshare($name, isset($data['parent']) ? $data['parent'] : null);
}
} elseif ((isset($data['users']) && $data['users']) || (isset($data['groups']) && $data['groups'])) {
if ($this->getUser()->can('application/share/navigation')) {
// It is not shared yet but should be
$this->secondaryConfig = $config;
$config = $this->getShareConfig();
$data['owner'] = $this->getUser()->getUsername();
$shared = true;
} else {
unset($data['users']);
unset($data['groups']);
}
} elseif (isset($data['parent']) && $data['parent'] && $this->hasBeenShared($data['parent'])) {
// Its parent is shared so should it itself
$this->secondaryConfig = $config;
$config = $this->getShareConfig();
$data['owner'] = $this->getUser()->getUsername();
$shared = true;
}
$oldName = null;
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$oldName = $name;
$name = $data['name'];
$exists = $config->hasSection($name);
if (! $exists) {
$ownerName = $itemConfig->owner ?: $this->getUser()->getUsername();
if ($shared || $this->hasBeenShared($oldName)) {
if ($ownerName === $this->getUser()->getUsername()) {
$exists = $this->getUserConfig()->hasSection($name);
} else {
$owner = new User($ownerName);
$exists = $owner->loadNavigationConfig()->hasSection($name);
}
} else {
$exists = (bool) $this->getShareConfig()
->select()
->where('name', $name)
->where('owner', $ownerName)
->count();
}
}
if ($exists) {
throw new IcingaException(
$this->translate('A navigation item with the name "%s" does already exist'),
$name
);
}
}
unset($data['name']);
}
$itemConfig->merge($data);
foreach ($itemConfig->toArray() as $k => $v) {
if ($v === null) {
unset($itemConfig->$k);
}
}
if ($shared) {
// Share all descendant children
foreach ($this->getFlattenedChildren($oldName ?: $name) as $child) {
$childConfig = $this->secondaryConfig->getSection($child);
$this->secondaryConfig->removeSection($child);
$childConfig->owner = $this->getUser()->getUsername();
$config->setSection($child, $childConfig);
}
}
if ($oldName) {
// Update the parent name on all direct children
foreach ($config as $sectionConfig) {
if ($sectionConfig->parent === $oldName) {
$sectionConfig->parent = $name;
}
}
$config->removeSection($oldName);
}
if ($this->secondaryConfig !== null) {
$this->secondaryConfig->removeSection($oldName ?: $name);
}
$config->setSection($name, $itemConfig);
$this->setIniConfig($config);
return $this;
}
/**
* Remove a navigation item
*
* @param string $name
*
* @return ConfigObject The navigation item's config
*
* @throws NotFoundError In case no navigation item with the given name is found
* @throws IcingaException In case the navigation item has still children
*/
public function delete($name)
{
$config = $this->getConfigForItem($name);
if ($config === null) {
throw new NotFoundError('No navigation item called "%s" found', $name);
}
$children = $this->getFlattenedChildren($name);
if (! empty($children)) {
throw new IcingaException(
$this->translate(
'Unable to delete navigation item "%s". There'
. ' are other items dependent from it: %s'
),
$name,
join(', ', $children)
);
}
$section = $config->getSection($name);
$config->removeSection($name);
$this->setIniConfig($config);
return $section;
}
/**
* Unshare the given navigation item
*
* @param string $name
* @param string $parent
*
* @return Config The new config of the given navigation item
*
* @throws NotFoundError In case no navigation item with the given name is found
* @throws IcingaException In case the navigation item has a parent assigned to it
*/
public function unshare($name, $parent = null)
{
$config = $this->getShareConfig();
if (! $config->hasSection($name)) {
throw new NotFoundError('No navigation item called "%s" found', $name);
}
$itemConfig = $config->getSection($name);
if ($parent === null) {
$parent = $itemConfig->parent;
}
if ($parent && $this->hasBeenShared($parent)) {
throw new IcingaException(
$this->translate(
'Unable to unshare navigation item "%s". It is dependent from item "%s".'
. ' Dependent items can only be unshared by unsharing their parent'
),
$name,
$parent
);
}
$children = $this->getFlattenedChildren($name);
$config->removeSection($name);
$this->secondaryConfig = $config;
if (! $itemConfig->owner || $itemConfig->owner === $this->getUser()->getUsername()) {
$config = $this->getUserConfig();
} else {
$owner = new User($itemConfig->owner);
$config = $owner->loadNavigationConfig();
}
foreach ($children as $child) {
$childConfig = $this->secondaryConfig->getSection($child);
unset($childConfig->owner);
$this->secondaryConfig->removeSection($child);
$config->setSection($child, $childConfig);
}
unset($itemConfig->owner);
unset($itemConfig->users);
unset($itemConfig->groups);
$config->setSection($name, $itemConfig);
$this->setIniConfig($config);
return $config;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
$shared = false;
$itemTypes = $this->getItemTypes();
$itemType = isset($formData['type']) ? $formData['type'] : key($itemTypes);
$itemForm = $this->getItemForm($itemType);
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Name'),
'description' => $this->translate(
'The name of this navigation item that is used to differentiate it from others'
)
)
);
if (
(! $itemForm->requiresParentSelection() || !isset($formData['parent']) || !$formData['parent'])
&& $this->getUser()->can('application/share/navigation')
) {
$checked = isset($formData['shared']) ? null : (isset($formData['users']) || isset($formData['groups']));
$this->addElement(
'checkbox',
'shared',
array(
'autosubmit' => true,
'ignore' => true,
'value' => $checked,
'label' => $this->translate('Shared'),
'description' => $this->translate('Tick this box to share this item with others')
)
);
if ($checked || (isset($formData['shared']) && $formData['shared'])) {
$shared = true;
$this->addElement(
'text',
'users',
array(
'label' => $this->translate('Users'),
'description' => $this->translate(
'Comma separated list of usernames to share this item with'
)
)
);
$this->addElement(
'text',
'groups',
array(
'label' => $this->translate('Groups'),
'description' => $this->translate(
'Comma separated list of group names to share this item with'
)
)
);
}
}
$this->addElement(
'select',
'type',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Type'),
'description' => $this->translate('The type of this navigation item'),
'multiOptions' => $itemTypes
)
);
if (! $shared && $itemForm->requiresParentSelection()) {
if ($this->itemToLoad && $this->hasBeenShared($this->itemToLoad)) {
$itemConfig = $this->getShareConfig()->getSection($this->itemToLoad);
$availableParents = $this->listAvailableParents($itemType, $itemConfig->owner);
} else {
$availableParents = $this->listAvailableParents($itemType);
}
$this->addElement(
'select',
'parent',
array(
'allowEmpty' => true,
'autosubmit' => true,
'label' => $this->translate('Parent'),
'description' => $this->translate(
'The parent item to assign this navigation item to. '
. 'Select "None" to make this a main navigation item'
),
'multiOptions' => array_merge(
array('' => $this->translate('None', 'No parent for a navigation item')),
empty($availableParents) ? array() : array_combine($availableParents, $availableParents)
)
)
);
}
$this->addSubForm($itemForm, 'item_form');
$itemForm->create($formData); // May require a parent which gets set by addSubForm()
}
/**
* Populate the configuration of the navigation item to load
*/
public function onRequest()
{
if ($this->itemToLoad) {
$data = $this->getConfigForItem($this->itemToLoad)->getSection($this->itemToLoad)->toArray();
$data['name'] = $this->itemToLoad;
$this->populate($data);
}
}
/**
* {@inheritdoc}
*/
public function isValid($formData)
{
if (! parent::isValid($formData)) {
return false;
}
$valid = true;
if (isset($formData['users']) && $formData['users']) {
$parsedUserRestrictions = array();
foreach (Auth::getInstance()->getRestrictions('application/share/users') as $userRestriction) {
$parsedUserRestrictions[] = array_map('trim', explode(',', $userRestriction));
}
if (! empty($parsedUserRestrictions)) {
$desiredUsers = array_map('trim', explode(',', $formData['users']));
array_unshift($parsedUserRestrictions, $desiredUsers);
$forbiddenUsers = call_user_func_array('array_diff', $parsedUserRestrictions);
if (! empty($forbiddenUsers)) {
$valid = false;
$this->getElement('users')->addError(
$this->translate(sprintf(
'You are not permitted to share this navigation item with the following users: %s',
implode(', ', $forbiddenUsers)
))
);
}
}
}
if (isset($formData['groups']) && $formData['groups']) {
$parsedGroupRestrictions = array();
foreach (Auth::getInstance()->getRestrictions('application/share/groups') as $groupRestriction) {
$parsedGroupRestrictions[] = array_map('trim', explode(',', $groupRestriction));
}
if (! empty($parsedGroupRestrictions)) {
$desiredGroups = array_map('trim', explode(',', $formData['groups']));
array_unshift($parsedGroupRestrictions, $desiredGroups);
$forbiddenGroups = call_user_func_array('array_diff', $parsedGroupRestrictions);
if (! empty($forbiddenGroups)) {
$valid = false;
$this->getElement('groups')->addError(
$this->translate(sprintf(
'You are not permitted to share this navigation item with the following groups: %s',
implode(', ', $forbiddenGroups)
))
);
}
}
}
return $valid;
}
/**
* {@inheritdoc}
*/
public function getValues($suppressArrayNotation = false)
{
$values = parent::getValues();
$values = array_merge($values, $values['item_form']);
unset($values['item_form']);
return $values;
}
/**
* {@inheritdoc}
*/
protected function writeConfig(Config $config)
{
parent::writeConfig($config);
if ($this->secondaryConfig !== null) {
$this->config = $this->secondaryConfig; // Causes the config being displayed to the user in case of an error
parent::writeConfig($this->secondaryConfig);
}
}
/**
* Return the navigation configuration the given item is a part of
*
* @param string $name
*
* @return Config|null In case the item is not part of any configuration
*/
protected function getConfigForItem($name)
{
if ($this->getUserConfig()->hasSection($name)) {
return $this->getUserConfig();
} elseif ($this->getShareConfig()->hasSection($name)) {
if (
$this->getShareConfig()->get($name, 'owner') === $this->getUser()->getUsername()
|| $this->getUser()->can('config/application/navigation')
) {
return $this->getShareConfig();
}
}
}
/**
* Return whether the given navigation item has been shared
*
* @param string $name
*
* @return bool
*/
protected function hasBeenShared($name)
{
return $this->getConfigForItem($name) === $this->getShareConfig();
}
/**
* Return the form for the given type of navigation item
*
* @param string $type
*
* @return Form
*/
protected function getItemForm($type)
{
$className = String::cname($type, '-') . 'Form';
$form = null;
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $module) {
$classPath = 'Icinga\\Module\\'
. ucfirst($module->getName())
. '\\'
. static::FORM_NS
. '\\'
. $className;
if (class_exists($classPath)) {
$form = new $classPath();
break;
}
}
if ($form === null) {
$classPath = 'Icinga\\' . static::FORM_NS . '\\' . $className;
if (class_exists($classPath)) {
$form = new $classPath();
}
}
if ($form === null) {
Logger::debug(
'Failed to find custom navigation item form %s for item %s. Using form NavigationItemForm now',
$className,
$type
);
$form = new NavigationItemForm();
} elseif (! $form instanceof NavigationItemForm) {
throw new ProgrammingError('Class %s must inherit from NavigationItemForm', $classPath);
}
return $form;
}
}

View File

@ -0,0 +1,74 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Navigation;
use Icinga\Web\Form;
class NavigationItemForm extends Form
{
/**
* Whether to create a select input to choose a parent for a navigation item of a particular type
*
* @var bool
*/
protected $requiresParentSelection = false;
/**
* Return whether to create a select input to choose a parent for a navigation item of a particular type
*
* @return bool
*/
public function requiresParentSelection()
{
return $this->requiresParentSelection;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
$this->addElement(
'select',
'target',
array(
'allowEmpty' => true,
'label' => $this->translate('Target'),
'description' => $this->translate('The target where to open this navigation item\'s url'),
'multiOptions' => array(
'_blank' => $this->translate('New Window'),
'_next' => $this->translate('New Column'),
'_main' => $this->translate('Single Column'),
'_self' => $this->translate('Current Column')
)
)
);
$this->addElement(
'text',
'url',
array(
'allowEmpty' => true,
'label' => $this->translate('Url'),
'description' => $this->translate(
'The url of this navigation item. Leave blank if you only want the'
. ' name being displayed. For external urls, make sure to prepend'
. ' an appropriate protocol identifier (e.g. http://example.tld)'
)
)
);
$this->addElement(
'text',
'icon',
array(
'allowEmpty' => true,
'label' => $this->translate('Icon'),
'description' => $this->translate(
'The icon of this navigation item. Leave blank if you do not want a icon being displayed'
)
)
);
}
}

View File

@ -5,6 +5,7 @@ namespace Icinga\Forms;
use Exception;
use DateTimeZone;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Authentication\Auth;
use Icinga\User\Preferences;
@ -178,6 +179,19 @@ class PreferenceForm extends Form
)
);
if (Auth::getInstance()->hasPermission('application/stacktraces')) {
$this->addElement(
'checkbox',
'show_stacktraces',
array(
'required' => true,
'value' => $this->getDefaultShowStacktraces(),
'label' => $this->translate('Show Stacktraces'),
'description' => $this->translate('Set whether to show an exception\'s stacktrace.')
)
);
}
$this->addElement(
'checkbox',
'show_benchmark',
@ -220,8 +234,20 @@ class PreferenceForm extends Form
)
);
$this->setAttrib('data-progress-element', 'preferences-progress');
$this->addElement(
'note',
'preferences-progress',
array(
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => 'preferences-progress'))
)
)
);
$this->addDisplayGroup(
array('btn_submit_preferences', 'btn_submit_session'),
array('btn_submit_preferences', 'btn_submit_session', 'preferences-progress'),
'submit_buttons',
array(
'decorators' => array(
@ -257,4 +283,14 @@ class PreferenceForm extends Form
$locale = Translator::getPreferredLocaleCode($_SERVER['HTTP_ACCEPT_LANGUAGE']);
return $locale;
}
/**
* Return the default global setting for show_stacktraces
*
* @return bool
*/
protected function getDefaultShowStacktraces()
{
return Config::app()->get('global', 'show_stacktraces', true);
}
}

View File

@ -3,10 +3,11 @@
namespace Icinga\Forms\Security;
use InvalidArgumentException;
use LogicException;
use Zend_Form_Element;
use Icinga\Application\Icinga;
use Icinga\Exception\AlreadyExistsException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Util\String;
@ -20,49 +21,72 @@ class RoleForm extends ConfigForm
*
* @var array
*/
protected $providedPermissions = array(
'*' => 'Allow everything (*)',
'config/*' => 'Allow config access (config/*)',
/*
// [tg] seems excessive for me, hidden for rc1, tbd
'config/application/*' => 'config/application/*',
'config/application/general' => 'config/application/general',
'config/application/resources' => 'config/application/resources',
'config/application/userbackend' => 'config/application/userbackend',
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
'config/authentication/*' => 'config/authentication/*',
'config/authentication/users/*' => 'config/authentication/users/*',
'config/authentication/users/show' => 'config/authentication/users/show',
'config/authentication/users/add' => 'config/authentication/users/add',
'config/authentication/users/edit' => 'config/authentication/users/edit',
'config/authentication/users/remove' => 'config/authentication/users/remove',
'config/authentication/groups/*' => 'config/authentication/groups/*',
'config/authentication/groups/show' => 'config/authentication/groups/show',
'config/authentication/groups/add' => 'config/authentication/groups/add',
'config/authentication/groups/edit' => 'config/authentication/groups/edit',
'config/authentication/groups/remove' => 'config/authentication/groups/remove',
'config/authentication/roles/*' => 'config/authentication/roles/*',
'config/authentication/roles/show' => 'config/authentication/roles/show',
'config/authentication/roles/add' => 'config/authentication/roles/add',
'config/authentication/roles/edit' => 'config/authentication/roles/edit',
'config/authentication/roles/remove' => 'config/authentication/roles/remove',
'config/modules' => 'config/modules'
*/
);
protected $providedPermissions;
/**
* Provided restrictions by currently loaded modules
*
* @var array
*/
protected $providedRestrictions = array();
protected $providedRestrictions;
/**
* {@inheritdoc}
*/
public function init()
{
$this->providedPermissions = array(
'*' => $this->translate('Allow everything') . ' (*)',
'application/share/navigation' => $this->translate('Allow to share navigation items')
. ' (application/share/navigation)',
'application/stacktraces' => $this->translate(
'Allow to adjust in the preferences whether to show stacktraces'
) . ' (application/stacktraces)',
'config/*' => $this->translate('Allow config access') . ' (config/*)',
/*
// [tg] seems excessive for me, hidden for rc1, tbd
'config/application/*' => 'config/application/*',
'config/application/general' => 'config/application/general',
'config/application/resources' => 'config/application/resources',
'config/application/userbackend' => 'config/application/userbackend',
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
'config/application/navigation' => 'config/application/navigation',
'config/authentication/*' => 'config/authentication/*',
'config/authentication/users/*' => 'config/authentication/users/*',
'config/authentication/users/show' => 'config/authentication/users/show',
'config/authentication/users/add' => 'config/authentication/users/add',
'config/authentication/users/edit' => 'config/authentication/users/edit',
'config/authentication/users/remove' => 'config/authentication/users/remove',
'config/authentication/groups/*' => 'config/authentication/groups/*',
'config/authentication/groups/show' => 'config/authentication/groups/show',
'config/authentication/groups/add' => 'config/authentication/groups/add',
'config/authentication/groups/edit' => 'config/authentication/groups/edit',
'config/authentication/groups/remove' => 'config/authentication/groups/remove',
'config/authentication/roles/*' => 'config/authentication/roles/*',
'config/authentication/roles/show' => 'config/authentication/roles/show',
'config/authentication/roles/add' => 'config/authentication/roles/add',
'config/authentication/roles/edit' => 'config/authentication/roles/edit',
'config/authentication/roles/remove' => 'config/authentication/roles/remove',
'config/modules' => 'config/modules'
*/
);
$helper = new Zend_Form_Element('bogus');
$this->providedRestrictions = array(
$helper->filterName('application/share/users') => array(
'name' => 'application/share/users',
'description' => $this->translate(
'Restrict which users this role can share items and information with'
)
),
$helper->filterName('application/share/groups') => array(
'name' => 'application/share/groups',
'description' => $this->translate(
'Restrict which groups this role can share items and information with'
)
)
);
$mm = Icinga::app()->getModuleManager();
foreach ($mm->listInstalledModules() as $moduleName) {
$modulePermission = $mm::MODULE_PERMISSION_NS . $moduleName;
@ -162,6 +186,7 @@ class RoleForm extends ConfigForm
* @return $this
*
* @throws LogicException If the config is not set
* @throws NotFoundError If the given role does not exist
* @see ConfigForm::setConfig() For setting the config.
*/
public function load($name)
@ -170,10 +195,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t load role \'%s\'. Config is not set', $name));
}
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
throw new NotFoundError(
$this->translate('Can\'t load role \'%s\'. Role does not exist'),
$name
));
);
}
$role = $this->config->getSection($name)->toArray();
$role['permissions'] = ! empty($role['permissions'])
@ -196,14 +221,14 @@ class RoleForm extends ConfigForm
/**
* Add a role
*
* @param string $name The name of the role
* @param string $name The name of the role
* @param array $values
*
* @return $this
*
* @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role to add already exists
* @see ConfigForm::setConfig() For setting the config.
* @throws LogicException If the config is not set
* @throws AlreadyExistsException If the role to add already exists
* @see ConfigForm::setConfig() For setting the config.
*/
public function add($name, array $values)
{
@ -211,10 +236,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t add role \'%s\'. Config is not set', $name));
}
if ($this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
throw new AlreadyExistsException(
$this->translate('Can\'t add role \'%s\'. Role already exists'),
$name
));
);
}
$this->config->setSection($name, $values);
return $this;
@ -223,13 +248,13 @@ class RoleForm extends ConfigForm
/**
* Remove a role
*
* @param string $name The name of the role
* @param string $name The name of the role
*
* @return $this
*
* @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role does not exist
* @see ConfigForm::setConfig() For setting the config.
* @throws LogicException If the config is not set
* @throws NotFoundError If the role does not exist
* @see ConfigForm::setConfig() For setting the config.
*/
public function remove($name)
{
@ -237,10 +262,10 @@ class RoleForm extends ConfigForm
throw new LogicException(sprintf('Can\'t remove role \'%s\'. Config is not set', $name));
}
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
throw new NotFoundError(
$this->translate('Can\'t remove role \'%s\'. Role does not exist'),
$name
));
);
}
$this->config->removeSection($name);
return $this;
@ -249,15 +274,15 @@ class RoleForm extends ConfigForm
/**
* Update a role
*
* @param string $name The possibly new name of the role
* @param string $name The possibly new name of the role
* @param array $values
* @param string $oldName The name of the role to update
* @param string $oldName The name of the role to update
*
* @return $this
*
* @throws LogicException If the config is not set
* @throws InvalidArgumentException If the role to update does not exist
* @see ConfigForm::setConfig() For setting the config.
* @throws LogicException If the config is not set
* @throws NotFoundError If the role to update does not exist
* @see ConfigForm::setConfig() For setting the config.
*/
public function update($name, array $values, $oldName)
{
@ -270,10 +295,10 @@ class RoleForm extends ConfigForm
$this->add($name, $values);
} else {
if (! $this->config->hasSection($name)) {
throw new InvalidArgumentException(sprintf(
throw new NotFoundError(
$this->translate('Can\'t update role \'%s\'. Role does not exist'),
$name
));
);
}
$this->config->setSection($name, $values);
}

View File

@ -4,59 +4,60 @@ use Icinga\Web\Url;
use Icinga\Web\Notification;
use Icinga\Authentication\Auth;
$moduleName = $this->layout()->moduleName;
if ($moduleName !== 'default') {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';
}
if (Auth::getInstance()->isAuthenticated()):
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';
}
$refresh = '';
if ($this->layout()->autorefreshInterval) {
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
}
$refresh = '';
if ($this->layout()->autorefreshInterval) {
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
}
?>
<div id="header">
<div id="logo" data-base-target="_main">
<?= $this->qlink(
'',
'/dashboard',
null,
array(
'icon' => '../logo_icinga-inv.png',
'aria-hidden' => 'true',
'tabindex' => -1
)
); ?>
</div>
</div>
<?php if (!$this->layout()->isIframe): ?>
<div id="sidebar">
<?php echo $this->render('parts/navigation.phtml') ?>
</div>
<div id="header">
<div id="logo">
<?php if (Auth::getInstance()->isAuthenticated()): ?>
<?= $this->qlink(
'',
'dashboard',
null,
array(
'icon' => '../logo_icinga-inv.png',
'data-base-target' => '_main',
'aria-hidden' => 'true',
'tabindex' => -1
)
); ?>
<?php else: ?>
<?= $this->icon('../logo_icinga-inv.png'); ?>
<?php endif ?>
</div>
</div>
<?php if (! $this->layout()->isIframe): ?>
<div id="sidebar">
<?= $this->render('parts/navigation.phtml'); ?>
</div>
<?php endif ?>
<div id="main" role="main">
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest()->without('renderLayout') ?>"<?= $refresh ?> style="display: block">
<?= $this->render('inline.phtml') ?>
<div id="main" role="main">
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest()->without('renderLayout'); ?>"<?= $refresh; ?> style="display: block">
<?= $this->render('inline.phtml') ?>
</div>
<div id="col2" class="container">
</div>
<div id="col3" class="container">
</div>
</div><!-- END of main -->
<div id="footer">
<ul id="notifications"><?php
$notifications = Notification::getInstance();
if ($notifications->hasMessages()) {
foreach ($notifications->popMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
</div>
<div id="footer">
<ul id="notifications"><?php
$notifications = Notification::getInstance();
if ($notifications->hasMessages()) {
foreach ($notifications->popMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
}
}
}
?></ul>
</div>
<?php else: ?>
<?= $this->render('inline.phtml') ?>
<?php endif ?>
?></ul>
</div>

View File

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

View File

@ -1,6 +1,5 @@
<?php
use Icinga\Web\JavaScript;
use Icinga\Util\Translator;
if (array_key_exists('_dev', $_GET)) {
@ -16,6 +15,7 @@ $lang = Translator::splitLocaleCode()->language;
$isIframe = $this->layout()->isIframe;
$showFullscreen = $this->layout()->showFullscreen;
$iframeClass = $isIframe ? ' iframe' : '';
$innerLayoutScript = $this->layout()->innerLayout . '.phtml';
?><!DOCTYPE html>
<!--[if IE 8]>
@ -52,7 +52,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
<body id="body">
<pre id="responsive-debug"></pre>
<div id="layout" class="default-layout<?php if ($showFullscreen): ?> fullscreen-layout<?php endif ?>">
<?= $this->render('body.phtml') ?>
<?= $this->render($innerLayoutScript); ?>
</div>
<iframe id="fileupload-frame-target" name="fileupload-frame-target"></iframe>
<!--[if IE 8]>
@ -63,7 +63,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
<!--<![endif]-->
<script type="text/javascript">
var icinga = new Icinga({
baseUrl: '<?= $this->href('/') ?>'
baseUrl: '<?= $this->baseUrl(); ?>'
});
</script>
</body>

View File

@ -1,8 +1,6 @@
<?php
use Icinga\Web\Url;
use Icinga\Web\Menu;
use Icinga\Web\MenuRenderer;
use Icinga\Application\Icinga;
// Don't render a menu for unauthenticated users unless menu is auth aware
if (! $this->auth()->isAuthenticated()) {
@ -27,10 +25,7 @@ if (! $this->auth()->isAuthenticated()) {
'layout/menu.phtml',
'default',
array(
'menuRenderer' => new MenuRenderer(
Menu::load(),
Url::fromRequest()->without('renderLayout')->getRelativeUrl()
)
'menuRenderer' => Icinga::app()->getMenu()->getRenderer()
)
) ?>
</div>

View File

@ -4,7 +4,7 @@ use Icinga\Web\StyleSheet;
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
if ($moduleName !== 'default') {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';

View File

@ -2,33 +2,36 @@
# Copyright (C) 2015 Icinga Development Team
# This file is distributed under the same license as Icinga Web 2.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
#
msgid ""
msgstr ""
"Project-Id-Version: Icinga Web 2 (None)\n"
"Project-Id-Version: Icinga Web 2\n"
"Report-Msgid-Bugs-To: dev@icinga.org\n"
"POT-Creation-Date: 2015-03-13 14:55+0100\n"
"PO-Revision-Date: 2015-03-13 14:51+0100\n"
"PO-Revision-Date: 2015-07-17 10:17+0200\n"
"Last-Translator: Davide Demuru <davide.demuru@gmail.com>\n"
"Language: it_IT\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language-Team: \n"
"X-Generator: Poedit 1.8.2\n"
"X-Poedit-KeywordsList: translate\n"
"X-Poedit-Basepath: ../..\n"
"X-Poedit-SearchPath-0: .\n"
#: /usr/share/icingaweb2/library/Icinga/Web/Form/Validator/InArray.php:16
#, php-format
msgid "\"%s\" is not in the list of allowed values."
msgstr ""
msgstr "\"%s\" non è tra i valori ammessi."
#: /usr/share/icingaweb2/library/Icinga/Web/Form/Validator/InArray.php:19
#, php-format
msgid ""
"\"%s\" is not in the list of allowed values. Did you mean one of the "
"following?: %s"
msgstr ""
msgstr "\"%s\" non è tra i valori ammessi. Intendevi uno dei seguenti?: %s"
#: /usr/share/icingaweb2/application/forms/Config/Authentication/ExternalBackendForm.php:56
#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:50
@ -38,23 +41,23 @@ msgstr "\"%value%\" non è un'espressione regolare valida"
#: /usr/share/icingaweb2/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php:40
#, php-format
msgid "%d unhandled hosts down"
msgstr ""
msgstr "%d host down non gestiti"
#: /usr/share/icingaweb2/library/Icinga/Web/Menu/MonitoringMenuItemRenderer.php:41
#, php-format
msgid "%d unhandled services critical"
msgstr ""
msgstr "%d servizi down non gestiti"
#: /usr/share/icingaweb2/library/Icinga/Web/View/DateTimeRenderer.php:185
msgctxt "timespan"
msgid "%im %ss"
msgstr ""
msgstr "%im %ss"
#: /usr/share/icingaweb2/library/Icinga/Web/Form/ErrorLabeller.php:47
#, fuzzy, php-format
#, php-format
msgctxt "config.path"
msgid "%s does not exist"
msgstr "%s non è scrivibile"
msgstr "%s non esiste"
#: /usr/share/icingaweb2/library/Icinga/Web/Form/ErrorLabeller.php:48
#, php-format
@ -81,10 +84,10 @@ msgstr "%s non è nel formato previsto: %%value%%"
#: /usr/share/icingaweb2/application/views/scripts/pivottablePagination.phtml:9
#, php-format
msgid "%s: %d to %d of %d (on the %s-axis)"
msgstr "%s: %d a %d di %d (nell asse %s)"
msgstr "%s: %d a %d di %d (dell'asse %s)"
#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:9
#, fuzzy, php-format
#, php-format
msgctxt "pagination.joystick"
msgid "%s: Show %s %u to %u out of %u"
msgstr "%s: Mostra %s %u a %u di %u"
@ -100,7 +103,7 @@ msgstr "...e password"
#: /usr/share/icingaweb2/application/layouts/scripts/parts/navigation.phtml:14
msgid "Accessibility Skip Links"
msgstr ""
msgstr "Salta Controllo di Accessibilità"
#: /usr/share/icingaweb2/application/controllers/DashboardController.php:69
msgid "Add Dashlet To Dashboard"
@ -112,9 +115,8 @@ msgid "Add To Dashboard"
msgstr "Aggiungi alla Dashboard"
#: /usr/share/icingaweb2/library/Icinga/Web/Widget/FilterEditor.php:327
#, fuzzy
msgid "Add another filter"
msgstr "Cliccare per aggiungere un altro filtro"
msgstr "Aggiungi un altro filtro"
#: /usr/share/icingaweb2/library/Icinga/Web/Widget/FilterWidget.php:80
msgid "Add filter..."
@ -127,7 +129,7 @@ msgstr "Cambia la configurazione generale di Icinga Web 2"
#: /usr/share/icingaweb2/application/controllers/PreferenceController.php:28
msgid "Adjust the preferences of Icinga Web 2 according to your needs"
msgstr ""
msgstr "Modifica le preferenze di Icinga Web 2 in base alle tue esigenze"
#: /usr/share/icingaweb2/application/controllers/AuthenticationController.php:111
msgid ""
@ -337,11 +339,11 @@ msgstr ""
#: /usr/share/icingaweb2/library/Icinga/Chart/GridChart.php:90
msgid "Contains data in a bar or line chart."
msgstr ""
msgstr "Contiene i dati in grafici a barre o linee"
#: /usr/share/icingaweb2/library/Icinga/Chart/PieChart.php:56
msgid "Contains data in a pie chart."
msgstr ""
msgstr "Contiene i dati in grafici a torta"
#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:37
msgid ""
@ -622,7 +624,7 @@ msgstr "Configurazione Generale"
#: /usr/share/icingaweb2/library/Icinga/Chart/GridChart.php:89
msgid "Grid Chart"
msgstr ""
msgstr "Grafico a Griglia"
#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:98
#: /usr/share/icingaweb2/application/views/scripts/roles/index.phtml:16
@ -700,8 +702,8 @@ msgstr "Tipo \"%s\" di risorsa non valido"
msgid ""
"It appears that you did not configure Icinga Web 2 yet so it's not possible "
"to log in without any defined authentication method. Please define a "
"authentication method by following the instructions in the %1$sdocumentation%"
"3$s or by using our %2$sweb-based setup-wizard%3$s."
"authentication method by following the instructions in the %1$sdocumentation"
"%3$s or by using our %2$sweb-based setup-wizard%3$s."
msgstr ""
"Icinga Web 2 non è stato configurato e quindi non è possibile autenticarsi "
"senza aver definito nessun metodo di autenticazione. Si prega di definire un "
@ -951,7 +953,7 @@ msgstr "Permessi"
#: /usr/share/icingaweb2/library/Icinga/Chart/PieChart.php:55
msgid "Pie Chart"
msgstr ""
msgstr "Grafico a Torta"
#: /usr/share/icingaweb2/application/views/scripts/dashboard/error.phtml:4
msgid "Please copy the following dashboard snippet to "
@ -1007,12 +1009,16 @@ msgid ""
"Push this button to update the form to reflect the change that was made in "
"the field on the left"
msgstr ""
"Premere questo bottone per aggiornare il form e mostrare i cambiamenti "
"effettuati nel campo a sinistra"
#: /usr/share/icingaweb2/library/Icinga/Web/Form/Decorator/Autosubmit.php:119
msgid ""
"Push this button to update the form to reflect the changes that were made "
"below"
msgstr ""
"Premere questo bottone per aggiornare il form e mostrare i cambiamenti "
"effettuati sotto"
#: /usr/share/icingaweb2/library/Icinga/Web/Widget/SearchDashboard.php:63
msgid "Ready to search"
@ -1533,6 +1539,8 @@ msgid ""
"Upon any of this form's fields were changed, this page is being updated "
"automatically."
msgstr ""
"Cambiando un qualsiasi valore del form la pagina verrà ricaricata "
"automaticamente."
#: /usr/share/icingaweb2/library/Icinga/Web/Form.php:693
#: /usr/share/icingaweb2/library/Icinga/Web/Form/Decorator/Autosubmit.php:100

View File

@ -4,7 +4,7 @@
<img aria-hidden="true" class="fade-in one" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
</div>
</div>
<div class="form" data-base-target="layout">
<div class="below-logo" data-base-target="layout">
<h1><?= $this->translate('Welcome to Icinga Web 2'); ?></h1>
<?php if ($requiresSetup): ?>
<p class="config-note"><?= sprintf(

View File

@ -16,9 +16,9 @@
<?php if ($module->enabled && $module->loaded) {
echo $this->icon('thumbs-up', sprintf($this->translate('Module %s is enabled'), $module->name));
} elseif (! $module->enabled) {
echo $this->icon('thumbs-down', sprintf($this->translate('Module %s is disabled'), $module->name));
echo $this->icon('block', sprintf($this->translate('Module %s is disabled'), $module->name));
} else { // ! $module->loaded
echo $this->icon('thumbs-down', sprintf($this->translate('Module %s has failed to load'), $module->name));
echo $this->icon('block', sprintf($this->translate('Module %s has failed to load'), $module->name));
}
echo $this->qlink(

View File

@ -2,7 +2,7 @@
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<a href="<?= $this->href('/config/createresource'); ?>">
<a href="<?= $this->href('config/createresource'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Resource'); ?>
</a>
<table class="action alternating" id="resource-edit-table">
@ -11,15 +11,38 @@
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
</thead>
<tbody>
<?php foreach ($this->resources as $name): ?>
<?php foreach ($this->resources as $name => $value): ?>
<tr>
<td>
<?php
switch ($value->type) {
case 'db':
$icon = 'database';
break;
case 'ldap':
$icon = 'sitemap';
break;
case 'livestatus':
$icon = 'flash';
break;
case 'ssh':
$icon = 'user';
break;
case 'file':
case 'ini':
$icon = 'doc-text';
break;
default:
$icon = 'edit';
break;
}
?>
<?= $this->qlink(
$name,
'config/editresource',
array('resource' => $name),
array(
'icon' => 'edit',
'icon' => $icon,
'title' => sprintf($this->translate('Edit resource %s'), $name)
)
); ?>

View File

@ -2,7 +2,7 @@
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<a href="<?= $this->href('/config/createuserbackend'); ?>">
<a href="<?= $this->href('config/createuserbackend'); ?>">
<?= $this->icon('plus'); ?><?= $this->translate('Create A New User Backend'); ?>
</a>
<div id="authentication-reorder-form">

View File

@ -11,10 +11,16 @@
<div class="content">
<h1><?= $this->escape($this->translate('Welcome to Icinga Web!')) ?></h1>
<p>
<?= sprintf(
<?php if (! $this->hasPermission('config/modules')) {
echo $this->escape($this->translate(
'Currently there is no dashlet available. Please contact the administrator.'
));
} else {
printf(
$this->escape($this->translate('Currently there is no dashlet available. This might change once you enabled some of the available %s.')),
$this->qlink($this->translate('modules'), 'config/modules')
) ?>
);
} ?>
</p>
</div>
<?php endif ?>

View File

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

View File

@ -6,8 +6,12 @@
<th style="width: 5em"><?= $this->translate('Order'); ?></th>
</thead>
<tbody>
<?php $backendNames = $form->getBackendOrder(); ?>
<?php for ($i = 0; $i < count($backendNames); $i++): ?>
<?php
$backendNames = $form->getBackendOrder();
$backendConfigs = $form->getConfig();
for ($i = 0; $i < count($backendNames); $i++):
$type = $backendConfigs->getSection($backendNames[$i])->get('backend');
?>
<tr>
<td class="action">
<?= $this->qlink(
@ -15,7 +19,8 @@
'config/edituserbackend',
array('backend' => $backendNames[$i]),
array(
'icon' => 'edit',
'icon' => $type === 'external' ?
'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
'class' => 'rowaction',
'title' => sprintf($this->translate('rEdit user backend %s'), $backendNames[$i])
)
@ -34,7 +39,7 @@
</td>
<td data-base-target="_self">
<?php if ($i > 0): ?>
<button type="submit" name="backend_newpos" class="link-like icon-only" value="<?= sprintf(
<button type="submit" name="backend_newpos" class="link-like icon-only animated move-up" value="<?= sprintf(
'%s|%s',
$backendNames[$i],
$i - 1
@ -48,7 +53,7 @@
</button>
<?php endif; ?>
<?php if ($i + 1 < count($backendNames)): ?>
<button type="submit" name="backend_newpos" class="link-like icon-only" value="<?= sprintf(
<button type="submit" name="backend_newpos" class="link-like icon-only animated move-down" value="<?= sprintf(
'%s|%s',
$backendNames[$i],
$i + 1

View File

@ -12,8 +12,5 @@ if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
/>
</form>
<? endif; ?>
<nav>
<h1 id="navigation" class="sr-only"><?= t('Navigation'); ?></h1>
<?= $menuRenderer; ?>
</nav>
<?php endif; ?>
<?= $menuRenderer->setHeading(t('Navigation'))->setElementTag('nav'); ?>

View File

@ -0,0 +1,6 @@
<div class="controls">
<?= $tabs->showOnlyCloseButton(); ?>
</div>
<div class="content">
<?= $form; ?>
</div>

View File

@ -0,0 +1,53 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content" data-base-target="_next">
<a href="<?= $this->href('navigation/add'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create A New Navigation Item'); ?>
</a>
<?php if (count($items) === 0): ?>
<p><?= $this->translate('You did not create any navigation item yet'); ?></p>
<?php else: ?>
<table class="action alternating">
<thead>
<th><?= $this->translate('Navigation'); ?></th>
<th style="width: 10em"><?= $this->translate('Type'); ?></th>
<th style="width: 5em"><?= $this->translate('Shared'); ?></th>
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
</thead>
<tbody>
<?php foreach ($items as $name => $item): ?>
<tr>
<td><?= $this->qlink(
$name,
'navigation/edit',
array('name' => $name),
array(
'title' => sprintf($this->translate('Edit navigation item %s'), $name)
)
); ?></td>
<td><?= $item->type && isset($types[$item->type])
? $this->escape($types[$item->type])
: $this->escape($this->translate('Unknown')); ?></td>
<td><?= $item->owner ? $this->translate('Yes') : $this->translate('No'); ?></td>
<td><?= $this->qlink(
'',
'navigation/remove',
array('name' => $name),
array(
'icon' => 'trash',
'title' => sprintf($this->translate('Remove navigation item %s'), $name)
)
); ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<?php endif ?>
</div>

View File

@ -0,0 +1,58 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs; ?>
<?= $this->sortBox; ?>
<?= $this->limiter; ?>
<?= $this->paginator; ?>
<?= $this->filterEditor; ?>
</div>
<?php endif ?>
<div class="content" data-base-target="_next">
<?php if (count($items) === 0): ?>
<p><?= $this->translate('There are currently no navigation items being shared'); ?></p>
<?php else: ?>
<table class="action alternating">
<thead>
<th><?= $this->translate('Shared Navigation'); ?></th>
<th style="width: 10em"><?= $this->translate('Type'); ?></th>
<th style="width: 10em"><?= $this->translate('Owner'); ?></th>
<th style="width: 5em"><?= $this->translate('Unshare'); ?></th>
</thead>
<tbody>
<?php foreach ($items as $name => $item): ?>
<tr>
<td><?= $this->qlink(
$name,
'navigation/edit',
array(
'name' => $name,
'referrer' => 'shared'
),
array(
'title' => sprintf($this->translate('Edit shared navigation item %s'), $name)
)
); ?></td>
<td><?= $item->type && isset($types[$item->type])
? $this->escape($types[$item->type])
: $this->escape($this->translate('Unknown')); ?></td>
<td><?= $this->escape($item->owner); ?></td>
<?php if ($item->parent): ?>
<td><?= $this->icon(
'block',
sprintf(
$this->translate(
'This is a child of the navigation item %1$s. You can'
. ' only unshare this item by unsharing %1$s'
),
$item->parent
)
); ?></td>
<?php else: ?>
<td data-base-target="_self"><?= $removeForm->setDefault('name', $name); ?></td>
<?php endif ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
<?php endif ?>
</div>

View File

@ -19,14 +19,19 @@
<tr>
</thead>
<tbody>
<?php foreach ($backendNames as $backendName): ?>
<?php foreach ($backendNames as $backendName => $config):
$type = $config->get('backend');
?>
<tr>
<td class="backend-name">
<?= $this->qlink(
$backendName,
'usergroupbackend/edit',
array('backend' => $backendName),
array('title' => sprintf($this->translate('Edit user group backend %s'), $backendName))
array(
'icon' => $type === 'external' ? 'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
'title' => sprintf($this->translate('Edit user group backend %s'), $backendName)
)
); ?>
</td>
<td class="backend-remove"><?= $this->qlink(

View File

@ -43,6 +43,7 @@ Directive | Description
**resource** | The name of the LDAP resource defined in [resources.ini](resources.md#resources).
**user_class** | LDAP user class.
**user_name_attribute** | LDAP attribute which contains the username.
**filter** | LDAP search filter.
**Example:**
@ -52,6 +53,7 @@ backend = ldap
resource = my_ldap
user_class = inetOrgPerson
user_name_attribute = uid
filter = "memberOf=cn=icinga_users,cn=groups,cn=accounts,dc=icinga,dc=org"
```
Note that in case the set *user_name_attribute* holds multiple values it is required that all of its
@ -62,14 +64,14 @@ with Icinga Web 2 (e.g. an alias) no matter what the primary user id might actua
Directive | Description
------------------------|------------
**backend** | `ad`
**backend** | `msldap`
**resource** | The name of the LDAP resource defined in [resources.ini](resources.md#resources).
**Example:**
```
[auth_ad]
backend = ad
backend = msldap
resource = my_ad
```

View File

@ -101,12 +101,11 @@ The packages for Debian wheezy depend on other packages which are distributed as
### <a id="installing-from-package-example"></a> Installing Icinga Web 2
You can install Icinga Web 2 by using your distribution's package manager to install the `icingaweb2` package.
Below is a list with examples for various distributions. The additional package `icingacli` is necessary
for being able to follow further steps in this guide.
Below is a list with examples for various distributions. The additional package `icingacli` is necessary on RPM based systems for being able to follow further steps in this guide. In DEB based systems, the icingacli binary is included in the icingaweb2 package.
**Debian and Ubuntu**:
````
apt-get install icingaweb2 icingacli
apt-get install icingaweb2
````
For Debian wheezy please read the [package repositories notes](#package-repositories-wheezy-notes).
@ -275,9 +274,11 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
`icingaweb_group_membership` were altered to ensure referential integrity.
Please use the upgrade script located in **etc/schema/** to update your
database schema
* Users who are using PostgreSQL < v9.1 are required to upgrade their
environment to v9.1+ as this is the new minimum required version
for utilizing PostgreSQL as database backend
* The restrictions `monitoring/hosts/filter` and `monitoring/services/filter`
provided by the monitoring module were merged together. The new
restriction is called `monitoring/filter/objects` and supports only a
@ -287,12 +288,21 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
## <a id="upgrading-to-2.0.0"></a> Upgrading to Icinga Web 2 2.0.0
* Icinga Web 2 installations from package on RHEL/CentOS 7 now depend on `php-ZendFramework` which is available through
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
through the package `icingaweb2-vendor-zend`. After upgrading, please make sure to remove the package
`icingaweb2-vendor-zend`.
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
through the package `icingaweb2-vendor-zend`. After upgrading, please make sure to remove the package
`icingaweb2-vendor-zend`.
* Icinga Web 2 version 2.0.0 requires permissions for accessing modules. Those permissions are automatically generated
for each installed module in the format `module/<moduleName>`. Administrators have to grant the module permissions to
users and/or user groups in the roles configuration for permitting access to specific modules.
In addition, restrictions provided by modules are now configurable for each installed module too. Before,
a module had to be enabled before having the possibility to configure restrictions.
for each installed module in the format `module/<moduleName>`. Administrators have to grant the module permissions to
users and/or user groups in the roles configuration for permitting access to specific modules.
In addition, restrictions provided by modules are now configurable for each installed module too. Before,
a module had to be enabled before having the possibility to configure restrictions.
* The **instances.ini** configuration file provided by the monitoring module
has been renamed to **commandtransports.ini**. The content and location of
the file remains unchanged.
* The location of a user's preferences has been changed from
**<config-dir>/preferences/<username>.ini** to
**<config-dir>/preferences/<username>/config.ini**.
The content of the file remains unchanged.

View File

@ -78,9 +78,9 @@ abstract class ApplicationBootstrap
protected $configDir;
/**
* Icinga auto loader
* Icinga class loader
*
* @var Loader
* @var ClassLoader
*/
private $loader;
@ -183,7 +183,7 @@ abstract class ApplicationBootstrap
/**
* Getter for class loader
*
* @return Loader
* @return ClassLoader
*/
public function getLoader()
{
@ -339,15 +339,15 @@ abstract class ApplicationBootstrap
}
/**
* Setup Icinga auto loader
* Setup Icinga class loader
*
* @return $this
*/
public function setupAutoloader()
{
require $this->libDir . '/Icinga/Application/Loader.php';
require $this->libDir . '/Icinga/Application/ClassLoader.php';
$this->loader = new Loader();
$this->loader = new ClassLoader();
$this->loader->registerNamespace('Icinga', $this->libDir. '/Icinga');
$this->loader->register();

View File

@ -0,0 +1,113 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application;
/**
* PSR-4 class loader
*/
class ClassLoader
{
/**
* Namespace separator
*/
const NAMESPACE_SEPARATOR = '\\';
/**
* Namespaces
*
* @var array
*/
private $namespaces = array();
/**
* Register a base directory for a namespace prefix
*
* @param string $namespace
* @param string $directory
*
* @return $this
*/
public function registerNamespace($namespace, $directory)
{
$this->namespaces[$namespace] = $directory;
return $this;
}
/**
* Test whether a namespace exists
*
* @param string $namespace
*
* @return bool
*/
public function hasNamespace($namespace)
{
return array_key_exists($namespace, $this->namespaces);
}
/**
* Get the source file of the given class or interface
*
* @param string $class Name of the class or interface
*
* @return string|null
*/
public function getSourceFile($class)
{
foreach ($this->namespaces as $namespace => $dir) {
if ($class === strstr($class, $namespace)) {
$classPath = str_replace(
self::NAMESPACE_SEPARATOR,
DIRECTORY_SEPARATOR,
substr($class, strlen($namespace))
) . '.php';
if (file_exists($file = $dir . $classPath)) {
return $file;
}
}
}
return null;
}
/**
* Load the given class or interface
*
* @param string $class Name of the class or interface
*
* @return bool Whether the class or interface has been loaded
*/
public function loadClass($class)
{
if ($file = $this->getSourceFile($class)) {
require $file;
return true;
}
return false;
}
/**
* Register {@link loadClass()} as an autoloader
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Unregister {@link loadClass()} as an autoloader
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Unregister this as an autoloader
*/
public function __destruct()
{
$this->unregister();
}
}

View File

@ -12,6 +12,7 @@ use Icinga\Data\ConfigObject;
use Icinga\Data\Selectable;
use Icinga\Data\SimpleQuery;
use Icinga\File\Ini\IniWriter;
use Icinga\File\Ini\IniParser;
use Icinga\Exception\NotReadableError;
/**
@ -313,9 +314,7 @@ class Config implements Countable, Iterator, Selectable
if ($filepath === false) {
$emptyConfig->setConfigFile($file);
} elseif (is_readable($filepath)) {
$config = new static(new ConfigObject(parse_ini_file($filepath, true)));
$config->setConfigFile($filepath);
return $config;
return IniParser::parseIniFile($filepath);
} elseif (@file_exists($filepath)) {
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
}
@ -359,11 +358,7 @@ class Config implements Countable, Iterator, Selectable
*/
protected function getIniWriter($filePath = null, $fileMode = null)
{
return new IniWriter(array(
'config' => $this,
'filename' => $filePath,
'filemode' => $fileMode
));
return new IniWriter($this, $filePath, $fileMode);
}
/**
@ -418,7 +413,6 @@ class Config implements Countable, Iterator, Selectable
static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
);
}
return $moduleConfigs[$configname];
}

View File

@ -5,6 +5,9 @@ namespace Icinga\Application;
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
use Icinga\Web\Request;
use Icinga\Web\Response;
/**
* Use this if you want to make use of Icinga functionality in other web projects
*
@ -16,6 +19,40 @@ require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
*/
class EmbeddedWeb extends ApplicationBootstrap
{
/**
* Request object
*
* @var Request
*/
protected $request;
/**
* Response
*
* @var Response
*/
protected $response;
/**
* Get the request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the response
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/**
* Embedded bootstrap parts
*
@ -26,10 +63,34 @@ class EmbeddedWeb extends ApplicationBootstrap
{
return $this
->setupZendAutoloader()
->loadConfig()
->setupErrorHandling()
->loadConfig()
->setupRequest()
->setupResponse()
->setupTimezone()
->setupModuleManager()
->loadEnabledModules();
}
/**
* Set the request
*
* @return $this
*/
protected function setupRequest()
{
$this->request = new Request();
return $this;
}
/**
* Set the response
*
* @return $this
*/
protected function setupResponse()
{
$this->response = new Response();
return $this;
}
}

View File

@ -1,138 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application;
use Icinga\Exception\ProgrammingError;
class Loader
{
/**
* Namespace separator
*/
const NAMESPACE_SEPARATOR = '\\';
/**
* List of namespaces
*
* @var array
*/
private $namespaces = array();
/**
* Detach spl autoload method from stack
*/
public function __destruct()
{
$this->unRegister();
}
/**
* Register new namespace for directory
*
* @param string $namespace
* @param string $directory
*
* @throws ProgrammingError
*/
public function registerNamespace($namespace, $directory)
{
if (!is_dir($directory)) {
throw new ProgrammingError(
'Directory "%s" for namespace "%s" does not exist',
$directory,
$namespace
);
}
$this->namespaces[$namespace] = $directory;
}
/**
* Test if a namespace exists
*
* @param string $namespace
*
* @return bool
*/
public function hasNamespace($namespace)
{
return array_key_exists($namespace, $this->namespaces);
}
/**
* Class loader
*
* Ignores all but classes in registered namespaces.
*
* @param string $class
*
* @return boolean
*/
public function loadClass($class)
{
$namespace = $this->getNamespaceForClass($class);
if ($namespace) {
$file = $this->namespaces[$namespace] . preg_replace('/^' . preg_quote($namespace) . '/', '', $class);
$file = str_replace(self::NAMESPACE_SEPARATOR, '/', $file) . '.php';
if (@file_exists($file)) {
require_once $file;
return true;
}
}
return false;
}
/**
* Test if we have a registered namespaces for this class
*
* Return is the longest match in the array found
*
* @param string $className
*
* @return bool|string
*/
private function getNamespaceForClass($className)
{
$testNamespace = '';
$testLength = 0;
foreach (array_keys($this->namespaces) as $namespace) {
$stub = preg_replace(
'/^' . preg_quote($namespace) . '(' . preg_quote(self::NAMESPACE_SEPARATOR) . '|$)/', '', $className
);
$length = strlen($className) - strlen($stub);
if ($length > $testLength) {
$testLength = $length;
$testNamespace = $namespace;
}
}
if ($testLength > 0) {
return $testNamespace;
}
return false;
}
/**
* Effectively registers the autoloader the PHP/SPL way
*/
public function register()
{
// Think about to add class pathes to php include path
// this could be faster (tg)
spl_autoload_register(array(&$this, 'loadClass'));
}
/**
* Detach autoloader from spl registration
*/
public function unRegister()
{
spl_autoload_unregister(array(&$this, 'loadClass'));
}
}

View File

@ -36,7 +36,7 @@ class StdoutWriter extends LogWriter
$color = 'red';
break;
case Logger::WARNING:
$color = 'orange';
$color = 'yellow';
break;
case Logger::INFO:
$color = 'green';

View File

@ -0,0 +1,54 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application\Modules;
/**
* Container for module dashboards
*/
class DashboardContainer extends NavigationItemContainer
{
/**
* This dashboard's dashlets
*
* @var array
*/
protected $dashlets;
/**
* Set this dashboard's dashlets
*
* @param array $dashlets
*
* @return $this
*/
public function setDashlets(array $dashlets)
{
$this->dashlets = $dashlets;
return $this;
}
/**
* Return this dashboard's dashlets
*
* @return array
*/
public function getDashlets()
{
return $this->dashlets ?: array();
}
/**
* Add a new dashlet
*
* @param string $name
* @param string $url
*
* @return $this
*/
public function add($name, $url)
{
$this->dashlets[$name] = $url;
return $this;
}
}

View File

@ -281,7 +281,10 @@ class Manager
public function disableModule($name)
{
if (! $this->hasEnabled($name)) {
return $this;
throw new ConfigurationError(
'Cannot disable module "%s". Module is not installed.',
$name
);
}
if (! is_writable($this->enableDir)) {

View File

@ -0,0 +1,55 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application\Modules;
/**
* Container for module menu items
*/
class MenuItemContainer extends NavigationItemContainer
{
/**
* This menu item's children
*
* @var MenuItemContainer[]
*/
protected $children;
/**
* Set this menu item's children
*
* @param MenuItemContainer[] $children
*
* @return $this
*/
public function setChildren(array $children)
{
$this->children = $children;
return $this;
}
/**
* Return this menu item's children
*
* @return array
*/
public function getChildren()
{
return $this->children ?: array();
}
/**
* Add a new sub menu
*
* @param string $name
* @param array $properties
*
* @return MenuItemContainer The newly added sub menu
*/
public function add($name, array $properties = array())
{
$child = new MenuItemContainer($name, $properties);
$this->children[] = $child;
return $child;
}
}

View File

@ -4,23 +4,24 @@
namespace Icinga\Application\Modules;
use Exception;
use Zend_Controller_Router_Route;
use Zend_Controller_Router_Route_Abstract;
use Zend_Controller_Router_Route as Route;
use Zend_Controller_Router_Route_Regex as RegexRoute;
use Zend_Controller_Router_Route_Regex;
use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Util\Translator;
use Icinga\Web\Hook;
use Icinga\Web\Menu;
use Icinga\Web\Widget;
use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Application\Modules\DashboardContainer;
use Icinga\Application\Modules\MenuItemContainer;
use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Exception\ProgrammingError;
use Icinga\Exception\IcingaException;
use Icinga\Util\Translator;
use Icinga\Web\Controller\Dispatcher;
use Icinga\Web\Hook;
use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Widget;
/**
* Module handling
@ -162,6 +163,20 @@ class Module
*/
private $app;
/**
* The CSS/LESS files this module provides
*
* @var array
*/
protected $cssFiles = array();
/**
* The Javascript files this module provides
*
* @var array
*/
protected $jsFiles = array();
/**
* Routes to add to the route chain
*
@ -174,7 +189,7 @@ class Module
/**
* A set of menu elements
*
* @var array
* @var MenuItemContainer[]
*/
protected $menuItems = array();
@ -206,6 +221,13 @@ class Module
*/
protected $userGroupBackends = array();
/**
* This module's configurable navigation items
*
* @var array
*/
protected $navigationItems = array();
/**
* Create a new module object
*
@ -262,38 +284,98 @@ class Module
}
/**
* Get all pane items
* Return this module's dashboard
*
* @return array
* @return Navigation
*/
public function getPaneItems()
public function getDashboard()
{
$this->launchConfigScript();
return $this->paneItems;
return $this->createDashboard($this->paneItems);
}
/**
* Add a pane to dashboard
* Create and return a new navigation for the given dashboard panes
*
* @param string $name
* @param DashboardContainer[] $panes
*
* @return Pane
* @return Navigation
*/
protected function dashboard($name)
public function createDashboard(array $panes)
{
$this->paneItems[$name] = new Pane($name);
$navigation = new Navigation();
foreach ($panes as $pane) {
/** @var DashboardContainer $pane */
$dashlets = array();
foreach ($pane->getDashlets() as $dashletName => $dashletUrl) {
$dashlets[$this->translate($dashletName)] = $dashletUrl;
}
$navigation->addItem(
$pane->getName(),
array_merge(
$pane->getProperties(),
array(
'label' => $this->translate($pane->getName()),
'type' => 'dashboard-pane',
'dashlets' => $dashlets
)
)
);
}
return $navigation;
}
/**
* Add or get a dashboard pane
*
* @param string $name
* @param array $properties
*
* @return DashboardContainer
*/
protected function dashboard($name, array $properties = array())
{
if (array_key_exists($name, $this->paneItems)) {
$this->paneItems[$name]->setProperties($properties);
} else {
$this->paneItems[$name] = new DashboardContainer($name, $properties);
}
return $this->paneItems[$name];
}
/**
* Get all menu items
* Return this module's menu
*
* @return array
* @return Navigation
*/
public function getMenuItems()
public function getMenu()
{
$this->launchConfigScript();
return $this->menuItems;
return $this->createMenu($this->menuItems);
}
/**
* Create and return a new navigation for the given menu items
*
* @param MenuItemContainer[] $items
*
* @return Navigation
*/
private function createMenu(array $items)
{
$navigation = new Navigation();
foreach ($items as $item) {
/** @var MenuItemContainer $item */
$navigationItem = $navigation->createItem($item->getName(), $item->getProperties());
$navigationItem->setChildren($this->createMenu($item->getChildren()));
$navigationItem->setLabel($this->translate($item->getName()));
$navigation->addItem($navigationItem);
}
return $navigation;
}
/**
@ -302,14 +384,14 @@ class Module
* @param string $name
* @param array $properties
*
* @return Menu
* @return MenuItemContainer
*/
protected function menuSection($name, array $properties = array())
{
if (array_key_exists($name, $this->menuItems)) {
$this->menuItems[$name]->setProperties($properties);
} else {
$this->menuItems[$name] = new Menu($name, new ConfigObject($properties));
$this->menuItems[$name] = new MenuItemContainer($name, $properties);
}
return $this->menuItems[$name];
@ -388,6 +470,19 @@ class Module
return $manager->getModule($name);
}
/**
* Provide an additional CSS/LESS file
*
* @param string $path The path to the file, relative to self::$cssdir
*
* @return $this
*/
protected function provideCssFile($path)
{
$this->cssFiles[] = $this->cssdir . DIRECTORY_SEPARATOR . $path;
return $this;
}
/**
* Test if module provides css
*
@ -395,7 +490,12 @@ class Module
*/
public function hasCss()
{
return file_exists($this->getCssFilename());
if (file_exists($this->getCssFilename())) {
return true;
}
$this->launchConfigScript();
return !empty($this->cssFiles);
}
/**
@ -408,6 +508,32 @@ class Module
return $this->cssdir . '/module.less';
}
/**
* Return the CSS/LESS files this module provides
*
* @return array
*/
public function getCssFiles()
{
$this->launchConfigScript();
$files = $this->cssFiles;
$files[] = $this->getCssFilename();
return $files;
}
/**
* Provide an additional Javascript file
*
* @param string $path The path to the file, relative to self::$jsdir
*
* @return $this
*/
protected function provideJsFile($path)
{
$this->jsFiles[] = $this->jsdir . DIRECTORY_SEPARATOR . $path;
return $this;
}
/**
* Test if module provides js
*
@ -415,7 +541,12 @@ class Module
*/
public function hasJs()
{
return file_exists($this->getJsFilename());
if (file_exists($this->getJsFilename())) {
return true;
}
$this->launchConfigScript();
return !empty($this->jsFiles);
}
/**
@ -428,6 +559,19 @@ class Module
return $this->jsdir . '/module.js';
}
/**
* Return the Javascript files this module provides
*
* @return array
*/
public function getJsFiles()
{
$this->launchConfigScript();
$files = $this->jsFiles;
$files[] = $this->getJsFilename();
return $files;
}
/**
* Get the module name
*
@ -694,6 +838,7 @@ class Module
{
$this->launchConfigScript();
$tabs = Widget::create('tabs');
/** @var \Icinga\Web\Widget\Tabs $tabs */
$tabs->add('info', array(
'url' => 'config/module',
'urlParams' => array('name' => $this->getName()),
@ -753,6 +898,17 @@ class Module
return $this->userGroupBackends;
}
/**
* Return this module's configurable navigation items
*
* @return array
*/
public function getNavigationItems()
{
$this->launchConfigScript();
return $this->navigationItems;
}
/**
* Provide a named permission
*
@ -858,7 +1014,21 @@ class Module
}
/**
* Register module namespaces on the autoloader
* Provide a new type of configurable navigation item with a optional label
*
* @param string $type
* @param string $label
*
* @return $this
*/
protected function provideNavigationItem($type, $label = null)
{
$this->navigationItems[$type] = $label ?: $type;
return $this;
}
/**
* Register module namespaces on our class loader
*
* @return $this
*/
@ -868,16 +1038,17 @@ class Module
return $this;
}
$loader = $this->app->getLoader();
$moduleName = ucfirst($this->getName());
$moduleLibraryDir = $this->getLibDir(). '/'. $moduleName;
if (is_dir($moduleLibraryDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName, $moduleLibraryDir);
$loader->registerNamespace('Icinga\\Module\\' . $moduleName, $moduleLibraryDir);
}
$moduleFormDir = $this->getFormDir();
if (is_dir($moduleFormDir)) {
$this->app->getLoader()->registerNamespace('Icinga\\Module\\' . $moduleName. '\\Forms', $moduleFormDir);
$loader->registerNamespace('Icinga\\Module\\' . $moduleName. '\\Forms', $moduleFormDir);
}
$this->registeredAutoloader = true;
@ -939,19 +1110,23 @@ class Module
*/
protected function registerWebIntegration()
{
if (!$this->app->isWeb()) {
if (! $this->app->isWeb()) {
return $this;
}
if (file_exists($this->controllerdir) && is_dir($this->controllerdir)) {
$moduleControllerDir = $this->getControllerDir();
if (is_dir($moduleControllerDir)) {
$this->app->getfrontController()->addControllerDirectory(
$this->controllerdir,
$this->name
$moduleControllerDir,
$this->getName()
);
$this->app->getLoader()->registerNamespace(
'Icinga\\Module\\' . ucfirst($this->getName()) . '\\' . Dispatcher::CONTROLLER_NAMESPACE,
$moduleControllerDir
);
}
$this->registerLocales()
->registerRoutes();
$this
->registerLocales()
->registerRoutes();
return $this;
}
@ -963,27 +1138,30 @@ class Module
protected function registerRoutes()
{
$router = $this->app->getFrontController()->getRouter();
/** @var \Zend_Controller_Router_Rewrite $router */
foreach ($this->routes as $name => $route) {
$router->addRoute($name, $route);
}
$router->addRoute(
$this->name . '_jsprovider',
new Route(
new Zend_Controller_Router_Route(
'js/' . $this->name . '/:file',
array(
'action' => 'javascript',
'controller' => 'static',
'action' =>'javascript',
'module' => 'default',
'module_name' => $this->name
)
)
);
$router->addRoute(
$this->name . '_img',
new RegexRoute(
new Zend_Controller_Router_Route_Regex(
'img/' . $this->name . '/(.+)',
array(
'controller' => 'static',
'action' => 'img',
'controller' => 'static',
'module' => 'default',
'module_name' => $this->name
),
array(

View File

@ -0,0 +1,117 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Application\Modules;
use Icinga\Exception\ProgrammingError;
/**
* Container for module navigation items
*/
abstract class NavigationItemContainer
{
/**
* This navigation item's name
*
* @var string
*/
protected $name;
/**
* This navigation item's properties
*
* @var array
*/
protected $properties;
/**
* Create a new NavigationItemContainer
*
* @param string $name
* @param array $properties
*/
public function __construct($name, array $properties = array())
{
$this->name = $name;
$this->properties = $properties;
}
/**
* Set this menu item's name
*
* @param string $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Return this menu item's name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set this menu item's properties
*
* @param array $properties
*
* @return $this
*/
public function setProperties(array $properties)
{
$this->properties = $properties;
return $this;
}
/**
* Return this menu item's properties
*
* @return array
*/
public function getProperties()
{
return $this->properties ?: array();
}
/**
* Allow dynamic setters and getters for properties
*
* @param string $name
* @param array $arguments
*
* @return mixed
*
* @throws ProgrammingError In case the called method is not supported
*/
public function __call($name, $arguments)
{
if (method_exists($this, $name)) {
return call_user_method_array($name, $this, $arguments);
}
$type = substr($name, 0, 3);
if ($type !== 'set' && $type !== 'get') {
throw new ProgrammingError(
'Dynamic method %s is not supported. Only getters (get*) and setters (set*) are.',
$name
);
}
$propertyName = strtolower(join('_', preg_split('~(?=[A-Z])~', lcfirst(substr($name, 3)))));
if ($type === 'set') {
$this->properties[$propertyName] = $arguments[0];
return $this;
} else { // $type === 'get'
return array_key_exists($propertyName, $this->properties) ? $this->properties[$propertyName] : null;
}
}
}

View File

@ -318,6 +318,41 @@ class Platform
return false;
}
/**
* Return whether it's possible to connect to a LDAP server
*
* Checks whether the ldap extension is loaded
*
* @return bool
*/
public static function hasLdapSupport()
{
return static::extensionLoaded('ldap');
}
/**
* Return whether it's possible to connect to any of the supported database servers
*
* @return bool
*/
public static function hasDatabaseSupport()
{
return static::hasMssqlSupport() || static::hasMysqlSupport() || static::hasOciSupport()
|| static::hasOracleSupport() || static::hasPostgresqlSupport();
}
/**
* Return whether it's possible to connect to a MSSQL database
*
* Checks whether the mssql pdo extension has been loaded and Zend framework adapter for MSSQL is available
*
* @return bool
*/
public static function hasMssqlSupport()
{
return static::extensionLoaded('mssql') && static::classExists('Zend_Db_Adapter_Pdo_Mssql');
}
/**
* Return whether it's possible to connect to a MySQL database
*
@ -330,6 +365,30 @@ class Platform
return static::extensionLoaded('mysql') && static::classExists('Zend_Db_Adapter_Pdo_Mysql');
}
/**
* Return whether it's possible to connect to a Oracle database using OCI8
*
* Checks whether the OCI8 extension has been loaded and the Zend framework adapter for Oracle is available
*
* @return bool
*/
public static function hasOciSupport()
{
return static::extensionLoaded('oci8') && static::classExists('Zend_Db_Adapter_Oracle');
}
/**
* Return whether it's possible to connect to a Oracle database using PDO_OCI
*
* Checks whether the OCI PDO extension has been loaded and the Zend framework adapter for Oci is available
*
* @return bool
*/
public static function hasOracleSupport()
{
return static::extensionLoaded('pdo_oci') && static::classExists('Zend_Db_Adapter_Pdo_Mysql');
}
/**
* Return whether it's possible to connect to a PostgreSQL database
*

View File

@ -3,12 +3,15 @@
namespace Icinga\Application;
/**
* Retrieve the version of Icinga Web 2
*/
class Version
{
/**
* Get the version of this instance of Icinga Web 2
*
* @return array|bool array on success, false otherwise
* @return array|false array on success, false otherwise
*/
public static function get()
{

View File

@ -3,7 +3,7 @@
namespace Icinga\Application;
require_once __DIR__ . '/ApplicationBootstrap.php';
require_once __DIR__ . '/EmbeddedWeb.php';
use Zend_Controller_Action_HelperBroker;
use Zend_Controller_Front;
@ -11,14 +11,13 @@ use Zend_Controller_Router_Route;
use Zend_Layout;
use Zend_Paginator;
use Zend_View_Helper_PaginationControl;
use Icinga\Application\Logger;
use Icinga\Authentication\Auth;
use Icinga\User;
use Icinga\Util\TimezoneDetect;
use Icinga\Util\Translator;
use Icinga\Web\Controller\Dispatcher;
use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Notification;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\Session;
use Icinga\Web\Session\Session as BaseSession;
use Icinga\Web\View;
@ -28,11 +27,11 @@ use Icinga\Web\View;
*
* Usage example:
* <code>
* use Icinga\Application\EmbeddedWeb;
* EmbeddedWeb::start();
* use Icinga\Application\Web;
* Web::start();
* </code>
*/
class Web extends ApplicationBootstrap
class Web extends EmbeddedWeb
{
/**
* View object
@ -48,20 +47,6 @@ class Web extends ApplicationBootstrap
*/
private $frontController;
/**
* Request object
*
* @var Request
*/
private $request;
/**
* Response
*
* @var Response
*/
protected $response;
/**
* Session object
*
@ -100,14 +85,14 @@ class Web extends ApplicationBootstrap
->setupNotifications()
->setupRequest()
->setupResponse()
->setupUserBackendFactory()
->setupUser()
->setupTimezone()
->setupLogger()
->setupInternationalization()
->setupZendMvc()
->setupFormNamespace()
->setupNamespaces()
->setupModuleManager()
->setupUserBackendFactory()
->loadSetupModuleIfNecessary()
->loadEnabledModules()
->setupRoute()
@ -145,26 +130,6 @@ class Web extends ApplicationBootstrap
return $this->frontController;
}
/**
* Get the request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the response
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/**
* Getter for view
*
@ -175,6 +140,204 @@ class Web extends ApplicationBootstrap
return $this->viewRenderer;
}
private function hasAccessToSharedNavigationItem(& $config)
{
// TODO: Provide a more sophisticated solution
if (isset($config['owner']) && $config['owner'] === $this->user->getUsername()) {
unset($config['owner']);
return true;
}
if (isset($config['users'])) {
$users = array_map('trim', explode(',', strtolower($config['users'])));
if (in_array($this->user->getUsername(), $users, true)) {
unset($config['users']);
return true;
}
}
if (isset($config['groups'])) {
$groups = array_map('trim', explode(',', strtolower($config['groups'])));
$userGroups = array_map('strtolower', $this->user->getGroups());
$matches = array_intersect($userGroups, $groups);
if (! empty($matches)) {
unset($config['groups']);
return true;
}
}
return false;
}
/**
* Load and return the shared navigation of the given type
*
* @param string $type
*
* @return Navigation
*/
public function getSharedNavigation($type)
{
$config = Config::app('navigation')->getConfigObject();
$config->setKeyColumn('name');
if ($type === 'dashboard-pane') {
$panes = array();
foreach ($config->select()->where('type', 'dashlet') as $dashletName => $dashletConfig) {
if ($this->hasAccessToSharedNavigationItem($dashletConfig)) {
// TODO: Throw ConfigurationError if pane or url is missing
$panes[$dashletConfig->pane][$dashletName] = $dashletConfig->url;
}
}
$navigation = new Navigation();
foreach ($panes as $paneName => $dashlets) {
$navigation->addItem(
$paneName,
array(
'type' => 'dashboard-pane',
'dashlets' => $dashlets
)
);
}
} else {
$items = array();
foreach ($config->select()->where('type', $type) as $name => $typeConfig) {
if ($this->hasAccessToSharedNavigationItem($typeConfig)) {
$items[$name] = $typeConfig;
}
}
$navigation = Navigation::fromConfig($items);
}
return $navigation;
}
/**
* Return the app's menu
*
* @return Navigation
*/
public function getMenu()
{
if ($this->user !== null) {
$menu = array(
'dashboard' => array(
'label' => t('Dashboard'),
'url' => 'dashboard',
'icon' => 'dashboard',
'priority' => 10
),
'system' => array(
'label' => t('System'),
'icon' => 'services',
'priority' => 700,
'renderer' => array(
'SummaryNavigationItemRenderer',
'state' => 'critical'
),
'children' => array(
'about' => array(
'label' => t('About'),
'url' => 'about',
'priority' => 701
)
)
),
'configuration' => array(
'label' => t('Configuration'),
'icon' => 'wrench',
'permission' => 'config/*',
'priority' => 800,
'children' => array(
'application' => array(
'label' => t('Application'),
'url' => 'config/general',
'permission' => 'config/application/*',
'priority' => 810
),
'navigation' => array(
'label' => t('Shared Navigation'),
'url' => 'navigation/shared',
'permission' => 'config/application/navigation',
'priority' => 820,
),
'authentication' => array(
'label' => t('Authentication'),
'url' => 'config/userbackend',
'permission' => 'config/authentication/*',
'priority' => 830
),
'roles' => array(
'label' => t('Roles'),
'url' => 'role/list',
'permission' => 'config/authentication/roles/show',
'priority' => 840
),
'users' => array(
'label' => t('Users'),
'url' => 'user/list',
'permission' => 'config/authentication/users/show',
'priority' => 850
),
'groups' => array(
'label' => t('Usergroups'),
'url' => 'group/list',
'permission' => 'config/authentication/groups/show',
'priority' => 860
),
'modules' => array(
'label' => t('Modules'),
'url' => 'config/modules',
'permission' => 'config/modules',
'priority' => 890
)
)
),
'user' => array(
'label' => $this->user->getUsername(),
'icon' => 'user',
'priority' => 900,
'children' => array(
'preferences' => array(
'label' => t('Preferences'),
'url' => 'preference',
'priority' => 910
),
'navigation' => array(
'label' => t('Navigation'),
'url' => 'navigation',
'priority' => 920
),
'logout' => array(
'label' => t('Logout'),
'url' => 'authentication/logout',
'priority' => 990,
'renderer' => array(
'NavigationItemRenderer',
'target' => '_self'
)
)
)
)
);
if (Logger::writesToFile()) {
$menu['system']['children']['application_log'] = array(
'label' => t('Application Log'),
'url' => 'list/applicationlog',
'priority' => 710
);
}
} else {
$menu = array();
}
return Navigation::fromArray($menu)->load('menu-item');
}
/**
* Dispatch public interface
*/
@ -211,7 +374,7 @@ class Web extends ApplicationBootstrap
$auth = Auth::getInstance();
if ($auth->isAuthenticated()) {
$user = $auth->getUser();
$this->request->setUser($user);
$this->getRequest()->setUser($user);
$this->user = $user;
}
return $this;
@ -239,28 +402,6 @@ class Web extends ApplicationBootstrap
return $this;
}
/**
* Set the request
*
* @return $this
*/
private function setupRequest()
{
$this->request = new Request();
return $this;
}
/**
* Set the response
*
* @return $this
*/
protected function setupResponse()
{
$this->response = new Response();
return $this;
}
/**
* Instantiate front controller
*
@ -269,11 +410,22 @@ class Web extends ApplicationBootstrap
private function setupFrontController()
{
$this->frontController = Zend_Controller_Front::getInstance();
$this->frontController->setRequest($this->request);
$this->frontController->setDispatcher(new Dispatcher());
$this->frontController->setRequest($this->getRequest());
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
$displayExceptions = $this->config->get('global', 'show_stacktraces', true);
if ($this->user !== null && $this->user->can('application/stacktraces')) {
$displayExceptions = $this->user->getPreferences()->getValue(
'icingaweb',
'show_stacktraces',
$displayExceptions
);
}
$this->frontController->setParams(
array(
'displayExceptions' => true
'displayExceptions' => $displayExceptions
)
);
return $this;
@ -355,16 +507,22 @@ class Web extends ApplicationBootstrap
}
/**
* Setup an autoloader namespace for Icinga\Forms
* Setup class loader namespaces for Icinga\Controllers and Icinga\Forms
*
* @return $this
*/
private function setupFormNamespace()
private function setupNamespaces()
{
$this->getLoader()->registerNamespace(
'Icinga\\Forms',
$this->getApplicationDir('forms')
);
$this
->getLoader()
->registerNamespace(
'Icinga\\' . Dispatcher::CONTROLLER_NAMESPACE,
$this->getApplicationDir('controllers')
)
->registerNamespace(
'Icinga\\Forms',
$this->getApplicationDir('forms')
);
return $this;
}
}

View File

@ -3,6 +3,23 @@
use Icinga\Util\Translator;
/**
* No-op translate
*
* Supposed to be used for marking a string as available for translation without actually translating it immediately.
* The returned string is the one given in the input. This does only work with the standard gettext macros t() and mt().
*
* @param string $messageId
*
* @return string
*/
function N_($messageId)
{
return $messageId;
}
if (extension_loaded('gettext')) {
/**

View File

@ -3,11 +3,9 @@
namespace Icinga\Application;
use Icinga\Application\EmbeddedWeb;
use Icinga\Application\Web;
use Icinga\Web\StyleSheet;
use Icinga\Web\JavaScript;
use Icinga\Chart\Inline\PieChart;
use Icinga\Web\JavaScript;
use Icinga\Web\StyleSheet;
error_reporting(E_ALL | E_STRICT);

View File

@ -61,7 +61,14 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
*
* @var array
*/
protected $filterColumns = array('user');
protected $blacklistedQueryColumns = array('user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('user');
/**
* The default sort rules to be applied on a query
@ -98,6 +105,23 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
}
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
$userLabel = t('Username') . ' ' . t('(Case insensitive)');
return array(
$userLabel => 'user',
t('Username') => 'user_name',
t('Active') => 'is_active',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Insert a table row with the given data
*
@ -260,7 +284,7 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
$insp->write($this->ds->inspect());
try {
$users = $this->select()->where('is_active', true)->count();
if ($users >= 1) {
if ($users > 0) {
$insp->write(sprintf('%s active users', $users));
} else {
return $insp->error('0 active users', $users);

View File

@ -50,7 +50,14 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
*
* @var array
*/
protected $filterColumns = array('user');
protected $blacklistedQueryColumns = array('user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('user');
/**
* The default sort rules to be applied on a query
@ -243,6 +250,21 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
);
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('Username') => 'user_name',
t('Active') => 'is_active',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Initialize this repository's conversion rules
*
@ -342,7 +364,12 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
return false;
}
return $this->ds->testCredentials($userDn, $password);
$testCredentialsResult = $this->ds->testCredentials($userDn, $password);
if ($testCredentialsResult) {
$user->setAdditional('ldap_dn', $userDn);
}
return $testCredentialsResult;
} catch (LdapException $e) {
throw new AuthenticationException(
'Failed to authenticate user "%s" against backend "%s". An exception was thrown:',

View File

@ -71,7 +71,14 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
*
* @var array
*/
protected $filterColumns = array('group', 'user');
protected $blacklistedQueryColumns = array('group', 'user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group', 'user');
/**
* The value conversion rules to apply on a query or statement
@ -97,6 +104,26 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
}
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
$userLabel = t('Username') . ' ' . t('(Case insensitive)');
$groupLabel = t('User Group') . ' ' . t('(Case insensitive)');
return array(
$userLabel => 'user',
t('Username') => 'user_name',
$groupLabel => 'group',
t('User Group') => 'group_name',
t('Parent') => 'parent',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Insert a table row with the given data
*

View File

@ -32,7 +32,14 @@ class IniUserGroupBackend extends IniRepository implements UserGroupBackendInter
*
* @var array
*/
protected $filterColumns = array('group');
protected $blacklistedQueryColumns = array('group');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group');
/**
* The value conversion rules to apply on a query or statement
@ -55,6 +62,21 @@ class IniUserGroupBackend extends IniRepository implements UserGroupBackendInter
$this->ds->getConfigObject()->setKeyColumn('name');
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('User Group') => 'group',
t('Parent') => 'parent',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Add a new group to this backend
*

View File

@ -12,6 +12,7 @@ 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
{
@ -83,7 +84,14 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
*
* @var array
*/
protected $filterColumns = array('group', 'user');
protected $blacklistedQueryColumns = array('group', 'user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group', 'user');
/**
* The default sort rules to be applied on a query
@ -457,6 +465,21 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
return array('group' => $columns, 'group_membership' => $columns);
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('Username') => 'user',
t('User Group') => 'group_name',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Initialize this repository's conversion rules
*
@ -510,18 +533,26 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
*/
public function getMemberships(User $user)
{
$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->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 (($userDn = $userQuery->fetchDn()) === null) {
return array();
if (($userDn = $userQuery->fetchDn()) === null) {
return array();
}
}
}
$groupQuery = $this->ds
@ -533,10 +564,12 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
$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;
}

View File

@ -273,30 +273,10 @@ class Loader
protected function searchMatch($needle, $haystack)
{
$stack = $haystack;
$search = $needle;
$this->lastSuggestions = array();
while (strlen($search) > 0) {
$len = strlen($search);
foreach ($stack as & $s) {
$s = substr($s, 0, $len);
}
$res = array_keys($stack, $search, true);
if (count($res) === 1) {
$found = $haystack[$res[0]];
if (substr($found, 0, strlen($needle)) === $needle) {
return $found;
} else {
return false;
}
} elseif (count($res) > 1) {
foreach ($res as $key) {
$this->lastSuggestions[] = $haystack[$key];
}
return false;
}
$search = substr($search, 0, -1);
$this->lastSuggestions = preg_grep(sprintf('/^%s.*$/', preg_quote($needle, '/')), $haystack);
$match = array_search($needle, $haystack, true);
if (false !== $match) {
return $haystack[$match];
}
return false;
}

View File

@ -173,8 +173,10 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess
/**
* Add a new property or section
*
* @param string $key The name of the new property or section
* @param mixed $value The value to set for the new property or section
* @param string $key The name of the new property or section
* @param mixed $value The value to set for the new property or section
*
* @throws ProgrammingError If the key is null
*/
public function offsetSet($key, $value)
{
@ -254,7 +256,7 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess
/**
* Merge the given data with this config
*
* @param array|Config $data An array or a config
* @param array|ConfigObject $data An array or a config
*
* @return $this
*/

View File

@ -80,7 +80,7 @@ class ArrayDatasource implements Selectable
*/
public function select()
{
return new SimpleQuery($this);
return new SimpleQuery(clone $this);
}
/**

View File

@ -43,7 +43,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
private $dbType;
/**
* @var Zend_Db_Adapter_Abstract
* @var \Zend_Db_Adapter_Abstract
*/
private $dbAdapter;
@ -62,8 +62,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
private static $driverOptions = array(
PDO::ATTR_TIMEOUT => 10,
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// TODO: allow configurable PDO::ATTR_PERSISTENT => true
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
/**
@ -112,7 +111,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
/**
* Getter for the Zend_Db_Adapter
*
* @return Zend_Db_Adapter_Abstract
* @return \Zend_Db_Adapter_Abstract
*/
public function getDbAdapter()
{
@ -131,11 +130,16 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
'username' => $this->config->username,
'password' => $this->config->password,
'dbname' => $this->config->dbname,
'persistent' => (bool) $this->config->get('persistent', false),
'options' => & $genericAdapterOptions,
'driver_options' => & $driverOptions
);
$this->dbType = strtolower($this->config->get('db', 'mysql'));
switch ($this->dbType) {
case 'mssql':
$adapter = 'Pdo_Mssql';
$adapterParamaters['pdoType'] = $this->config->get('pdoType', 'dblib');
break;
case 'mysql':
$adapter = 'Pdo_Mysql';
/*
@ -150,19 +154,21 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
. 'NO_AUTO_CREATE_USER,ANSI_QUOTES,PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\';';
$adapterParamaters['port'] = $this->config->get('port', 3306);
break;
case 'oci':
$adapter = 'Oracle';
unset($adapterParamaters['options']);
unset($adapterParamaters['driver_options']);
$adapterParamaters['driver_options'] = array(
'lob_as_string' => true
);
break;
case 'oracle':
$adapter = 'Pdo_Oci';
break;
case 'pgsql':
$adapter = 'Pdo_Pgsql';
$adapterParamaters['port'] = $this->config->get('port', 5432);
break;
/*case 'oracle':
if ($this->dbtype === 'oracle') {
$attributes['persistent'] = true;
}
$this->db = ZfDb::factory($adapter, $attributes);
if ($adapter === 'Oracle') {
$this->db->setLobAsString(false);
}
break;*/
default:
throw new ConfigurationError(
'Backend "%s" is not supported',

View File

@ -47,16 +47,6 @@ class DbQuery extends SimpleQuery
*/
protected $useSubqueryCount = false;
/**
* Set the count maximum
*
* If the count maximum is set, count queries will not count more than that many rows. You should set this
* property only for really heavy queries.
*
* @var int
*/
protected $maxCount;
/**
* Count query result
*
@ -333,19 +323,16 @@ class DbQuery extends SimpleQuery
{
// TODO: there may be situations where we should clone the "select"
$count = $this->dbSelect();
$group = $this->getGroup();
if ($group) {
$count->group($group);
}
$this->applyFilterSql($count);
if ($this->useSubqueryCount || $this->group) {
$group = $this->getGroup();
if ($this->useSubqueryCount || $group) {
$count->columns($this->columns);
if ($group) {
$count->group($group);
}
$columns = array('cnt' => 'COUNT(*)');
return $this->db->select()->from($count, $columns);
}
if ($this->maxCount !== null) {
return $this->db->select()->from($count->limit($this->maxCount));
}
$count->columns(array('cnt' => 'COUNT(*)'));
return $count;

View File

@ -227,6 +227,8 @@ abstract class Filter
* Create filter from queryString
*
* This is still pretty basic, need improvement
*
* @return static
*/
public static function fromQueryString($query)
{

View File

@ -97,18 +97,41 @@ class FilterExpression extends Filter
public function matches($row)
{
if (! isset($row->{$this->column})) {
// TODO: REALLY? Exception?
return false;
}
if (is_array($this->expression)) {
return in_array($row->{$this->column}, $this->expression);
} elseif (strpos($this->expression, '*') === false) {
return (string) $row->{$this->column} === (string) $this->expression;
} else {
$parts = preg_split('~\*~', $this->expression);
foreach ($parts as & $part) {
$part = preg_quote($part);
}
$pattern = '/^' . implode('.*', $parts) . '$/';
return (bool) preg_match($pattern, $row->{$this->column});
}
$expression = (string) $this->expression;
if (strpos($expression, '*') === false) {
if (is_array($row->{$this->column})) {
return in_array($expression, $row->{$this->column});
}
return (string) $row->{$this->column} === $expression;
}
$parts = array();
foreach (preg_split('~\*~', $expression) as $part) {
$parts[] = preg_quote($part);
}
$pattern = '/^' . implode('.*', $parts) . '$/';
if (is_array($row->{$this->column})) {
foreach ($row->{$this->column} as $candidate) {
if (preg_match($pattern, $candidate)) {
return true;
}
}
return false;
}
return (bool) preg_match($pattern, $row->{$this->column});
}
public function andFilter(Filter $filter)

View File

@ -5,21 +5,4 @@ namespace Icinga\Data\Filter;
class FilterMatch extends FilterExpression
{
public function matches($row)
{
if (! isset($row->{$this->column})) {
// TODO: REALLY? Exception?
return false;
}
$expression = (string) $this->expression;
if (strpos($expression, '*') === false) {
return (string) $row->{$this->column} === $expression;
} else {
$parts = array();
foreach (preg_split('/\*/', $expression) as $part) {
$parts[] = preg_quote($part);
}
return preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
}
}
}

View File

@ -7,15 +7,6 @@ class FilterMatchNot extends FilterExpression
{
public function matches($row)
{
$expression = (string) $this->expression;
if (strpos($expression, '*') === false) {
return (string) $row->{$this->column} !== $expression;
} else {
$parts = array();
foreach (preg_split('/\*/', $expression) as $part) {
$parts[] = preg_quote($part);
}
return ! preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
}
return !parent::matches($row);
}
}

View File

@ -0,0 +1,21 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Data;
interface FilterColumns
{
/**
* Return a filterable's filter columns with their optional label as key
*
* @return array
*/
public function getFilterColumns();
/**
* Return a filterable's search columns
*
* @return array
*/
public function getSearchColumns();
}

View File

@ -4,12 +4,11 @@
namespace Icinga\Data;
use Icinga\Data\Filter\Filter;
use Icinga\Data\SimpleQuery;
use Icinga\Application\Icinga;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
use Zend_Paginator;
class PivotTable
class PivotTable implements Sortable
{
/**
* The query to fetch as pivot table
@ -19,53 +18,74 @@ class PivotTable
protected $baseQuery;
/**
* The query to fetch the x axis labels
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the y axis labels
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* The column that contains the labels for the x axis
* X-axis pivot column
*
* @var string
*/
protected $xAxisColumn;
/**
* The column that contains the labels for the y axis
* Y-axis pivot column
*
* @var string
*/
protected $yAxisColumn;
/**
* The filter being applied on the query for the x axis
* Column for sorting the result set
*
* @var array
*/
protected $order = array();
/**
* The filter being applied on the query for the x-axis
*
* @var Filter
*/
protected $xAxisFilter;
/**
* The filter being applied on the query for the y axis
* The filter being applied on the query for the y-axis
*
* @var Filter
*/
protected $yAxisFilter;
/**
* The query to fetch the leading x-axis rows and their headers
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the leading y-axis rows and their headers
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* X-axis header column
*
* @var string|null
*/
protected $xAxisHeader;
/**
* Y-axis header column
*
* @var string|null
*/
protected $yAxisHeader;
/**
* Create a new pivot table
*
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn The column that contains the labels for the x axis
* @param string $yAxisColumn The column that contains the labels for the y axis
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn X-axis pivot column
* @param string $yAxisColumn Y-axis pivot column
*/
public function __construct(SimpleQuery $query, $xAxisColumn, $yAxisColumn)
{
@ -75,7 +95,32 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the x axis
* {@inheritdoc}
*/
public function getOrder()
{
return $this->order;
}
/**
* {@inheritdoc}
*/
public function hasOrder()
{
return ! empty($this->order);
}
/**
* {@inheritdoc}
*/
public function order($field, $direction = null)
{
$this->order[$field] = $direction;
return $this;
}
/**
* Set the filter to apply on the query for the x-axis
*
* @param Filter $filter
*
@ -88,7 +133,7 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the y axis
* Set the filter to apply on the query for the y-axis
*
* @param Filter $filter
*
@ -100,6 +145,56 @@ class PivotTable
return $this;
}
/**
* Get the x-axis header
*
* Defaults to {@link $xAxisColumn} in case no x-axis header has been set using {@link setXAxisHeader()}
*
* @return string
*/
public function getXAxisHeader()
{
return $this->xAxisHeader !== null ? $this->xAxisHeader : $this->xAxisColumn;
}
/**
* Set the x-axis header
*
* @param string $xAxisHeader
*
* @return $this
*/
public function setXAxisHeader($xAxisHeader)
{
$this->xAxisHeader = (string) $xAxisHeader;
return $this;
}
/**
* Get the y-axis header
*
* Defaults to {@link $yAxisColumn} in case no x-axis header has been set using {@link setYAxisHeader()}
*
* @return string
*/
public function getYAxisHeader()
{
return $this->yAxisHeader !== null ? $this->yAxisHeader : $this->yAxisColumn;
}
/**
* Set the y-axis header
*
* @param string $yAxisHeader
*
* @return $this
*/
public function setYAxisHeader($yAxisHeader)
{
$this->yAxisHeader = (string) $yAxisHeader;
return $this;
}
/**
* Return the value for the given request parameter
*
@ -107,7 +202,7 @@ class PivotTable
* @param string $param The parameter name to return
* @param int $default The default value to return
*
* @return int
* @return int
*/
protected function getPaginationParameter($axis, $param, $default = null)
{
@ -125,23 +220,25 @@ class PivotTable
/**
* Query horizontal (x) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryXAxis()
{
if ($this->xAxisQuery === null) {
$this->xAxisQuery = clone $this->baseQuery;
$this->xAxisQuery->group($this->xAxisColumn);
$this->xAxisQuery->columns(array($this->xAxisColumn));
$this->xAxisQuery->setUseSubqueryCount();
$xAxisHeader = $this->getXAxisHeader();
$columns = array($this->xAxisColumn, $xAxisHeader);
$this->xAxisQuery->group(array_unique($columns)); // xAxisColumn and header may be the same column
$this->xAxisQuery->columns($columns);
if ($this->xAxisFilter !== null) {
$this->xAxisQuery->addFilter($this->xAxisFilter);
}
if (! $this->xAxisQuery->hasOrder($this->xAxisColumn)) {
$this->xAxisQuery->order($this->xAxisColumn, 'asc');
}
$this->xAxisQuery->order(
$xAxisHeader,
isset($this->order[$xAxisHeader]) ? $this->order[$xAxisHeader] : self::SORT_ASC
);
}
return $this->xAxisQuery;
@ -150,30 +247,31 @@ class PivotTable
/**
* Query vertical (y) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryYAxis()
{
if ($this->yAxisQuery === null) {
$this->yAxisQuery = clone $this->baseQuery;
$this->yAxisQuery->group($this->yAxisColumn);
$this->yAxisQuery->columns(array($this->yAxisColumn));
$this->yAxisQuery->setUseSubqueryCount();
$yAxisHeader = $this->getYAxisHeader();
$columns = array($this->yAxisColumn, $yAxisHeader);
$this->yAxisQuery->group(array_unique($columns)); // yAxisColumn and header may be the same column
$this->yAxisQuery->columns($columns);
if ($this->yAxisFilter !== null) {
$this->yAxisQuery->addFilter($this->yAxisFilter);
}
if (! $this->yAxisQuery->hasOrder($this->yAxisColumn)) {
$this->yAxisQuery->order($this->yAxisColumn, 'asc');
}
$this->yAxisQuery->order(
$yAxisHeader,
isset($this->order[$yAxisHeader]) ? $this->order[$yAxisHeader] : self::SORT_ASC
);
}
return $this->yAxisQuery;
}
/**
* Return a pagination adapter for the x axis query
* Return a pagination adapter for the x-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -204,7 +302,7 @@ class PivotTable
}
/**
* Return a pagination adapter for the y axis query
* Return a pagination adapter for the y-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -235,9 +333,9 @@ class PivotTable
}
/**
* Return the pivot table as array
* Return the pivot table as an array of pivot data and pivot header
*
* @return array
* @return array
*/
public function toArray()
{
@ -245,33 +343,39 @@ class PivotTable
($this->xAxisFilter === null && $this->yAxisFilter === null)
|| ($this->xAxisFilter !== null && $this->yAxisFilter !== null)
) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->fetchPairs();
} else {
if ($this->xAxisFilter !== null) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchPairs();
} else { // $this->yAxisFilter !== null
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchColumn();
$yAxis = $this->queryYAxis()->fetchPairs();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchPairs();
}
}
$pivotData = array();
$pivotHeader = array(
'cols' => $xAxis,
'rows' => $yAxis
);
if (! empty($xAxis) && ! empty($yAxis)) {
$xAxisKeys = array_keys($xAxis);
$yAxisKeys = array_keys($yAxis);
$this->baseQuery
->where($this->xAxisColumn, $xAxisKeys)
->where($this->yAxisColumn, $yAxisKeys);
$pivot = array();
if (!empty($xAxis) && !empty($yAxis)) {
$this->baseQuery->where($this->xAxisColumn, $xAxis)->where($this->yAxisColumn, $yAxis);
foreach ($yAxis as $yLabel) {
foreach ($xAxis as $xLabel) {
$pivot[$yLabel][$xLabel] = null;
foreach ($yAxisKeys as $yAxisKey) {
foreach ($xAxisKeys as $xAxisKey) {
$pivotData[$yAxisKey][$xAxisKey] = null;
}
}
foreach ($this->baseQuery as $row) {
$pivot[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
$pivotData[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
}
}
return $pivot;
return array($pivotData, $pivotHeader);
}
}

View File

@ -143,6 +143,16 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator
return $this->ds;
}
/**
* Return the current position of this query's iterator
*
* @return int
*/
public function getIteratorPosition()
{
return $this->iteratorPosition;
}
/**
* Start or rewind the iteration
*/

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception;
/**
* Exception thrown if something to add already exists
*/
class AlreadyExistsException extends IcingaException
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace Icinga\Exception\Http;
/**
* Exception thrown for sending a HTTP 400 response w/ a custom message
*/
class HttpBadRequestException extends HttpException
{
}

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