mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-27 15:54:03 +02:00
Merge branch 'master' into feature/add-ssl-support-to-mysql-database-resources-11115
This commit is contained in:
commit
dce6b4eb08
@ -6,6 +6,16 @@
|
|||||||
/* The directory which contains the plugins from the Monitoring Plugins project. */
|
/* The directory which contains the plugins from the Monitoring Plugins project. */
|
||||||
const PluginDir = "/usr/lib64/nagios/plugins"
|
const PluginDir = "/usr/lib64/nagios/plugins"
|
||||||
|
|
||||||
|
/* The directory which contains the Manubulon plugins.
|
||||||
|
* Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details.
|
||||||
|
*/
|
||||||
|
const ManubulonPluginDir = "/usr/lib64/nagios/plugins"
|
||||||
|
|
||||||
|
/* The directory which you use to store additional plugins which ITL provides user contributed command definitions for.
|
||||||
|
* Check the documentation, chapter "Plugins Contribution", for details.
|
||||||
|
*/
|
||||||
|
const PluginContribDir = "/usr/lib64/nagios/plugins"
|
||||||
|
|
||||||
/* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`.
|
/* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`.
|
||||||
* This should be the common name from the API certificate.
|
* This should be the common name from the API certificate.
|
||||||
*/
|
*/
|
||||||
@ -13,3 +23,4 @@ const NodeName = "localhost"
|
|||||||
|
|
||||||
/* Our local zone name. */
|
/* Our local zone name. */
|
||||||
const ZoneName = NodeName
|
const ZoneName = NodeName
|
||||||
|
const TicketSalt = ""
|
||||||
|
22
ChangeLog
22
ChangeLog
@ -1,5 +1,25 @@
|
|||||||
# Icinga Web 2 Changelog
|
# Icinga Web 2 Changelog
|
||||||
|
|
||||||
|
## What's New
|
||||||
|
|
||||||
|
### What's New in Version 2.3.4/2.3.3
|
||||||
|
|
||||||
|
#### Bugfixes
|
||||||
|
|
||||||
|
* Bug 11267: Links in plugin output don't behave as expected
|
||||||
|
* Bug 11348: Host aliases are not shown in detail area
|
||||||
|
* Bug 11728: First non whitespace character after comma stripped from plugin output
|
||||||
|
* Bug 11729: Sort by severity depends on state type
|
||||||
|
* Bug 11737: Zero width space characters destroy state highlighting in plugin output
|
||||||
|
* Bug 11796: Zero width space characters may destroy links in plugin output
|
||||||
|
* Bug 11831: module.info parsing fails in case it contains newlines that are not part of the module's description
|
||||||
|
* Bug 11850: "Add to menu" tab unnecessarily appears in command forms
|
||||||
|
* Bug 11871: Colors used in the timeline are not accessible
|
||||||
|
* Bug 11883: Delete action on comments and downtimes in list views not accessible because they lack context
|
||||||
|
* Bug 11885: Database: Asterisk filters ignored when combined w/ other filters
|
||||||
|
* Bug 11910: Web 2 lacks mobile meta tags
|
||||||
|
* Fix remote code execution via remote command transport
|
||||||
|
|
||||||
### What's New in Version 2.3.2
|
### What's New in Version 2.3.2
|
||||||
|
|
||||||
#### Feature
|
#### Feature
|
||||||
@ -14,8 +34,6 @@
|
|||||||
* Bug 10848: Can't change items per page if filter is in modify state
|
* Bug 10848: Can't change items per page if filter is in modify state
|
||||||
* Bug 11392: Can't configure monitoring backend via the web interface when no monitoring backend was configured
|
* Bug 11392: Can't configure monitoring backend via the web interface when no monitoring backend was configured
|
||||||
|
|
||||||
## What's New
|
|
||||||
|
|
||||||
### What's New in Version 2.3.1
|
### What's New in Version 2.3.1
|
||||||
|
|
||||||
#### Bugfixes
|
#### Bugfixes
|
||||||
|
73
application/controllers/AccountController.php
Normal file
73
application/controllers/AccountController.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Controllers;
|
||||||
|
|
||||||
|
use Icinga\Application\Config;
|
||||||
|
use Icinga\Authentication\User\UserBackend;
|
||||||
|
use Icinga\Data\ConfigObject;
|
||||||
|
use Icinga\Exception\ConfigurationError;
|
||||||
|
use Icinga\Forms\Account\ChangePasswordForm;
|
||||||
|
use Icinga\Forms\PreferenceForm;
|
||||||
|
use Icinga\User\Preferences\PreferencesStore;
|
||||||
|
use Icinga\Web\Controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* My Account
|
||||||
|
*/
|
||||||
|
class AccountController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->getTabs()
|
||||||
|
->add('account', array(
|
||||||
|
'title' => $this->translate('Update your account'),
|
||||||
|
'label' => $this->translate('My Account'),
|
||||||
|
'url' => 'account'
|
||||||
|
))
|
||||||
|
->add('navigation', array(
|
||||||
|
'title' => $this->translate('List and configure your own navigation items'),
|
||||||
|
'label' => $this->translate('Navigation'),
|
||||||
|
'url' => 'navigation'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* My account
|
||||||
|
*/
|
||||||
|
public function indexAction()
|
||||||
|
{
|
||||||
|
$config = Config::app()->getSection('global');
|
||||||
|
$user = $this->Auth()->getUser();
|
||||||
|
if ($user->getAdditional('backend_type') === 'db') {
|
||||||
|
try {
|
||||||
|
$userBackend = UserBackend::create($user->getAdditional('backend_name'));
|
||||||
|
} catch (ConfigurationError $e) {
|
||||||
|
$userBackend = null;
|
||||||
|
}
|
||||||
|
if ($userBackend !== null) {
|
||||||
|
$changePasswordForm = new ChangePasswordForm();
|
||||||
|
$changePasswordForm
|
||||||
|
->setBackend($userBackend)
|
||||||
|
->handleRequest();
|
||||||
|
$this->view->changePasswordForm = $changePasswordForm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new PreferenceForm();
|
||||||
|
$form->setPreferences($user->getPreferences());
|
||||||
|
if ($config->get('config_backend', 'ini') !== 'none') {
|
||||||
|
$form->setStore(PreferencesStore::create(new ConfigObject(array(
|
||||||
|
'store' => $config->get('config_backend', 'ini'),
|
||||||
|
'resource' => $config->config_resource
|
||||||
|
)), $user));
|
||||||
|
}
|
||||||
|
$form->handleRequest();
|
||||||
|
|
||||||
|
$this->view->form = $form;
|
||||||
|
$this->getTabs()->activate('account');
|
||||||
|
}
|
||||||
|
}
|
100
application/controllers/AnnouncementsController.php
Normal file
100
application/controllers/AnnouncementsController.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Controllers;
|
||||||
|
|
||||||
|
use Icinga\Exception\NotFoundError;
|
||||||
|
use Icinga\Forms\Announcement\AcknowledgeAnnouncementForm;
|
||||||
|
use Icinga\Forms\Announcement\AnnouncementForm;
|
||||||
|
use Icinga\Web\Announcement\AnnouncementIniRepository;
|
||||||
|
use Icinga\Web\Controller;
|
||||||
|
use Icinga\Web\Url;
|
||||||
|
|
||||||
|
class AnnouncementsController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* List all announcements
|
||||||
|
*/
|
||||||
|
public function indexAction()
|
||||||
|
{
|
||||||
|
$this->getTabs()->add(
|
||||||
|
'announcements',
|
||||||
|
array(
|
||||||
|
'active' => true,
|
||||||
|
'label' => $this->translate('Announcements'),
|
||||||
|
'title' => $this->translate('List All Announcements'),
|
||||||
|
'url' => Url::fromPath('announcements')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$repo = new AnnouncementIniRepository();
|
||||||
|
$this->view->announcements = $repo
|
||||||
|
->select(array('id', 'author', 'message', 'start', 'end'))
|
||||||
|
->order('start');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an announcement
|
||||||
|
*/
|
||||||
|
public function newAction()
|
||||||
|
{
|
||||||
|
$this->assertPermission('admin');
|
||||||
|
|
||||||
|
$form = $this->prepareForm()->add();
|
||||||
|
$form->handleRequest();
|
||||||
|
$this->renderForm($form, $this->translate('New Announcement'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an announcement
|
||||||
|
*/
|
||||||
|
public function updateAction()
|
||||||
|
{
|
||||||
|
$this->assertPermission('admin');
|
||||||
|
|
||||||
|
$form = $this->prepareForm()->edit($this->params->getRequired('id'));
|
||||||
|
try {
|
||||||
|
$form->handleRequest();
|
||||||
|
} catch (NotFoundError $_) {
|
||||||
|
$this->httpNotFound($this->translate('Announcement not found'));
|
||||||
|
}
|
||||||
|
$this->renderForm($form, $this->translate('Update Announcement'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an announcement
|
||||||
|
*/
|
||||||
|
public function removeAction()
|
||||||
|
{
|
||||||
|
$this->assertPermission('admin');
|
||||||
|
|
||||||
|
$form = $this->prepareForm()->remove($this->params->getRequired('id'));
|
||||||
|
try {
|
||||||
|
$form->handleRequest();
|
||||||
|
} catch (NotFoundError $_) {
|
||||||
|
$this->httpNotFound($this->translate('Announcement not found'));
|
||||||
|
}
|
||||||
|
$this->renderForm($form, $this->translate('Remove Announcement'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acknowledgeAction()
|
||||||
|
{
|
||||||
|
$this->assertHttpMethod('POST');
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true);
|
||||||
|
$form = new AcknowledgeAnnouncementForm();
|
||||||
|
$form->handleRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert permission admin and return a prepared RepositoryForm
|
||||||
|
*
|
||||||
|
* @return AnnouncementForm
|
||||||
|
*/
|
||||||
|
protected function prepareForm()
|
||||||
|
{
|
||||||
|
$form = new AnnouncementForm();
|
||||||
|
return $form
|
||||||
|
->setRepository(new AnnouncementIniRepository())
|
||||||
|
->setRedirectUrl(Url::fromPath('announcements'));
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,8 @@
|
|||||||
namespace Icinga\Controllers;
|
namespace Icinga\Controllers;
|
||||||
|
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
|
use Icinga\Web\Announcement\AnnouncementCookie;
|
||||||
|
use Icinga\Web\Announcement\AnnouncementIniRepository;
|
||||||
use Icinga\Web\Controller;
|
use Icinga\Web\Controller;
|
||||||
use Icinga\Web\Session;
|
use Icinga\Web\Session;
|
||||||
|
|
||||||
@ -14,6 +16,7 @@ class ApplicationStateController extends Controller
|
|||||||
{
|
{
|
||||||
public function indexAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
|
$this->_helper->layout()->disableLayout();
|
||||||
if (isset($_COOKIE['icingaweb2-session'])) {
|
if (isset($_COOKIE['icingaweb2-session'])) {
|
||||||
$last = (int) $_COOKIE['icingaweb2-session'];
|
$last = (int) $_COOKIE['icingaweb2-session'];
|
||||||
} else {
|
} else {
|
||||||
@ -34,6 +37,23 @@ class ApplicationStateController extends Controller
|
|||||||
);
|
);
|
||||||
$_COOKIE['icingaweb2-session'] = $now;
|
$_COOKIE['icingaweb2-session'] = $now;
|
||||||
}
|
}
|
||||||
Icinga::app()->getResponse()->setHeader('X-Icinga-Container', 'ignore', true);
|
$announcementCookie = new AnnouncementCookie();
|
||||||
|
$announcementRepo = new AnnouncementIniRepository();
|
||||||
|
if ($announcementCookie->getEtag() !== $announcementRepo->getEtag()) {
|
||||||
|
$announcementCookie
|
||||||
|
->setEtag($announcementRepo->getEtag())
|
||||||
|
->setNextActive($announcementRepo->findNextActive());
|
||||||
|
$this->getResponse()->setCookie($announcementCookie);
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true);
|
||||||
|
} else {
|
||||||
|
$nextActive = $announcementCookie->getNextActive();
|
||||||
|
if ($nextActive && $nextActive <= $now) {
|
||||||
|
$announcementCookie->setNextActive($announcementRepo->findNextActive());
|
||||||
|
$this->getResponse()->setCookie($announcementCookie);
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
namespace Icinga\Controllers;
|
namespace Icinga\Controllers;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Icinga\Application\Version;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Icinga\Application\Config;
|
use Icinga\Application\Config;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
@ -122,6 +123,7 @@ class ConfigController extends Controller
|
|||||||
|
|
||||||
$this->view->module = $module;
|
$this->view->module = $module;
|
||||||
$this->view->tabs = $module->getConfigTabs()->activate('info');
|
$this->view->tabs = $module->getConfigTabs()->activate('info');
|
||||||
|
$this->view->moduleGitCommitId = Version::getGitHead($module->getBaseDir());
|
||||||
} else {
|
} else {
|
||||||
$this->view->module = false;
|
$this->view->module = false;
|
||||||
$this->view->tabs = null;
|
$this->view->tabs = null;
|
||||||
@ -213,7 +215,7 @@ class ConfigController extends Controller
|
|||||||
|
|
||||||
$form->setOnSuccess(function (UserBackendConfigForm $form) {
|
$form->setOnSuccess(function (UserBackendConfigForm $form) {
|
||||||
try {
|
try {
|
||||||
$form->add(array_filter($form->getValues()));
|
$form->add($form::transformEmptyValuesToNull($form->getValues()));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$form->error($e->getMessage());
|
$form->error($e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
@ -244,12 +246,7 @@ class ConfigController extends Controller
|
|||||||
$form->setIniConfig(Config::app('authentication'));
|
$form->setIniConfig(Config::app('authentication'));
|
||||||
$form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) {
|
$form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) {
|
||||||
try {
|
try {
|
||||||
$form->edit($backendName, array_map(
|
$form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues()));
|
||||||
function ($v) {
|
|
||||||
return $v !== '' ? $v : null;
|
|
||||||
},
|
|
||||||
$form->getValues()
|
|
||||||
));
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$form->error($e->getMessage());
|
$form->error($e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
@ -393,10 +390,10 @@ class ConfigController extends Controller
|
|||||||
$authConfig = Config::app('authentication');
|
$authConfig = Config::app('authentication');
|
||||||
foreach ($authConfig as $backendName => $config) {
|
foreach ($authConfig as $backendName => $config) {
|
||||||
if ($config->get('resource') === $resource) {
|
if ($config->get('resource') === $resource) {
|
||||||
$form->addDescription(sprintf(
|
$form->warning(sprintf(
|
||||||
$this->translate(
|
$this->translate(
|
||||||
'The resource "%s" is currently utilized for authentication by user backend "%s". ' .
|
'The resource "%s" is currently utilized for authentication by user backend "%s".'
|
||||||
'Removing the resource can result in noone being able to log in any longer.'
|
. ' Removing the resource can result in noone being able to log in any longer.'
|
||||||
),
|
),
|
||||||
$resource,
|
$resource,
|
||||||
$backendName
|
$backendName
|
||||||
@ -404,6 +401,17 @@ class ConfigController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if selected resource is currently used as user preferences backend
|
||||||
|
if (Config::app()->get('global', 'config_resource') === $resource) {
|
||||||
|
$form->warning(sprintf(
|
||||||
|
$this->translate(
|
||||||
|
'The resource "%s" is currently utilized to store user preferences. Removing the'
|
||||||
|
. ' resource causes all current user preferences not being available any longer.'
|
||||||
|
),
|
||||||
|
$resource
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$this->view->form = $form;
|
$this->view->form = $form;
|
||||||
$this->render('resource/remove');
|
$this->render('resource/remove');
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,6 @@ class ErrorController extends ActionController
|
|||||||
$error = $this->_getParam('error_handler');
|
$error = $this->_getParam('error_handler');
|
||||||
$exception = $error->exception;
|
$exception = $error->exception;
|
||||||
/** @var \Exception $exception */
|
/** @var \Exception $exception */
|
||||||
Logger::error($exception);
|
|
||||||
Logger::error('Stacktrace: %s', $exception->getTraceAsString());
|
|
||||||
|
|
||||||
if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) {
|
if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) {
|
||||||
$this->innerLayout = 'guest-error';
|
$this->innerLayout = 'guest-error';
|
||||||
@ -83,6 +81,7 @@ class ErrorController extends ActionController
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->getResponse()->setHttpResponseCode(500);
|
$this->getResponse()->setHttpResponseCode(500);
|
||||||
|
Logger::error("%s\n%s", $exception, $exception->getTraceAsString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$this->view->message = $exception->getMessage();
|
$this->view->message = $exception->getMessage();
|
||||||
|
@ -20,4 +20,9 @@ class LayoutController extends ActionController
|
|||||||
$this->_helper->layout()->disableLayout();
|
$this->_helper->layout()->disableLayout();
|
||||||
$this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer();
|
$this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function announcementsAction()
|
||||||
|
{
|
||||||
|
$this->_helper->layout()->disableLayout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,11 +128,11 @@ class NavigationController extends Controller
|
|||||||
|
|
||||||
$this->getTabs()
|
$this->getTabs()
|
||||||
->add(
|
->add(
|
||||||
'preferences',
|
'account',
|
||||||
array(
|
array(
|
||||||
'title' => $this->translate('Adjust the preferences of Icinga Web 2 according to your needs'),
|
'title' => $this->translate('Update your account'),
|
||||||
'label' => $this->translate('Preferences'),
|
'label' => $this->translate('My Account'),
|
||||||
'url' => 'preference'
|
'url' => 'account'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
->add(
|
->add(
|
||||||
@ -219,7 +219,7 @@ class NavigationController extends Controller
|
|||||||
$form->setDefaultUrl(rawurldecode($this->params->get('url', '')));
|
$form->setDefaultUrl(rawurldecode($this->params->get('url', '')));
|
||||||
|
|
||||||
$form->setOnSuccess(function (NavigationConfigForm $form) {
|
$form->setOnSuccess(function (NavigationConfigForm $form) {
|
||||||
$data = array_filter($form->getValues());
|
$data = $form::transformEmptyValuesToNull($form->getValues());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$form->add($data);
|
$form->add($data);
|
||||||
@ -266,12 +266,7 @@ class NavigationController extends Controller
|
|||||||
$form->setUserConfig(Config::navigation($itemType, $itemOwner));
|
$form->setUserConfig(Config::navigation($itemType, $itemOwner));
|
||||||
$form->setRedirectUrl($referrer === 'shared' ? 'navigation/shared' : 'navigation');
|
$form->setRedirectUrl($referrer === 'shared' ? 'navigation/shared' : 'navigation');
|
||||||
$form->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) {
|
$form->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) {
|
||||||
$data = array_map(
|
$data = $form::transformEmptyValuesToNull($form->getValues());
|
||||||
function ($v) {
|
|
||||||
return $v !== '' ? $v : null;
|
|
||||||
},
|
|
||||||
$form->getValues()
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$form->edit($itemName, $data);
|
$form->edit($itemName, $data);
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application wide preference controller for user preferences
|
|
||||||
*
|
|
||||||
* @TODO(el): Rename to PreferencesController: https://dev.icinga.org/issues/10014
|
|
||||||
*/
|
|
||||||
class PreferenceController extends BasePreferenceController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create tabs for this preference controller
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @see BasePreferenceController::createProvidedTabs()
|
|
||||||
*/
|
|
||||||
public static function createProvidedTabs()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
'preferences' => new Tab(array(
|
|
||||||
'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'),
|
|
||||||
'label' => t('Preferences'),
|
|
||||||
'url' => 'preference'
|
|
||||||
)),
|
|
||||||
'navigation' => new Tab(array(
|
|
||||||
'title' => t('List and configure your own navigation items'),
|
|
||||||
'label' => t('Navigation'),
|
|
||||||
'url' => 'navigation'
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show form to adjust user preferences
|
|
||||||
*/
|
|
||||||
public function indexAction()
|
|
||||||
{
|
|
||||||
$config = Config::app()->getSection('global');
|
|
||||||
$user = $this->getRequest()->getUser();
|
|
||||||
|
|
||||||
$form = new PreferenceForm();
|
|
||||||
$form->setPreferences($user->getPreferences());
|
|
||||||
if ($config->get('config_backend', 'ini') !== 'none') {
|
|
||||||
$form->setStore(PreferencesStore::create(new ConfigObject(array(
|
|
||||||
'store' => $config->get('config_backend', 'ini'),
|
|
||||||
'resource' => $config->config_resource
|
|
||||||
)), $user));
|
|
||||||
}
|
|
||||||
$form->handleRequest();
|
|
||||||
|
|
||||||
$this->view->form = $form;
|
|
||||||
$this->getTabs()->activate('preferences');
|
|
||||||
}
|
|
||||||
}
|
|
@ -43,7 +43,7 @@ class UsergroupbackendController extends Controller
|
|||||||
$form->setIniConfig(Config::app('groups'));
|
$form->setIniConfig(Config::app('groups'));
|
||||||
$form->setOnSuccess(function (UserGroupBackendForm $form) {
|
$form->setOnSuccess(function (UserGroupBackendForm $form) {
|
||||||
try {
|
try {
|
||||||
$form->add(array_filter($form->getValues()));
|
$form->add($form::transformEmptyValuesToNull($form->getValues()));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$form->error($e->getMessage());
|
$form->error($e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
@ -73,12 +73,7 @@ class UsergroupbackendController extends Controller
|
|||||||
$form->setIniConfig(Config::app('groups'));
|
$form->setIniConfig(Config::app('groups'));
|
||||||
$form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) {
|
$form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) {
|
||||||
try {
|
try {
|
||||||
$form->edit($backendName, array_map(
|
$form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues()));
|
||||||
function ($v) {
|
|
||||||
return $v !== '' ? $v : null;
|
|
||||||
},
|
|
||||||
$form->getValues()
|
|
||||||
));
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$form->error($e->getMessage());
|
$form->error($e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
|
11
application/fonts/fontello-ifont/LICENSE.txt
Normal file → Executable file
11
application/fonts/fontello-ifont/LICENSE.txt
Normal file → Executable file
@ -3,7 +3,7 @@ Font license info
|
|||||||
|
|
||||||
## Font Awesome
|
## Font Awesome
|
||||||
|
|
||||||
Copyright (C) 2012 by Dave Gandy
|
Copyright (C) 2016 by Dave Gandy
|
||||||
|
|
||||||
Author: Dave Gandy
|
Author: Dave Gandy
|
||||||
License: SIL ()
|
License: SIL ()
|
||||||
@ -19,15 +19,6 @@ Font license info
|
|||||||
Homepage: http://somerandomdude.com/work/iconic/
|
Homepage: http://somerandomdude.com/work/iconic/
|
||||||
|
|
||||||
|
|
||||||
## MFG Labs
|
|
||||||
|
|
||||||
Copyright (C) 2012 by Daniel Bruce
|
|
||||||
|
|
||||||
Author: MFG Labs
|
|
||||||
License: SIL (http://scripts.sil.org/OFL)
|
|
||||||
Homepage: http://www.mfglabs.com/
|
|
||||||
|
|
||||||
|
|
||||||
## Entypo
|
## Entypo
|
||||||
|
|
||||||
Copyright (C) 2012 by Daniel Bruce
|
Copyright (C) 2012 by Daniel Bruce
|
||||||
|
4
application/fonts/fontello-ifont/README.txt
Normal file → Executable file
4
application/fonts/fontello-ifont/README.txt
Normal file → Executable file
@ -2,14 +2,14 @@ This webfont is generated by http://fontello.com open source project.
|
|||||||
|
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Please, note, that you should obey original font licences, used to make this
|
Please, note, that you should obey original font licenses, used to make this
|
||||||
webfont pack. Details available in LICENSE.txt file.
|
webfont pack. Details available in LICENSE.txt file.
|
||||||
|
|
||||||
- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
|
- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
|
||||||
site in "About" section.
|
site in "About" section.
|
||||||
|
|
||||||
- If your project is open-source, usually, it will be ok to make LICENSE.txt
|
- If your project is open-source, usually, it will be ok to make LICENSE.txt
|
||||||
file publically available in your repository.
|
file publicly available in your repository.
|
||||||
|
|
||||||
- Fonts, used in Fontello, don't require a clickable link on your site.
|
- Fonts, used in Fontello, don't require a clickable link on your site.
|
||||||
But any kind of additional authors crediting is welcome.
|
But any kind of additional authors crediting is welcome.
|
||||||
|
54
application/fonts/fontello-ifont/config.json
Normal file → Executable file
54
application/fonts/fontello-ifont/config.json
Normal file → Executable file
@ -711,7 +711,7 @@
|
|||||||
{
|
{
|
||||||
"uid": "3bd18d47a12b8709e9f4fe9ead4f7518",
|
"uid": "3bd18d47a12b8709e9f4fe9ead4f7518",
|
||||||
"css": "reschedule",
|
"css": "reschedule",
|
||||||
"code": 59492,
|
"code": 59524,
|
||||||
"src": "entypo"
|
"src": "entypo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -744,12 +744,6 @@
|
|||||||
"code": 59512,
|
"code": 59512,
|
||||||
"src": "typicons"
|
"src": "typicons"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"uid": "b90d80c250a9bbdd6cd3fe00e6351710",
|
|
||||||
"css": "ok",
|
|
||||||
"code": 59395,
|
|
||||||
"src": "iconic"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"uid": "11e664deed5b2587456a4f9c01d720b6",
|
"uid": "11e664deed5b2587456a4f9c01d720b6",
|
||||||
"css": "cancel",
|
"css": "cancel",
|
||||||
@ -775,10 +769,52 @@
|
|||||||
"src": "iconic"
|
"src": "iconic"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"uid": "9c3b8d8a6d477da4d3e65b92e4e9c290",
|
"uid": "8f28d948aa6379b1a69d2a090e7531d4",
|
||||||
|
"css": "warning-empty",
|
||||||
|
"code": 59525,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "d4816c0845aa43767213d45574b3b145",
|
||||||
|
"css": "history",
|
||||||
|
"code": 61914,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "b035c28eba2b35c6ffe92aee8b0df507",
|
||||||
|
"css": "attention-circled",
|
||||||
|
"code": 59521,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "73ffeb70554099177620847206c12457",
|
||||||
|
"css": "binoculars",
|
||||||
|
"code": 61925,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "a73c5deb486c8d66249811642e5d719a",
|
||||||
|
"css": "arrows-cw",
|
||||||
|
"code": 59492,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "dd6c6b221a1088ff8a9b9cd32d0b3dd5",
|
||||||
|
"css": "check",
|
||||||
|
"code": 59523,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "b90d80c250a9bbdd6cd3fe00e6351710",
|
||||||
|
"css": "ok",
|
||||||
|
"code": 59395,
|
||||||
|
"src": "iconic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "37c5ab63f10d7ad0b84d0978dcd0c7a8",
|
||||||
"css": "flapping",
|
"css": "flapping",
|
||||||
"code": 59485,
|
"code": 59485,
|
||||||
"src": "mfglabs"
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
0
application/fonts/fontello-ifont/css/animation.css
vendored
Normal file → Executable file
0
application/fonts/fontello-ifont/css/animation.css
vendored
Normal file → Executable file
8
application/fonts/fontello-ifont/css/ifont-codes.css
vendored
Normal file → Executable file
8
application/fonts/fontello-ifont/css/ifont-codes.css
vendored
Normal file → Executable file
@ -99,7 +99,7 @@
|
|||||||
.icon-bell-off-empty:before { content: '\e861'; } /* '' */
|
.icon-bell-off-empty:before { content: '\e861'; } /* '' */
|
||||||
.icon-plug:before { content: '\e862'; } /* '' */
|
.icon-plug:before { content: '\e862'; } /* '' */
|
||||||
.icon-eye-off:before { content: '\e863'; } /* '' */
|
.icon-eye-off:before { content: '\e863'; } /* '' */
|
||||||
.icon-reschedule:before { content: '\e864'; } /* '' */
|
.icon-arrows-cw:before { content: '\e864'; } /* '' */
|
||||||
.icon-cw:before { content: '\e865'; } /* '' */
|
.icon-cw:before { content: '\e865'; } /* '' */
|
||||||
.icon-host:before { content: '\e866'; } /* '' */
|
.icon-host:before { content: '\e866'; } /* '' */
|
||||||
.icon-thumbs-up:before { content: '\e867'; } /* '' */
|
.icon-thumbs-up:before { content: '\e867'; } /* '' */
|
||||||
@ -128,3 +128,9 @@
|
|||||||
.icon-twitter:before { content: '\e87e'; } /* '' */
|
.icon-twitter:before { content: '\e87e'; } /* '' */
|
||||||
.icon-facebook-squared:before { content: '\e87f'; } /* '' */
|
.icon-facebook-squared:before { content: '\e87f'; } /* '' */
|
||||||
.icon-gplus-squared:before { content: '\e880'; } /* '' */
|
.icon-gplus-squared:before { content: '\e880'; } /* '' */
|
||||||
|
.icon-attention-circled:before { content: '\e881'; } /* '' */
|
||||||
|
.icon-check:before { content: '\e883'; } /* '' */
|
||||||
|
.icon-reschedule:before { content: '\e884'; } /* '' */
|
||||||
|
.icon-warning-empty:before { content: '\e885'; } /* '' */
|
||||||
|
.icon-history:before { content: '\f1da'; } /* '' */
|
||||||
|
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
20
application/fonts/fontello-ifont/css/ifont-embedded.css
vendored
Normal file → Executable file
20
application/fonts/fontello-ifont/css/ifont-embedded.css
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
8
application/fonts/fontello-ifont/css/ifont-ie7-codes.css
vendored
Normal file → Executable file
8
application/fonts/fontello-ifont/css/ifont-ie7-codes.css
vendored
Normal file → Executable file
@ -99,7 +99,7 @@
|
|||||||
.icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-arrows-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
@ -128,3 +128,9 @@
|
|||||||
.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-attention-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
8
application/fonts/fontello-ifont/css/ifont-ie7.css
vendored
Normal file → Executable file
8
application/fonts/fontello-ifont/css/ifont-ie7.css
vendored
Normal file → Executable file
@ -110,7 +110,7 @@
|
|||||||
.icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-bell-off-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-plug { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-eye-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-arrows-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-cw { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-host { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-thumbs-up { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
@ -139,3 +139,9 @@
|
|||||||
.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-attention-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-check { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-reschedule { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-history { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||||
|
.icon-binoculars { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
21
application/fonts/fontello-ifont/css/ifont.css
vendored
Normal file → Executable file
21
application/fonts/fontello-ifont/css/ifont.css
vendored
Normal file → Executable file
@ -1,10 +1,11 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('../font/ifont.eot?65389432');
|
src: url('../font/ifont.eot?38679513');
|
||||||
src: url('../font/ifont.eot?65389432#iefix') format('embedded-opentype'),
|
src: url('../font/ifont.eot?38679513#iefix') format('embedded-opentype'),
|
||||||
url('../font/ifont.woff?65389432') format('woff'),
|
url('../font/ifont.woff2?38679513') format('woff2'),
|
||||||
url('../font/ifont.ttf?65389432') format('truetype'),
|
url('../font/ifont.woff?38679513') format('woff'),
|
||||||
url('../font/ifont.svg?65389432#ifont') format('svg');
|
url('../font/ifont.ttf?38679513') format('truetype'),
|
||||||
|
url('../font/ifont.svg?38679513#ifont') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@ -14,7 +15,7 @@
|
|||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('../font/ifont.svg?65389432#ifont') format('svg');
|
src: url('../font/ifont.svg?38679513#ifont') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@ -154,7 +155,7 @@
|
|||||||
.icon-bell-off-empty:before { content: '\e861'; } /* '' */
|
.icon-bell-off-empty:before { content: '\e861'; } /* '' */
|
||||||
.icon-plug:before { content: '\e862'; } /* '' */
|
.icon-plug:before { content: '\e862'; } /* '' */
|
||||||
.icon-eye-off:before { content: '\e863'; } /* '' */
|
.icon-eye-off:before { content: '\e863'; } /* '' */
|
||||||
.icon-reschedule:before { content: '\e864'; } /* '' */
|
.icon-arrows-cw:before { content: '\e864'; } /* '' */
|
||||||
.icon-cw:before { content: '\e865'; } /* '' */
|
.icon-cw:before { content: '\e865'; } /* '' */
|
||||||
.icon-host:before { content: '\e866'; } /* '' */
|
.icon-host:before { content: '\e866'; } /* '' */
|
||||||
.icon-thumbs-up:before { content: '\e867'; } /* '' */
|
.icon-thumbs-up:before { content: '\e867'; } /* '' */
|
||||||
@ -183,3 +184,9 @@
|
|||||||
.icon-twitter:before { content: '\e87e'; } /* '' */
|
.icon-twitter:before { content: '\e87e'; } /* '' */
|
||||||
.icon-facebook-squared:before { content: '\e87f'; } /* '' */
|
.icon-facebook-squared:before { content: '\e87f'; } /* '' */
|
||||||
.icon-gplus-squared:before { content: '\e880'; } /* '' */
|
.icon-gplus-squared:before { content: '\e880'; } /* '' */
|
||||||
|
.icon-attention-circled:before { content: '\e881'; } /* '' */
|
||||||
|
.icon-check:before { content: '\e883'; } /* '' */
|
||||||
|
.icon-reschedule:before { content: '\e884'; } /* '' */
|
||||||
|
.icon-warning-empty:before { content: '\e885'; } /* '' */
|
||||||
|
.icon-history:before { content: '\f1da'; } /* '' */
|
||||||
|
.icon-binoculars:before { content: '\f1e5'; } /* '' */
|
20
application/fonts/fontello-ifont/demo.html
Normal file → Executable file
20
application/fonts/fontello-ifont/demo.html
Normal file → Executable file
@ -229,11 +229,11 @@ body {
|
|||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'ifont';
|
font-family: 'ifont';
|
||||||
src: url('./font/ifont.eot?43849680');
|
src: url('./font/ifont.eot?54126565');
|
||||||
src: url('./font/ifont.eot?43849680#iefix') format('embedded-opentype'),
|
src: url('./font/ifont.eot?54126565#iefix') format('embedded-opentype'),
|
||||||
url('./font/ifont.woff?43849680') format('woff'),
|
url('./font/ifont.woff?54126565') format('woff'),
|
||||||
url('./font/ifont.ttf?43849680') format('truetype'),
|
url('./font/ifont.ttf?54126565') format('truetype'),
|
||||||
url('./font/ifont.svg?43849680#ifont') format('svg');
|
url('./font/ifont.svg?54126565#ifont') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@ -451,7 +451,7 @@ body {
|
|||||||
<div title="Code: 0xe863" class="the-icons span3"><i class="demo-icon icon-eye-off"></i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe863</span></div>
|
<div title="Code: 0xe863" class="the-icons span3"><i class="demo-icon icon-eye-off"></i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe863</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div title="Code: 0xe864" class="the-icons span3"><i class="demo-icon icon-reschedule"></i> <span class="i-name">icon-reschedule</span><span class="i-code">0xe864</span></div>
|
<div title="Code: 0xe864" class="the-icons span3"><i class="demo-icon icon-arrows-cw"></i> <span class="i-name">icon-arrows-cw</span><span class="i-code">0xe864</span></div>
|
||||||
<div title="Code: 0xe865" class="the-icons span3"><i class="demo-icon icon-cw"></i> <span class="i-name">icon-cw</span><span class="i-code">0xe865</span></div>
|
<div title="Code: 0xe865" class="the-icons span3"><i class="demo-icon icon-cw"></i> <span class="i-name">icon-cw</span><span class="i-code">0xe865</span></div>
|
||||||
<div title="Code: 0xe866" class="the-icons span3"><i class="demo-icon icon-host"></i> <span class="i-name">icon-host</span><span class="i-code">0xe866</span></div>
|
<div title="Code: 0xe866" class="the-icons span3"><i class="demo-icon icon-host"></i> <span class="i-name">icon-host</span><span class="i-code">0xe866</span></div>
|
||||||
<div title="Code: 0xe867" class="the-icons span3"><i class="demo-icon icon-thumbs-up"></i> <span class="i-name">icon-thumbs-up</span><span class="i-code">0xe867</span></div>
|
<div title="Code: 0xe867" class="the-icons span3"><i class="demo-icon icon-thumbs-up"></i> <span class="i-name">icon-thumbs-up</span><span class="i-code">0xe867</span></div>
|
||||||
@ -494,6 +494,14 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div title="Code: 0xe880" class="the-icons span3"><i class="demo-icon icon-gplus-squared"></i> <span class="i-name">icon-gplus-squared</span><span class="i-code">0xe880</span></div>
|
<div title="Code: 0xe880" class="the-icons span3"><i class="demo-icon icon-gplus-squared"></i> <span class="i-name">icon-gplus-squared</span><span class="i-code">0xe880</span></div>
|
||||||
|
<div title="Code: 0xe881" class="the-icons span3"><i class="demo-icon icon-attention-circled"></i> <span class="i-name">icon-attention-circled</span><span class="i-code">0xe881</span></div>
|
||||||
|
<div title="Code: 0xe883" class="the-icons span3"><i class="demo-icon icon-check"></i> <span class="i-name">icon-check</span><span class="i-code">0xe883</span></div>
|
||||||
|
<div title="Code: 0xe884" class="the-icons span3"><i class="demo-icon icon-reschedule"></i> <span class="i-name">icon-reschedule</span><span class="i-code">0xe884</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div title="Code: 0xe885" class="the-icons span3"><i class="demo-icon icon-warning-empty"></i> <span class="i-name">icon-warning-empty</span><span class="i-code">0xe885</span></div>
|
||||||
|
<div title="Code: 0xf1da" class="the-icons span3"><i class="demo-icon icon-history"></i> <span class="i-name">icon-history</span><span class="i-code">0xf1da</span></div>
|
||||||
|
<div title="Code: 0xf1e5" class="the-icons span3"><i class="demo-icon icon-binoculars"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
||||||
|
123
application/forms/Account/ChangePasswordForm.php
Normal file
123
application/forms/Account/ChangePasswordForm.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Forms\Account;
|
||||||
|
|
||||||
|
use Icinga\Authentication\User\DbUserBackend;
|
||||||
|
use Icinga\Data\Filter\Filter;
|
||||||
|
use Icinga\User;
|
||||||
|
use Icinga\Web\Form;
|
||||||
|
use Icinga\Web\Notification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form for changing user passwords
|
||||||
|
*/
|
||||||
|
class ChangePasswordForm extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The user backend
|
||||||
|
*
|
||||||
|
* @var DbUserBackend
|
||||||
|
*/
|
||||||
|
protected $backend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->setSubmitLabel($this->translate('Update Account'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createElements(array $formData)
|
||||||
|
{
|
||||||
|
$this->addElement(
|
||||||
|
'password',
|
||||||
|
'old_password',
|
||||||
|
array(
|
||||||
|
'label' => $this->translate('Old Password'),
|
||||||
|
'required' => true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->addElement(
|
||||||
|
'password',
|
||||||
|
'new_password',
|
||||||
|
array(
|
||||||
|
'label' => $this->translate('New Password'),
|
||||||
|
'required' => true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->addElement(
|
||||||
|
'password',
|
||||||
|
'new_password_confirmation',
|
||||||
|
array(
|
||||||
|
'label' => $this->translate('Confirm New Password'),
|
||||||
|
'required' => true,
|
||||||
|
'validators' => array(
|
||||||
|
array('identical', false, array('new_password'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onSuccess()
|
||||||
|
{
|
||||||
|
$backend = $this->getBackend();
|
||||||
|
$backend->update(
|
||||||
|
$backend->getBaseTable(),
|
||||||
|
array('password' => $this->getElement('new_password')->getValue()),
|
||||||
|
Filter::where('user_name', $this->Auth()->getUser()->getUsername())
|
||||||
|
);
|
||||||
|
Notification::success($this->translate('Account updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isValid($formData)
|
||||||
|
{
|
||||||
|
$valid = parent::isValid($formData);
|
||||||
|
if (! $valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oldPasswordEl = $this->getElement('old_password');
|
||||||
|
|
||||||
|
if (! $this->backend->authenticate($this->Auth()->getUser(), $oldPasswordEl->getValue())) {
|
||||||
|
$oldPasswordEl->addError($this->translate('Old password is invalid'));
|
||||||
|
$this->markAsError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user backend
|
||||||
|
*
|
||||||
|
* @return DbUserBackend
|
||||||
|
*/
|
||||||
|
public function getBackend()
|
||||||
|
{
|
||||||
|
return $this->backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the user backend
|
||||||
|
*
|
||||||
|
* @param DbUserBackend $backend
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setBackend(DbUserBackend $backend)
|
||||||
|
{
|
||||||
|
$this->backend = $backend;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Forms\Announcement;
|
||||||
|
|
||||||
|
use Icinga\Web\Announcement\AnnouncementCookie;
|
||||||
|
use Icinga\Web\Form;
|
||||||
|
|
||||||
|
class AcknowledgeAnnouncementForm extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->setAction('announcements/acknowledge');
|
||||||
|
$this->setAttrib('class', 'form-inline acknowledge-announcement-control');
|
||||||
|
$this->setRedirectUrl('layout/announcements');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function addSubmitButton()
|
||||||
|
{
|
||||||
|
$this->addElement(
|
||||||
|
'button',
|
||||||
|
'btn_submit',
|
||||||
|
array(
|
||||||
|
'class' => 'link-button spinner',
|
||||||
|
'decorators' => array(
|
||||||
|
'ViewHelper',
|
||||||
|
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
|
||||||
|
),
|
||||||
|
'escape' => false,
|
||||||
|
'ignore' => true,
|
||||||
|
'label' => $this->getView()->icon('cancel'),
|
||||||
|
'title' => $this->translate('Acknowledge this announcement'),
|
||||||
|
'type' => 'submit'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function createElements(array $formData = array())
|
||||||
|
{
|
||||||
|
$this->addElements(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'hidden',
|
||||||
|
'hash',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'validators' => array('NotEmpty'),
|
||||||
|
'decorators' => array('ViewHelper')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onSuccess()
|
||||||
|
{
|
||||||
|
$cookie = new AnnouncementCookie();
|
||||||
|
$acknowledged = $cookie->getAcknowledged();
|
||||||
|
$acknowledged[] = $this->getElement('hash')->getValue();
|
||||||
|
$cookie->setAcknowledged($acknowledged);
|
||||||
|
$this->getResponse()->setCookie($cookie);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
117
application/forms/Announcement/AnnouncementForm.php
Normal file
117
application/forms/Announcement/AnnouncementForm.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Forms\Announcement;
|
||||||
|
|
||||||
|
use Icinga\Authentication\Auth;
|
||||||
|
use Icinga\Data\Filter\Filter;
|
||||||
|
use Icinga\Forms\RepositoryForm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create, update and delete announcements
|
||||||
|
*/
|
||||||
|
class AnnouncementForm extends RepositoryForm
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function createInsertElements(array $formData)
|
||||||
|
{
|
||||||
|
$this->addElement(
|
||||||
|
'text',
|
||||||
|
'author',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'value' => Auth::getInstance()->getUser()->getUsername(),
|
||||||
|
'disabled' => true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->addElement(
|
||||||
|
'textarea',
|
||||||
|
'message',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'label' => $this->translate('Message'),
|
||||||
|
'description' => $this->translate('The message to display to users')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->addElement(
|
||||||
|
'dateTimePicker',
|
||||||
|
'start',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'label' => $this->translate('Start'),
|
||||||
|
'description' => $this->translate('The time to display the announcement from')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->addElement(
|
||||||
|
'dateTimePicker',
|
||||||
|
'end',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'label' => $this->translate('End'),
|
||||||
|
'description' => $this->translate('The time to display the announcement until')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->setTitle($this->translate('Create a new announcement'));
|
||||||
|
$this->setSubmitLabel($this->translate('Create'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function createUpdateElements(array $formData)
|
||||||
|
{
|
||||||
|
$this->createInsertElements($formData);
|
||||||
|
$this->setTitle(sprintf($this->translate('Edit announcement %s'), $this->getIdentifier()));
|
||||||
|
$this->setSubmitLabel($this->translate('Save'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function createDeleteElements(array $formData)
|
||||||
|
{
|
||||||
|
$this->setTitle(sprintf($this->translate('Remove announcement %s?'), $this->getIdentifier()));
|
||||||
|
$this->setSubmitLabel($this->translate('Yes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function createFilter()
|
||||||
|
{
|
||||||
|
return Filter::where('id', $this->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function getInsertMessage($success)
|
||||||
|
{
|
||||||
|
return $success
|
||||||
|
? $this->translate('Announcement created')
|
||||||
|
: $this->translate('Failed to create announcement');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function getUpdateMessage($success)
|
||||||
|
{
|
||||||
|
return $success
|
||||||
|
? $this->translate('Announcement updated')
|
||||||
|
: $this->translate('Failed to update announcement');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function getDeleteMessage($success)
|
||||||
|
{
|
||||||
|
return $success
|
||||||
|
? $this->translate('Announcement removed')
|
||||||
|
: $this->translate('Failed to remove announcement');
|
||||||
|
}
|
||||||
|
}
|
@ -39,9 +39,10 @@ class LoginForm extends Form
|
|||||||
'text',
|
'text',
|
||||||
'username',
|
'username',
|
||||||
array(
|
array(
|
||||||
'required' => true,
|
'autocapitalize' => 'off',
|
||||||
'label' => $this->translate('Username'),
|
'class' => false === isset($formData['username']) ? 'autofocus' : '',
|
||||||
'class' => false === isset($formData['username']) ? 'autofocus' : ''
|
'label' => $this->translate('Username'),
|
||||||
|
'required' => true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
namespace Icinga\Forms\Config\General;
|
namespace Icinga\Forms\Config\General;
|
||||||
|
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
|
use Icinga\Application\Logger\Writer\SyslogWriter;
|
||||||
|
use Icinga\Application\Platform;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,22 +92,33 @@ class LoggingConfigForm extends Form
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
/*
|
|
||||||
* Note(el): Since we provide only one possible value for the syslog facility, I opt against exposing
|
if (! isset($formData['logging_log']) || $formData['logging_log'] === 'syslog') {
|
||||||
* this configuration.
|
if (Platform::isWindows()) {
|
||||||
*/
|
/* @see https://secure.php.net/manual/en/function.openlog.php */
|
||||||
// $this->addElement(
|
$this->addElement(
|
||||||
// 'select',
|
'hidden',
|
||||||
// 'logging_facility',
|
'logging_facility',
|
||||||
// array(
|
array(
|
||||||
// 'required' => true,
|
'value' => 'user',
|
||||||
// 'label' => $this->translate('Facility'),
|
'disabled' => true
|
||||||
// 'description' => $this->translate('The syslog facility to utilize.'),
|
)
|
||||||
// 'multiOptions' => array(
|
);
|
||||||
// 'user' => 'LOG_USER'
|
} else {
|
||||||
// )
|
$facilities = array_keys(SyslogWriter::$facilities);
|
||||||
// )
|
$this->addElement(
|
||||||
// );
|
'select',
|
||||||
|
'logging_facility',
|
||||||
|
array(
|
||||||
|
'required' => true,
|
||||||
|
'label' => $this->translate('Facility'),
|
||||||
|
'description' => $this->translate('The syslog facility to utilize.'),
|
||||||
|
'value' => 'user',
|
||||||
|
'multiOptions' => array_combine($facilities, $facilities)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') {
|
} elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') {
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
'text',
|
'text',
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
namespace Icinga\Forms\Config;
|
namespace Icinga\Forms\Config;
|
||||||
|
|
||||||
|
use Icinga\Application\Config;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Icinga\Application\Platform;
|
use Icinga\Application\Platform;
|
||||||
use Icinga\Exception\ConfigurationError;
|
use Icinga\Exception\ConfigurationError;
|
||||||
@ -21,6 +22,13 @@ use Icinga\Web\Notification;
|
|||||||
|
|
||||||
class ResourceConfigForm extends ConfigForm
|
class ResourceConfigForm extends ConfigForm
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* If the global config must be updated because a resource has been changed, this is the updated global config
|
||||||
|
*
|
||||||
|
* @var Config|null
|
||||||
|
*/
|
||||||
|
protected $updatedAppConfig = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this form
|
* Initialize this form
|
||||||
*/
|
*/
|
||||||
@ -104,6 +112,16 @@ class ResourceConfigForm extends ConfigForm
|
|||||||
$this->config->removeSection($name);
|
$this->config->removeSection($name);
|
||||||
unset($values['name']);
|
unset($values['name']);
|
||||||
$this->config->setSection($newName, $resourceConfig->merge($values));
|
$this->config->setSection($newName, $resourceConfig->merge($values));
|
||||||
|
|
||||||
|
if ($newName !== $name) {
|
||||||
|
$appConfig = Config::app();
|
||||||
|
$section = $appConfig->getSection('global');
|
||||||
|
if ($section->config_resource === $name) {
|
||||||
|
$section->config_resource = $newName;
|
||||||
|
$this->updatedAppConfig = $appConfig->setSection('global', $section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $resourceConfig;
|
return $resourceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,10 +181,10 @@ class ResourceConfigForm extends ConfigForm
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->add(array_filter($this->getValues()));
|
$this->add(static::transformEmptyValuesToNull($this->getValues()));
|
||||||
$message = $this->translate('Resource "%s" has been successfully created');
|
$message = $this->translate('Resource "%s" has been successfully created');
|
||||||
} else { // edit existing resource
|
} else { // edit existing resource
|
||||||
$this->edit($resource, array_filter($this->getValues()));
|
$this->edit($resource, static::transformEmptyValuesToNull($this->getValues()));
|
||||||
$message = $this->translate('Resource "%s" has been successfully changed');
|
$message = $this->translate('Resource "%s" has been successfully changed');
|
||||||
}
|
}
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
@ -376,4 +394,15 @@ class ResourceConfigForm extends ConfigForm
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function writeConfig(Config $config)
|
||||||
|
{
|
||||||
|
parent::writeConfig($config);
|
||||||
|
if ($this->updatedAppConfig !== null) {
|
||||||
|
$this->updatedAppConfig->saveIni();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,12 +200,6 @@ class UserBackendConfigForm extends ConfigForm
|
|||||||
}
|
}
|
||||||
|
|
||||||
$backendConfig->merge($data);
|
$backendConfig->merge($data);
|
||||||
foreach ($backendConfig->toArray() as $k => $v) {
|
|
||||||
if ($v === null) {
|
|
||||||
unset($backendConfig->$k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->config->setSection($name, $backendConfig);
|
$this->config->setSection($name, $backendConfig);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,8 @@ class LdapUserGroupBackendForm extends Form
|
|||||||
$this->createGroupConfigElements($defaults, $groupConfigDisabled);
|
$this->createGroupConfigElements($defaults, $groupConfigDisabled);
|
||||||
if (count($userBackends) === 1 || (isset($formData['user_backend']) && $formData['user_backend'] === 'none')) {
|
if (count($userBackends) === 1 || (isset($formData['user_backend']) && $formData['user_backend'] === 'none')) {
|
||||||
$this->createUserConfigElements($defaults, $userConfigDisabled);
|
$this->createUserConfigElements($defaults, $userConfigDisabled);
|
||||||
|
} else {
|
||||||
|
$this->createHiddenUserConfigElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
@ -278,6 +280,19 @@ class LdapUserGroupBackendForm extends Form
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and add all elements for the user configuration as hidden inputs
|
||||||
|
*
|
||||||
|
* This is required to purge already present options when unlinking a group backend with a user backend.
|
||||||
|
*/
|
||||||
|
protected function createHiddenUserConfigElements()
|
||||||
|
{
|
||||||
|
$this->addElement('hidden', 'user_class', array('disabled' => true));
|
||||||
|
$this->addElement('hidden', 'user_filter', array('disabled' => true));
|
||||||
|
$this->addElement('hidden', 'user_name_attribute', array('disabled' => true));
|
||||||
|
$this->addElement('hidden', 'user_base_dn', array('disabled' => true));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the names of all configured LDAP resources
|
* Return the names of all configured LDAP resources
|
||||||
*
|
*
|
||||||
|
@ -127,14 +127,7 @@ class UserGroupBackendForm extends ConfigForm
|
|||||||
unset($data['name']);
|
unset($data['name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$backendConfig->merge($data);
|
$this->config->setSection($name, $backendConfig->merge($data));
|
||||||
foreach ($backendConfig->toArray() as $k => $v) {
|
|
||||||
if ($v === null) {
|
|
||||||
unset($backendConfig->$k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->config->setSection($name, $backendConfig);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class ConfigForm extends Form
|
|||||||
{
|
{
|
||||||
$sections = array();
|
$sections = array();
|
||||||
foreach ($this->getValues() as $sectionAndPropertyName => $value) {
|
foreach ($this->getValues() as $sectionAndPropertyName => $value) {
|
||||||
if ($value === '') {
|
if (empty($value)) {
|
||||||
$value = null; // Causes the config writer to unset it
|
$value = null; // Causes the config writer to unset it
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,4 +127,16 @@ class ConfigForm extends Form
|
|||||||
{
|
{
|
||||||
$config->saveIni();
|
$config->saveIni();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform all empty values of the given array to null
|
||||||
|
*
|
||||||
|
* @param array $values
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function transformEmptyValuesToNull(array $values)
|
||||||
|
{
|
||||||
|
return array_map(function ($v) { return empty($v) ? null : $v; }, $values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
namespace Icinga\Forms\Dashboard;
|
namespace Icinga\Forms\Dashboard;
|
||||||
|
|
||||||
use Icinga\Web\Widget\Dashboard;
|
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
|
use Icinga\Web\Form\Validator\InternalUrlValidator;
|
||||||
use Icinga\Web\Form\Validator\UrlValidator;
|
use Icinga\Web\Form\Validator\UrlValidator;
|
||||||
use Icinga\Web\Url;
|
use Icinga\Web\Url;
|
||||||
|
use Icinga\Web\Widget\Dashboard;
|
||||||
use Icinga\Web\Widget\Dashboard\Dashlet;
|
use Icinga\Web\Widget\Dashboard\Dashlet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,7 +71,7 @@ class DashletForm extends Form
|
|||||||
'description' => $this->translate(
|
'description' => $this->translate(
|
||||||
'Enter url being loaded in the dashlet. You can paste the full URL, including filters.'
|
'Enter url being loaded in the dashlet. You can paste the full URL, including filters.'
|
||||||
),
|
),
|
||||||
'validators' => array(new UrlValidator())
|
'validators' => array(new UrlValidator(), new InternalUrlValidator())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
|
@ -426,11 +426,6 @@ class NavigationConfigForm extends ConfigForm
|
|||||||
}
|
}
|
||||||
|
|
||||||
$itemConfig->merge($data);
|
$itemConfig->merge($data);
|
||||||
foreach ($itemConfig->toArray() as $k => $v) {
|
|
||||||
if ($v === null) {
|
|
||||||
unset($itemConfig->$k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($shared) {
|
if ($shared) {
|
||||||
// Share all descendant children
|
// Share all descendant children
|
||||||
|
@ -53,9 +53,27 @@ class NavigationItemForm extends Form
|
|||||||
'allowEmpty' => true,
|
'allowEmpty' => true,
|
||||||
'label' => $this->translate('Url'),
|
'label' => $this->translate('Url'),
|
||||||
'description' => $this->translate(
|
'description' => $this->translate(
|
||||||
'The url of this navigation item. Leave blank if you only want the'
|
'The url of this navigation item. Leave blank if only the name should be displayed.'
|
||||||
. ' name being displayed. For external urls, make sure to prepend'
|
. ' For urls with username and password and for all external urls,'
|
||||||
. ' an appropriate protocol identifier (e.g. http://example.tld)'
|
. ' make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)'
|
||||||
|
),
|
||||||
|
'validators' => array(
|
||||||
|
array(
|
||||||
|
'Callback',
|
||||||
|
false,
|
||||||
|
array(
|
||||||
|
'callback' => function($url) {
|
||||||
|
// Matches if the given url contains obviously
|
||||||
|
// a username but not any protocol identifier
|
||||||
|
return !preg_match('#^((?=[^/@]).)+@.*$#', $url);
|
||||||
|
},
|
||||||
|
'messages' => array(
|
||||||
|
'callbackValue' => $this->translate(
|
||||||
|
'Missing protocol identifier'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -81,8 +99,10 @@ class NavigationItemForm extends Form
|
|||||||
$values = parent::getValues($suppressArrayNotation);
|
$values = parent::getValues($suppressArrayNotation);
|
||||||
if (isset($values['url']) && $values['url']) {
|
if (isset($values['url']) && $values['url']) {
|
||||||
$url = Url::fromPath($values['url']);
|
$url = Url::fromPath($values['url']);
|
||||||
if (! $url->isExternal() && ($relativePath = $url->getRelativeUrl())) {
|
if ($url->getBasePath() === $this->getRequest()->getBasePath()) {
|
||||||
$values['url'] = $relativePath;
|
$values['url'] = $url->getRelativeUrl();
|
||||||
|
} else {
|
||||||
|
$values['url'] = $url->getAbsoluteUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +156,16 @@ class PreferenceForm extends Form
|
|||||||
*/
|
*/
|
||||||
public function createElements(array $formData)
|
public function createElements(array $formData)
|
||||||
{
|
{
|
||||||
|
if (setlocale(LC_ALL, 0) === 'C') {
|
||||||
|
$this->warning(
|
||||||
|
$this->translate(
|
||||||
|
'Your language setting is not applied because your platform is missing the corresponding locale.'
|
||||||
|
. ' Make sure to install the correct language pack and restart your web server afterwards.'
|
||||||
|
),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (! (bool) Config::app()->get('themes', 'disabled', false)) {
|
if (! (bool) Config::app()->get('themes', 'disabled', false)) {
|
||||||
$themes = Icinga::app()->getThemes();
|
$themes = Icinga::app()->getThemes();
|
||||||
if (count($themes) > 1) {
|
if (count($themes) > 1) {
|
||||||
|
@ -42,6 +42,11 @@ class RoleForm extends ConfigForm
|
|||||||
'application/stacktraces' => $this->translate(
|
'application/stacktraces' => $this->translate(
|
||||||
'Allow to adjust in the preferences whether to show stacktraces'
|
'Allow to adjust in the preferences whether to show stacktraces'
|
||||||
) . ' (application/stacktraces)',
|
) . ' (application/stacktraces)',
|
||||||
|
'application/log' => $this->translate('Allow to view the application log')
|
||||||
|
. ' (application/log)',
|
||||||
|
'admin' => $this->translate(
|
||||||
|
'Grant admin permissions, e.g. manage announcements'
|
||||||
|
) . ' (admin)',
|
||||||
'config/*' => $this->translate('Allow config access') . ' (config/*)'
|
'config/*' => $this->translate('Allow config access') . ' (config/*)'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -284,7 +289,7 @@ class RoleForm extends ConfigForm
|
|||||||
*/
|
*/
|
||||||
public function getValues($suppressArrayNotation = false)
|
public function getValues($suppressArrayNotation = false)
|
||||||
{
|
{
|
||||||
$values = array_filter(parent::getValues($suppressArrayNotation));
|
$values = static::transformEmptyValuesToNull(parent::getValues($suppressArrayNotation));
|
||||||
if (isset($values['permissions'])) {
|
if (isset($values['permissions'])) {
|
||||||
$values['permissions'] = implode(', ', $values['permissions']);
|
$values['permissions'] = implode(', ', $values['permissions']);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ if ($this->layout()->autorefreshInterval) {
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
<div id="header">
|
<div id="header">
|
||||||
|
<div id="announcements" class="container">
|
||||||
|
<?= $this->widget('announcements') ?>
|
||||||
|
</div>
|
||||||
<div id="header-logo-container">
|
<div id="header-logo-container">
|
||||||
<?= $this->qlink(
|
<?= $this->qlink(
|
||||||
'',
|
'',
|
||||||
|
@ -24,13 +24,14 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
|||||||
<html class="no-js<?= $iframeClass ?>" lang="<?= $lang ?>"> <!--<![endif]-->
|
<html class="no-js<?= $iframeClass ?>" lang="<?= $lang ?>"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
|
|
||||||
<meta name="google" value="notranslate">
|
<meta name="google" value="notranslate">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta http-equiv="cleartype" content="on">
|
<meta http-equiv="cleartype" content="on">
|
||||||
<title><?= $this->title ? $this->escape($this->title) : 'Icinga Web' ?></title>
|
<title><?= $this->title ? $this->escape($this->title) : 'Icinga Web' ?></title>
|
||||||
<!-- TODO: viewport and scale settings make no sense for us, fix this -->
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="application-name" content="Icinga Web 2">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Icinga">
|
||||||
<?php if ($isIframe): ?>
|
<?php if ($isIframe): ?>
|
||||||
<base target="_parent"/>
|
<base target="_parent"/>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@ -47,6 +48,7 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
|||||||
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
<link type="image/png" rel="shortcut icon" href="<?= $this->baseUrl('img/favicon.png') ?>" />
|
<link type="image/png" rel="shortcut icon" href="<?= $this->baseUrl('img/favicon.png') ?>" />
|
||||||
|
<link rel="apple-touch-icon" href="<?= $this->baseUrl('img/touch-icon.png') ?>">
|
||||||
</head>
|
</head>
|
||||||
<body id="body" class="loading">
|
<body id="body" class="loading">
|
||||||
<pre id="responsive-debug"></pre>
|
<pre id="responsive-debug"></pre>
|
||||||
|
@ -31,7 +31,7 @@ if ( isset($pdf) )
|
|||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<?= $this->img('img/logo_icinga_big_dark.png', null, array('align' => 'right', 'width' => '75')) ?>
|
<?= $this->img('img/icinga-logo-big-dark.png', null, array('align' => 'right', 'width' => '75')) ?>
|
||||||
<!--<div id="page-header">
|
<!--<div id="page-header">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
BIN
application/locale/ar_SA/LC_MESSAGES/icinga.mo
Normal file
BIN
application/locale/ar_SA/LC_MESSAGES/icinga.mo
Normal file
Binary file not shown.
3015
application/locale/ar_SA/LC_MESSAGES/icinga.po
Normal file
3015
application/locale/ar_SA/LC_MESSAGES/icinga.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -7,16 +7,16 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Icinga Web 2 (None)\n"
|
"Project-Id-Version: Icinga Web 2 (None)\n"
|
||||||
"Report-Msgid-Bugs-To: dev@icinga.org\n"
|
"Report-Msgid-Bugs-To: dev@icinga.org\n"
|
||||||
"POT-Creation-Date: 2016-02-29 14:39+0000\n"
|
"POT-Creation-Date: 2016-09-27 09:09+0000\n"
|
||||||
"PO-Revision-Date: 2016-02-29 22:16+0100\n"
|
"PO-Revision-Date: 2016-09-27 11:15+0200\n"
|
||||||
"Last-Translator: Thomas Gelf <thomas@gelf.net>\n"
|
"Last-Translator: Eric Lippmann <eric.lippmann@netways.de>\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"X-Generator: Poedit 1.8.7\n"
|
"X-Generator: Poedit 1.8.9\n"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Form/Validator/InArray.php:16
|
#: /vagrant/library/Icinga/Web/Form/Validator/InArray.php:16
|
||||||
#, php-format
|
#, php-format
|
||||||
@ -143,9 +143,8 @@ msgid "Add"
|
|||||||
msgstr "Hinzufügen"
|
msgstr "Hinzufügen"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/Tabextension/DashboardSettings.php:25
|
#: /vagrant/library/Icinga/Web/Widget/Tabextension/DashboardSettings.php:25
|
||||||
#, fuzzy
|
|
||||||
msgid "Add Dashlet"
|
msgid "Add Dashlet"
|
||||||
msgstr "Dashlet bearbeiten"
|
msgstr "Dashlet hinzufügen"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/DashboardController.php:76
|
#: /vagrant/application/controllers/DashboardController.php:76
|
||||||
msgid "Add Dashlet To Dashboard"
|
msgid "Add Dashlet To Dashboard"
|
||||||
@ -164,11 +163,11 @@ msgstr "Zum Dashboard hinzufügen"
|
|||||||
msgid "Add To Menu"
|
msgid "Add To Menu"
|
||||||
msgstr "Zum Menü hinzufügen"
|
msgstr "Zum Menü hinzufügen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:34
|
#: /vagrant/application/views/scripts/user/list.phtml:32
|
||||||
msgid "Add a New User"
|
msgid "Add a New User"
|
||||||
msgstr "Neuen Benutzer anlegen"
|
msgstr "Neuen Benutzer anlegen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:34
|
#: /vagrant/application/views/scripts/group/list.phtml:32
|
||||||
msgid "Add a New User Group"
|
msgid "Add a New User Group"
|
||||||
msgstr "Neue Gruppe anlegen"
|
msgstr "Neue Gruppe anlegen"
|
||||||
|
|
||||||
@ -180,13 +179,13 @@ msgstr "Neue Gruppe anlegen"
|
|||||||
msgid "Add a new user"
|
msgid "Add a new user"
|
||||||
msgstr "Neuen Benutzer anlegen"
|
msgstr "Neuen Benutzer anlegen"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:409
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:410
|
||||||
msgid "Add another filter"
|
msgid "Add another filter"
|
||||||
msgstr "Weiteren Filter hinzufügen..."
|
msgstr "Weiteren Filter hinzufügen"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:80
|
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:80
|
||||||
msgid "Add filter..."
|
msgid "Add filter..."
|
||||||
msgstr "Filter hinzufügen..."
|
msgstr "Filter hinzufügen"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/UserGroup/AddMemberForm.php:125
|
#: /vagrant/application/forms/Config/UserGroup/AddMemberForm.php:125
|
||||||
#, php-format
|
#, php-format
|
||||||
@ -231,6 +230,10 @@ msgstr "Erlaubt die Einstellung zur Anzeige von Stacktraces"
|
|||||||
msgid "Allow to share navigation items"
|
msgid "Allow to share navigation items"
|
||||||
msgstr "Erlaubt das Teilen von Navigationselemten"
|
msgstr "Erlaubt das Teilen von Navigationselemten"
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Security/RoleForm.php:45
|
||||||
|
msgid "Allow to view the application log"
|
||||||
|
msgstr "Erlaubt das Einsehen des Anwendungslogs"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:135
|
#: /vagrant/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:135
|
||||||
msgid ""
|
msgid ""
|
||||||
"An additional filter to use when looking up groups using the specified "
|
"An additional filter to use when looking up groups using the specified "
|
||||||
@ -249,7 +252,7 @@ msgstr ""
|
|||||||
"Verbindung. Leer lassen, um keine Filter zu verwenden."
|
"Verbindung. Leer lassen, um keine Filter zu verwenden."
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:318
|
#: /vagrant/library/Icinga/Application/Web.php:318
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:273
|
#: /vagrant/library/Icinga/Web/Menu.php:274
|
||||||
msgid "Application"
|
msgid "Application"
|
||||||
msgstr "Anwendung"
|
msgstr "Anwendung"
|
||||||
|
|
||||||
@ -268,7 +271,7 @@ msgstr "Anwenden"
|
|||||||
|
|
||||||
#: /vagrant/application/controllers/ConfigController.php:50
|
#: /vagrant/application/controllers/ConfigController.php:50
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:324
|
#: /vagrant/library/Icinga/Application/Web.php:324
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:278
|
#: /vagrant/library/Icinga/Web/Menu.php:279
|
||||||
msgid "Authentication"
|
msgid "Authentication"
|
||||||
msgstr "Authentifizierung"
|
msgstr "Authentifizierung"
|
||||||
|
|
||||||
@ -365,7 +368,7 @@ msgstr "Abbrechen"
|
|||||||
msgid "Cancel this membership"
|
msgid "Cancel this membership"
|
||||||
msgstr "Diese Mitgliedschaft beenden"
|
msgstr "Diese Mitgliedschaft beenden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:435
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:436
|
||||||
msgid "Cancel this operation"
|
msgid "Cancel this operation"
|
||||||
msgstr "Diese Operation abbrechen"
|
msgstr "Diese Operation abbrechen"
|
||||||
|
|
||||||
@ -446,7 +449,7 @@ msgstr ""
|
|||||||
msgid "Click to remove this part of your filter"
|
msgid "Click to remove this part of your filter"
|
||||||
msgstr "Klicken, um diesen Teil des Filters zu löschen"
|
msgstr "Klicken, um diesen Teil des Filters zu löschen"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Repository/Repository.php:1022
|
#: /vagrant/library/Icinga/Repository/Repository.php:1023
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Column \"%s\" cannot be queried"
|
msgid "Column \"%s\" cannot be queried"
|
||||||
msgstr "Spalte “%s” kann nicht abgefragt werden"
|
msgstr "Spalte “%s” kann nicht abgefragt werden"
|
||||||
@ -472,7 +475,7 @@ msgid "Comma-separated list of users that are assigned to the role"
|
|||||||
msgstr "Kommaseparierte Liste von Nutzern, die dieser Rolle zugewiesen werden"
|
msgstr "Kommaseparierte Liste von Nutzern, die dieser Rolle zugewiesen werden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:312
|
#: /vagrant/library/Icinga/Application/Web.php:312
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:268
|
#: /vagrant/library/Icinga/Web/Menu.php:269
|
||||||
msgid "Configuration"
|
msgid "Configuration"
|
||||||
msgstr "Konfiguration"
|
msgstr "Konfiguration"
|
||||||
|
|
||||||
@ -486,9 +489,8 @@ msgstr ""
|
|||||||
"2"
|
"2"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/ConfigController.php:49
|
#: /vagrant/application/controllers/ConfigController.php:49
|
||||||
#, fuzzy
|
|
||||||
msgid "Configure the user and group backends"
|
msgid "Configure the user and group backends"
|
||||||
msgstr "Neues Backend für Benutzergruppen erstellen"
|
msgstr "Konfiguration der Benutzer- und Gruppen-Backends"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/ConfigController.php:43
|
#: /vagrant/application/controllers/ConfigController.php:43
|
||||||
msgid "Configure which resources are being utilized by Icinga Web 2"
|
msgid "Configure which resources are being utilized by Icinga Web 2"
|
||||||
@ -522,7 +524,7 @@ msgid ""
|
|||||||
"modules."
|
"modules."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Enthält die Verzeichnisse, die nach verfügbaren Modulen durchsucht werden "
|
"Enthält die Verzeichnisse, die nach verfügbaren Modulen durchsucht werden "
|
||||||
"(kommasepariert). Module, die nicht in diesen Verzeichnissen vorhanden sind, "
|
"(getrennt durch Doppelpunkte). Module, die nicht in diesen Verzeichnissen vorhanden sind, "
|
||||||
"können trotzdem in den Modulordner gesymlinkt werden. Diese werden "
|
"können trotzdem in den Modulordner gesymlinkt werden. Diese werden "
|
||||||
"allerdings nicht in der der deaktivierten Module angezeigt."
|
"allerdings nicht in der der deaktivierten Module angezeigt."
|
||||||
|
|
||||||
@ -652,6 +654,7 @@ msgstr ""
|
|||||||
"Momentan ist kein Dashlet verfügbar. Das könnte durch die Aktivierung von "
|
"Momentan ist kein Dashlet verfügbar. Das könnte durch die Aktivierung von "
|
||||||
"einigen der verfügbaren %s behoben werden."
|
"einigen der verfügbaren %s behoben werden."
|
||||||
|
|
||||||
|
#: /vagrant/application/controllers/DashboardController.php:257
|
||||||
#: /vagrant/application/forms/Dashboard/DashletForm.php:120
|
#: /vagrant/application/forms/Dashboard/DashletForm.php:120
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:290
|
#: /vagrant/library/Icinga/Application/Web.php:290
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:243
|
#: /vagrant/library/Icinga/Web/Menu.php:243
|
||||||
@ -712,10 +715,9 @@ msgid "Debug"
|
|||||||
msgstr "Debug"
|
msgstr "Debug"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/General/ThemingConfigForm.php:42
|
#: /vagrant/application/forms/Config/General/ThemingConfigForm.php:42
|
||||||
#, fuzzy
|
|
||||||
msgctxt "Form element label"
|
msgctxt "Form element label"
|
||||||
msgid "Default Theme"
|
msgid "Default Theme"
|
||||||
msgstr "Standardsprache"
|
msgstr "Standard-Theme"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/config/module.phtml:46
|
#: /vagrant/application/views/scripts/config/module.phtml:46
|
||||||
msgid "Dependencies"
|
msgid "Dependencies"
|
||||||
@ -742,6 +744,14 @@ msgstr "Automatische Aktualisierung deaktivieren"
|
|||||||
msgid "Disable the %s module"
|
msgid "Disable the %s module"
|
||||||
msgstr "Modul %s deaktivieren"
|
msgstr "Modul %s deaktivieren"
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Dashboard/PaneForm.php:42
|
||||||
|
msgid "Disable the dashboard"
|
||||||
|
msgstr "Dashboard deaktivieren"
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Dashboard/PaneForm.php:43
|
||||||
|
msgid "Disabled"
|
||||||
|
msgstr "Deaktiviert"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/UserBackend/LdapBackendForm.php:88
|
#: /vagrant/application/forms/Config/UserBackend/LdapBackendForm.php:88
|
||||||
msgctxt "A button to discover LDAP capabilities"
|
msgctxt "A button to discover LDAP capabilities"
|
||||||
msgid "Discover"
|
msgid "Discover"
|
||||||
@ -756,11 +766,10 @@ msgid "Edit Dashlet"
|
|||||||
msgstr "Dashlet bearbeiten"
|
msgstr "Dashlet bearbeiten"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/user/show.phtml:16
|
#: /vagrant/application/views/scripts/user/show.phtml:16
|
||||||
#, fuzzy
|
|
||||||
msgid "Edit User"
|
msgid "Edit User"
|
||||||
msgstr "Nutzer %s bearbeiten"
|
msgstr "Nutzer bearbeiten"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/settings.phtml:53
|
#: /vagrant/application/views/scripts/dashboard/settings.phtml:54
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Edit dashlet %s"
|
msgid "Edit dashlet %s"
|
||||||
msgstr "Dashlet %s bearbeiten"
|
msgstr "Dashlet %s bearbeiten"
|
||||||
@ -831,7 +840,6 @@ msgid "Enter a title for the dashlet."
|
|||||||
msgstr "Titel für das Dashlet eingeben"
|
msgstr "Titel für das Dashlet eingeben"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Dashboard/DashletForm.php:111
|
#: /vagrant/application/forms/Dashboard/DashletForm.php:111
|
||||||
#, fuzzy
|
|
||||||
msgid "Enter a title for the new dashboard"
|
msgid "Enter a title for the new dashboard"
|
||||||
msgstr "Titel für das Dashboard eingeben"
|
msgstr "Titel für das Dashboard eingeben"
|
||||||
|
|
||||||
@ -844,7 +852,6 @@ msgstr ""
|
|||||||
"URLs mit Filtern möglich."
|
"URLs mit Filtern möglich."
|
||||||
|
|
||||||
#: /vagrant/application/controllers/ErrorController.php:109
|
#: /vagrant/application/controllers/ErrorController.php:109
|
||||||
#, fuzzy
|
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr "Fehler"
|
msgstr "Fehler"
|
||||||
|
|
||||||
@ -981,17 +988,17 @@ msgstr "Dateipfad"
|
|||||||
msgid "Filter Pattern"
|
msgid "Filter Pattern"
|
||||||
msgstr "Muster"
|
msgstr "Muster"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Repository/Repository.php:1066
|
#: /vagrant/library/Icinga/Repository/Repository.php:1067
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Filter column \"%s\" not found"
|
msgid "Filter column \"%s\" not found"
|
||||||
msgstr "Filterspalte “%s” nicht gefunden"
|
msgstr "Filterspalte “%s” nicht gefunden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Repository/Repository.php:1070
|
#: /vagrant/library/Icinga/Repository/Repository.php:1071
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Filter column \"%s\" not found in table \"%s\""
|
msgid "Filter column \"%s\" not found in table \"%s\""
|
||||||
msgstr "Filterspalte “%s” in Tabelle “%s” nicht gefunden"
|
msgstr "Filterspalte “%s” in Tabelle “%s” nicht gefunden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:744
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:745
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:89
|
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:89
|
||||||
msgid "Filter this list"
|
msgid "Filter this list"
|
||||||
msgstr "Diese Liste filtern"
|
msgstr "Diese Liste filtern"
|
||||||
@ -1110,7 +1117,7 @@ msgstr "Ich bin bereit zur Suche. Warte auf Eingabe"
|
|||||||
msgid "Icinga Documentation"
|
msgid "Icinga Documentation"
|
||||||
msgstr "Icinga Dokumentation"
|
msgstr "Icinga Dokumentation"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:11
|
#: /vagrant/application/views/scripts/authentication/login.phtml:18
|
||||||
msgid "Icinga Web 2 Documentation"
|
msgid "Icinga Web 2 Documentation"
|
||||||
msgstr "Icinga Web 2 Dokumentation"
|
msgstr "Icinga Web 2 Dokumentation"
|
||||||
|
|
||||||
@ -1118,7 +1125,7 @@ msgstr "Icinga Web 2 Dokumentation"
|
|||||||
msgid "Icinga Web 2 Login"
|
msgid "Icinga Web 2 Login"
|
||||||
msgstr "Icinga Web 2 Anmeldung"
|
msgstr "Icinga Web 2 Anmeldung"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:12
|
#: /vagrant/application/views/scripts/authentication/login.phtml:19
|
||||||
msgid "Icinga Web 2 Setup-Wizard"
|
msgid "Icinga Web 2 Setup-Wizard"
|
||||||
msgstr "Icinga Web 2 Einrichtungsassistent"
|
msgstr "Icinga Web 2 Einrichtungsassistent"
|
||||||
|
|
||||||
@ -1127,18 +1134,17 @@ msgid "Icinga Wiki"
|
|||||||
msgstr "Icinga Wiki"
|
msgstr "Icinga Wiki"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/about/index.phtml:55
|
#: /vagrant/application/views/scripts/about/index.phtml:55
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:37
|
#: /vagrant/application/views/scripts/authentication/login.phtml:44
|
||||||
msgid "Icinga on Facebook"
|
msgid "Icinga on Facebook"
|
||||||
msgstr "Icinga auf Facebook"
|
msgstr "Icinga auf Facebook"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/about/index.phtml:64
|
#: /vagrant/application/views/scripts/about/index.phtml:64
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:46
|
#: /vagrant/application/views/scripts/authentication/login.phtml:53
|
||||||
#, fuzzy
|
|
||||||
msgid "Icinga on Google+"
|
msgid "Icinga on Google+"
|
||||||
msgstr "Icinga auf Facebook"
|
msgstr "Icinga auf Google+"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/about/index.phtml:46
|
#: /vagrant/application/views/scripts/about/index.phtml:46
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:27
|
#: /vagrant/application/views/scripts/authentication/login.phtml:34
|
||||||
msgid "Icinga on Twitter"
|
msgid "Icinga on Twitter"
|
||||||
msgstr "Icinga auf Twitter"
|
msgstr "Icinga auf Twitter"
|
||||||
|
|
||||||
@ -1188,7 +1194,7 @@ msgstr "Ungültiger Backendtyp “%s” angegeben"
|
|||||||
msgid "Invalid resource type \"%s\" provided"
|
msgid "Invalid resource type \"%s\" provided"
|
||||||
msgstr "Ungültiger Ressourcentyp “%s” angegeben"
|
msgstr "Ungültiger Ressourcentyp “%s” angegeben"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:7
|
#: /vagrant/application/views/scripts/authentication/login.phtml:14
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"It appears that you did not configure Icinga Web 2 yet so it's not possible "
|
"It appears that you did not configure Icinga Web 2 yet so it's not possible "
|
||||||
@ -1322,7 +1328,7 @@ msgid "Login"
|
|||||||
msgstr "Anmelden"
|
msgstr "Anmelden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:355
|
#: /vagrant/library/Icinga/Application/Web.php:355
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:313
|
#: /vagrant/library/Icinga/Web/Menu.php:314
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Abmelden"
|
msgstr "Abmelden"
|
||||||
|
|
||||||
@ -1356,7 +1362,7 @@ msgstr "Menüeintrag"
|
|||||||
msgid "Method Not Allowed"
|
msgid "Method Not Allowed"
|
||||||
msgstr "Nicht zulässig"
|
msgstr "Nicht zulässig"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:746
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:747
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:94
|
#: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:94
|
||||||
msgid "Modify this filter"
|
msgid "Modify this filter"
|
||||||
msgstr "Diesen Filter bearbeiten"
|
msgstr "Diesen Filter bearbeiten"
|
||||||
@ -1396,7 +1402,7 @@ msgstr "Pfad zu den Modulen"
|
|||||||
|
|
||||||
#: /vagrant/application/controllers/ConfigController.php:96
|
#: /vagrant/application/controllers/ConfigController.php:96
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:336
|
#: /vagrant/library/Icinga/Application/Web.php:336
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:298
|
#: /vagrant/library/Icinga/Web/Menu.php:299
|
||||||
msgid "Modules"
|
msgid "Modules"
|
||||||
msgstr "Module"
|
msgstr "Module"
|
||||||
|
|
||||||
@ -1473,9 +1479,8 @@ msgid "New Dashboard Title"
|
|||||||
msgstr "Neuen Dashboardtitel vergeben"
|
msgstr "Neuen Dashboardtitel vergeben"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/DashboardController.php:42
|
#: /vagrant/application/controllers/DashboardController.php:42
|
||||||
#, fuzzy
|
|
||||||
msgid "New Dashlet"
|
msgid "New Dashlet"
|
||||||
msgstr "Dashlet aktualisieren"
|
msgstr "Neues Dashlet"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/NavigationController.php:244
|
#: /vagrant/application/controllers/NavigationController.php:244
|
||||||
msgid "New Navigation Item"
|
msgid "New Navigation Item"
|
||||||
@ -1550,15 +1555,15 @@ msgstr ""
|
|||||||
"Keine Authentifizierungsmethode verfügbar. Wurde bei der Installation von "
|
"Keine Authentifizierungsmethode verfügbar. Wurde bei der Installation von "
|
||||||
"Icinga Web 2 eine authentication.ini erstellt?"
|
"Icinga Web 2 eine authentication.ini erstellt?"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:24
|
#: /vagrant/application/views/scripts/group/list.phtml:22
|
||||||
msgid "No backend found which is able to list user groups"
|
msgid "No backend found which is able to list user groups"
|
||||||
msgstr "Kein Backend gefunden, um Benutzergruppen aufzulisten"
|
msgstr "Kein Backend gefunden, um Benutzergruppen aufzulisten"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:24
|
#: /vagrant/application/views/scripts/user/list.phtml:22
|
||||||
msgid "No backend found which is able to list users"
|
msgid "No backend found which is able to list users"
|
||||||
msgstr "Kein Backend gefunden, um Benutzer aufzulisten"
|
msgstr "Kein Backend gefunden, um Benutzer aufzulisten"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/settings.phtml:41
|
#: /vagrant/application/views/scripts/dashboard/settings.phtml:42
|
||||||
msgid "No dashlets added to dashboard"
|
msgid "No dashlets added to dashboard"
|
||||||
msgstr "Dem Dashboard wurden keine Dashlets hinzugefügt"
|
msgstr "Dem Dashboard wurden keine Dashlets hinzugefügt"
|
||||||
|
|
||||||
@ -1577,13 +1582,13 @@ msgstr ""
|
|||||||
msgid "No roles found."
|
msgid "No roles found."
|
||||||
msgstr "Es wurden keine Rollen gefunden"
|
msgstr "Es wurden keine Rollen gefunden"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:46
|
#: /vagrant/application/views/scripts/group/list.phtml:44
|
||||||
msgid "No user groups found matching the filter"
|
msgid "No user groups found matching the filter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Es wurden keine Benutzergruppen gefunden, welche den Filterkriterien "
|
"Es wurden keine Benutzergruppen gefunden, welche den Filterkriterien "
|
||||||
"entsprechen"
|
"entsprechen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:46
|
#: /vagrant/application/views/scripts/user/list.phtml:44
|
||||||
msgid "No users found matching the filter"
|
msgid "No users found matching the filter"
|
||||||
msgstr "Es wurde kein Benutzer gefunden, der den Filterkriterien entspricht"
|
msgstr "Es wurde kein Benutzer gefunden, der den Filterkriterien entspricht"
|
||||||
|
|
||||||
@ -1716,7 +1721,7 @@ msgstr "Port"
|
|||||||
#: /vagrant/application/controllers/NavigationController.php:134
|
#: /vagrant/application/controllers/NavigationController.php:134
|
||||||
#: /vagrant/application/controllers/PreferenceController.php:33
|
#: /vagrant/application/controllers/PreferenceController.php:33
|
||||||
#: /vagrant/library/Icinga/Application/Web.php:350
|
#: /vagrant/library/Icinga/Application/Web.php:350
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:308
|
#: /vagrant/library/Icinga/Web/Menu.php:309
|
||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr "Einstellungen"
|
msgstr "Einstellungen"
|
||||||
|
|
||||||
@ -1768,12 +1773,12 @@ msgid "Push to fill in the chosen connection's default settings."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Klicken Sie hier, um die gewählten Verbindungseinstellungen auszufüllen."
|
"Klicken Sie hier, um die gewählten Verbindungseinstellungen auszufüllen."
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Repository/Repository.php:1018
|
#: /vagrant/library/Icinga/Repository/Repository.php:1019
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Query column \"%s\" not found"
|
msgid "Query column \"%s\" not found"
|
||||||
msgstr "Abfragespalte “%s” wurde nicht gefunden"
|
msgstr "Abfragespalte “%s” wurde nicht gefunden"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Repository/Repository.php:1026
|
#: /vagrant/library/Icinga/Repository/Repository.php:1027
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Query column \"%s\" not found in table \"%s\""
|
msgid "Query column \"%s\" not found in table \"%s\""
|
||||||
msgstr "Abfragespalte “%s” wurde in Tabelle “%s” nicht gefunden"
|
msgstr "Abfragespalte “%s” wurde in Tabelle “%s” nicht gefunden"
|
||||||
@ -1782,9 +1787,9 @@ msgstr "Abfragespalte “%s” wurde in Tabelle “%s” nicht gefunden"
|
|||||||
msgid "Ready to search"
|
msgid "Ready to search"
|
||||||
msgstr "Bereit zur Suche"
|
msgstr "Bereit zur Suche"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:55
|
#: /vagrant/application/views/scripts/group/list.phtml:52
|
||||||
#: /vagrant/application/views/scripts/group/show.phtml:77
|
#: /vagrant/application/views/scripts/group/show.phtml:77
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:55
|
#: /vagrant/application/views/scripts/user/list.phtml:53
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Entfernen"
|
msgstr "Entfernen"
|
||||||
|
|
||||||
@ -1793,9 +1798,8 @@ msgid "Remove Dashboard"
|
|||||||
msgstr "Dashboard entfernen"
|
msgstr "Dashboard entfernen"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/DashboardController.php:158
|
#: /vagrant/application/controllers/DashboardController.php:158
|
||||||
#, fuzzy
|
|
||||||
msgid "Remove Dashlet"
|
msgid "Remove Dashlet"
|
||||||
msgstr "Dashboard entfernen"
|
msgstr "Dashlet entfernen"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/DashboardController.php:192
|
#: /vagrant/application/controllers/DashboardController.php:192
|
||||||
msgid "Remove Dashlet From Dashboard"
|
msgid "Remove Dashlet From Dashboard"
|
||||||
@ -1830,7 +1834,7 @@ msgstr "Benutzergruppe entfernen"
|
|||||||
msgid "Remove User Group Backend"
|
msgid "Remove User Group Backend"
|
||||||
msgstr "Backend für Benutzergruppen entfernen"
|
msgstr "Backend für Benutzergruppen entfernen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/settings.phtml:71
|
#: /vagrant/application/views/scripts/dashboard/settings.phtml:72
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Remove dashlet %s from pane %s"
|
msgid "Remove dashlet %s from pane %s"
|
||||||
msgstr "Dashlet %s aus Dashboard %s entfernen"
|
msgstr "Dashlet %s aus Dashboard %s entfernen"
|
||||||
@ -1845,7 +1849,7 @@ msgstr "Gruppe %s entfernen"
|
|||||||
msgid "Remove navigation item %s"
|
msgid "Remove navigation item %s"
|
||||||
msgstr "Navigationselement %s entfernen"
|
msgstr "Navigationselement %s entfernen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/settings.phtml:32
|
#: /vagrant/application/views/scripts/dashboard/settings.phtml:33
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Remove pane %s"
|
msgid "Remove pane %s"
|
||||||
msgstr "Dashboard %s entfernen"
|
msgstr "Dashboard %s entfernen"
|
||||||
@ -1873,11 +1877,11 @@ msgstr "Diesen Filter entfernen"
|
|||||||
msgid "Remove this member"
|
msgid "Remove this member"
|
||||||
msgstr "Dieses Mitglied entfernen"
|
msgstr "Dieses Mitglied entfernen"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:396
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:397
|
||||||
msgid "Remove this part of your filter"
|
msgid "Remove this part of your filter"
|
||||||
msgstr "Klicken, um diesen Teil des Filters zu löschen"
|
msgstr "Klicken, um diesen Teil des Filters zu löschen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:84
|
#: /vagrant/application/views/scripts/user/list.phtml:82
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Remove user %s"
|
msgid "Remove user %s"
|
||||||
msgstr "Benutzer %s entfernen"
|
msgstr "Benutzer %s entfernen"
|
||||||
@ -1892,7 +1896,7 @@ msgstr "Benutzer %s entfernen"
|
|||||||
msgid "Remove user backend %s"
|
msgid "Remove user backend %s"
|
||||||
msgstr "Benutzerbackend %s entfernen"
|
msgstr "Benutzerbackend %s entfernen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:88
|
#: /vagrant/application/views/scripts/group/list.phtml:86
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Remove user group %s"
|
msgid "Remove user group %s"
|
||||||
msgstr "Benutzergruppe %s entfernen"
|
msgstr "Benutzergruppe %s entfernen"
|
||||||
@ -1998,7 +2002,7 @@ msgstr "Rolle aktualisiert"
|
|||||||
#: /vagrant/application/controllers/GroupController.php:344
|
#: /vagrant/application/controllers/GroupController.php:344
|
||||||
#: /vagrant/application/controllers/RoleController.php:153
|
#: /vagrant/application/controllers/RoleController.php:153
|
||||||
#: /vagrant/application/controllers/UserController.php:310
|
#: /vagrant/application/controllers/UserController.php:310
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:283
|
#: /vagrant/library/Icinga/Web/Menu.php:284
|
||||||
msgid "Roles"
|
msgid "Roles"
|
||||||
msgstr "Rollen"
|
msgstr "Rollen"
|
||||||
|
|
||||||
@ -2053,7 +2057,7 @@ msgstr "Domains durchsuchen"
|
|||||||
msgid "Search this domain for records of available servers."
|
msgid "Search this domain for records of available servers."
|
||||||
msgstr "Diese Domain nach verfügbaren Servern durchsuchen."
|
msgstr "Diese Domain nach verfügbaren Servern durchsuchen."
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:740
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:741
|
||||||
msgid "Search..."
|
msgid "Search..."
|
||||||
msgstr "Suche..."
|
msgstr "Suche..."
|
||||||
|
|
||||||
@ -2062,9 +2066,8 @@ msgid "Searching"
|
|||||||
msgstr "Suche"
|
msgstr "Suche"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Dashboard/DashletForm.php:122
|
#: /vagrant/application/forms/Dashboard/DashletForm.php:122
|
||||||
#, fuzzy
|
|
||||||
msgid "Select a dashboard you want to add the dashlet to"
|
msgid "Select a dashboard you want to add the dashlet to"
|
||||||
msgstr "Wählen Sie ein Dashboard aus, dem Sie das Dashlet hinzufügen wollen."
|
msgstr "Wählen Sie das Dashboard aus, dem Sie das Dashlet hinzufügen wollen"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/User/CreateMembershipForm.php:81
|
#: /vagrant/application/forms/Config/User/CreateMembershipForm.php:81
|
||||||
#, php-format
|
#, php-format
|
||||||
@ -2117,7 +2120,7 @@ msgstr "Freigegeben"
|
|||||||
msgid "Shared Navigation"
|
msgid "Shared Navigation"
|
||||||
msgstr "Freigegebene Navigation"
|
msgstr "Freigegebene Navigation"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/Dashboard.php:238
|
#: /vagrant/library/Icinga/Web/Widget/Dashboard.php:235
|
||||||
#, php-format
|
#, php-format
|
||||||
msgctxt "dashboard.pane.tooltip"
|
msgctxt "dashboard.pane.tooltip"
|
||||||
msgid "Show %s"
|
msgid "Show %s"
|
||||||
@ -2133,13 +2136,13 @@ msgstr "Suche anzeigen"
|
|||||||
msgid "Show Stacktraces"
|
msgid "Show Stacktraces"
|
||||||
msgstr "Stacktrace anzeigen"
|
msgstr "Stacktrace anzeigen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/settings.phtml:61
|
#: /vagrant/application/views/scripts/dashboard/settings.phtml:62
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Show dashlet %s"
|
msgid "Show dashlet %s"
|
||||||
msgstr "Dashlet %s anzeigen"
|
msgstr "Dashlet %s anzeigen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/show.phtml:93
|
#: /vagrant/application/views/scripts/group/show.phtml:93
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:70
|
#: /vagrant/application/views/scripts/user/list.phtml:68
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Show detailed information about %s"
|
msgid "Show detailed information about %s"
|
||||||
msgstr "Zeige detaillierte Informationen über %s"
|
msgstr "Zeige detaillierte Informationen über %s"
|
||||||
@ -2149,7 +2152,7 @@ msgstr "Zeige detaillierte Informationen über %s"
|
|||||||
msgid "Show detailed information for group %s"
|
msgid "Show detailed information for group %s"
|
||||||
msgstr "Zeige detaillierte Informationen über die Gruppe %s"
|
msgstr "Zeige detaillierte Informationen über die Gruppe %s"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:72
|
#: /vagrant/application/views/scripts/group/list.phtml:69
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Show detailed information for user group %s"
|
msgid "Show detailed information for user group %s"
|
||||||
msgstr "Zeige detaillierte Informationen über die Benutzergruppe %s"
|
msgstr "Zeige detaillierte Informationen über die Benutzergruppe %s"
|
||||||
@ -2206,7 +2209,7 @@ msgstr "Sortieren nach"
|
|||||||
msgid "State"
|
msgid "State"
|
||||||
msgstr "Status"
|
msgstr "Status"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:422
|
#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:423
|
||||||
msgid "Strip this filter"
|
msgid "Strip this filter"
|
||||||
msgstr "Diesen Filter bearbeiten"
|
msgstr "Diesen Filter bearbeiten"
|
||||||
|
|
||||||
@ -2224,7 +2227,7 @@ msgid "Target"
|
|||||||
msgstr "Ziel"
|
msgstr "Ziel"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/about/index.phtml:29
|
#: /vagrant/application/views/scripts/about/index.phtml:29
|
||||||
#: /vagrant/application/views/scripts/authentication/login.phtml:19
|
#: /vagrant/application/views/scripts/authentication/login.phtml:26
|
||||||
msgid "The Icinga Project"
|
msgid "The Icinga Project"
|
||||||
msgstr "Das Icinga Projekt"
|
msgstr "Das Icinga Projekt"
|
||||||
|
|
||||||
@ -2296,7 +2299,7 @@ msgstr ""
|
|||||||
#: /vagrant/application/forms/Config/General/ThemingConfigForm.php:40
|
#: /vagrant/application/forms/Config/General/ThemingConfigForm.php:40
|
||||||
msgctxt "Form element description"
|
msgctxt "Form element description"
|
||||||
msgid "The default theme"
|
msgid "The default theme"
|
||||||
msgstr "Standardtheme"
|
msgstr "Standard-Theme"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/showConfiguration.phtml:5
|
#: /vagrant/application/views/scripts/showConfiguration.phtml:5
|
||||||
#, php-format
|
#, php-format
|
||||||
@ -2561,14 +2564,12 @@ msgstr "Der eindeutige Name dieser Ressource"
|
|||||||
|
|
||||||
#: /vagrant/application/forms/Navigation/NavigationItemForm.php:56
|
#: /vagrant/application/forms/Navigation/NavigationItemForm.php:56
|
||||||
msgid ""
|
msgid ""
|
||||||
"The url of this navigation item. Leave blank if you only want the name being "
|
"The url of this navigation item. Leave blank if only the name should be displayed. For urls with username and password and "
|
||||||
"displayed. For external urls, make sure to prepend an appropriate protocol "
|
"for all external urls, make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)"
|
||||||
"identifier (e.g. http://example.tld)"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Die URL dieses Navigationselementes. Dies kann leer gelassen werden, wenn "
|
"Die URL dieses Navigationselementes. Dies kann leer gelassen werden, wenn nur der Name angezeigt werden soll. Stelle für URLs "
|
||||||
"nur der Name angezeigt werden soll. Stellen Sie für externe URLs sicher, "
|
"mit Benutzername und Passwort sowie alle externen URLs sicher, dass eine passender Protokollnamen voran gestellt ist (z.B. "
|
||||||
"dass Sie einen passenden Protokollnamen voran stellen (z.B. http://example."
|
"http://example.tld)"
|
||||||
"tld)"
|
|
||||||
|
|
||||||
#: /vagrant/application/forms/Navigation/DashletForm.php:29
|
#: /vagrant/application/forms/Navigation/DashletForm.php:29
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -2600,7 +2601,7 @@ msgstr "Der für die Authentifizierung zu benutzende Benutzername."
|
|||||||
#: /vagrant/application/forms/PreferenceForm.php:170
|
#: /vagrant/application/forms/PreferenceForm.php:170
|
||||||
msgctxt "Form element label"
|
msgctxt "Form element label"
|
||||||
msgid "Theme"
|
msgid "Theme"
|
||||||
msgstr ""
|
msgstr "Theme"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/navigation/shared.phtml:15
|
#: /vagrant/application/views/scripts/navigation/shared.phtml:15
|
||||||
msgid "There are currently no navigation items being shared"
|
msgid "There are currently no navigation items being shared"
|
||||||
@ -2635,6 +2636,7 @@ msgid "This module has no dependencies"
|
|||||||
msgstr "Dieses Modul hat keine Abhängigkeiten"
|
msgstr "Dieses Modul hat keine Abhängigkeiten"
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Application/Modules/Module.php:721
|
#: /vagrant/library/Icinga/Application/Modules/Module.php:721
|
||||||
|
#: /vagrant/library/Icinga/Application/Modules/ModuleInfo.php:13
|
||||||
msgid "This module has no description"
|
msgid "This module has no description"
|
||||||
msgstr "Dieses Modul hat keine Beschreibung"
|
msgstr "Dieses Modul hat keine Beschreibung"
|
||||||
|
|
||||||
@ -2651,6 +2653,14 @@ msgid "Tick this box to share this item with others"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Markieren Sie dieses Kästchen, um dieses Element mit Anderen zu teilen."
|
"Markieren Sie dieses Kästchen, um dieses Element mit Anderen zu teilen."
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Dashboard/PaneForm.php:34
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "Titel"
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Dashboard/PaneForm.php:33
|
||||||
|
msgid "Title of the dashboard"
|
||||||
|
msgstr "Titel für das Dashboard"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Config/Resource/SshResourceForm.php:91
|
#: /vagrant/application/forms/Config/Resource/SshResourceForm.php:91
|
||||||
msgid "To modify the private key you must recreate this resource."
|
msgid "To modify the private key you must recreate this resource."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2708,6 +2718,10 @@ msgstr "Freigabe aufheben"
|
|||||||
msgid "Unshare this navigation item"
|
msgid "Unshare this navigation item"
|
||||||
msgstr "Freigabe dieses Navigationselementes aufheben"
|
msgstr "Freigabe dieses Navigationselementes aufheben"
|
||||||
|
|
||||||
|
#: /vagrant/application/forms/Dashboard/PaneForm.php:21
|
||||||
|
msgid "Update Dashboard"
|
||||||
|
msgstr "Dashboard aktualisieren"
|
||||||
|
|
||||||
#: /vagrant/application/controllers/DashboardController.php:86
|
#: /vagrant/application/controllers/DashboardController.php:86
|
||||||
#: /vagrant/application/controllers/DashboardController.php:92
|
#: /vagrant/application/controllers/DashboardController.php:92
|
||||||
msgid "Update Dashlet"
|
msgid "Update Dashlet"
|
||||||
@ -2752,8 +2766,7 @@ msgstr ""
|
|||||||
#: /vagrant/library/Icinga/Web/Form.php:951
|
#: /vagrant/library/Icinga/Web/Form.php:951
|
||||||
#: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
#: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upon its value has changed, this field issues an automatic update of this "
|
"Upon its value changing, this field issues an automatic update of this page."
|
||||||
"page."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Dieses Feld löst eine automatische Aktualisierung dieser Seite aus, wenn "
|
"Dieses Feld löst eine automatische Aktualisierung dieser Seite aus, wenn "
|
||||||
"sein Inhalt geändert wird."
|
"sein Inhalt geändert wird."
|
||||||
@ -2821,7 +2834,7 @@ msgstr "Backends für Benutzer"
|
|||||||
|
|
||||||
#: /vagrant/application/controllers/GroupController.php:69
|
#: /vagrant/application/controllers/GroupController.php:69
|
||||||
#: /vagrant/application/controllers/UserController.php:101
|
#: /vagrant/application/controllers/UserController.php:101
|
||||||
#: /vagrant/application/views/scripts/group/list.phtml:53
|
#: /vagrant/application/views/scripts/group/list.phtml:50
|
||||||
#: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:115
|
#: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:115
|
||||||
#: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:120
|
#: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:120
|
||||||
#: /vagrant/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:465
|
#: /vagrant/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:465
|
||||||
@ -2833,7 +2846,6 @@ msgid "User Group Backend"
|
|||||||
msgstr "Backend für Benutzergruppen"
|
msgstr "Backend für Benutzergruppen"
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/config/userbackend/reorder.phtml:19
|
#: /vagrant/application/views/scripts/config/userbackend/reorder.phtml:19
|
||||||
#, fuzzy
|
|
||||||
msgid "User Group Backends"
|
msgid "User Group Backends"
|
||||||
msgstr "Backend für Benutzergruppen"
|
msgstr "Backend für Benutzergruppen"
|
||||||
|
|
||||||
@ -2905,7 +2917,7 @@ msgstr ""
|
|||||||
"soll. Bitte beachten Sie, dass für diesen Benutzer ein schlüsselbasierter "
|
"soll. Bitte beachten Sie, dass für diesen Benutzer ein schlüsselbasierter "
|
||||||
"Zugriff möglich sein muss."
|
"Zugriff möglich sein muss."
|
||||||
|
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:293
|
#: /vagrant/library/Icinga/Web/Menu.php:294
|
||||||
msgid "Usergroups"
|
msgid "Usergroups"
|
||||||
msgstr "Benutzergruppen"
|
msgstr "Benutzergruppen"
|
||||||
|
|
||||||
@ -2915,7 +2927,7 @@ msgstr "Benutzergruppen"
|
|||||||
#: /vagrant/application/forms/Config/Resource/DbResourceForm.php:127
|
#: /vagrant/application/forms/Config/Resource/DbResourceForm.php:127
|
||||||
#: /vagrant/application/forms/Config/User/UserForm.php:33
|
#: /vagrant/application/forms/Config/User/UserForm.php:33
|
||||||
#: /vagrant/application/views/scripts/group/show.phtml:75
|
#: /vagrant/application/views/scripts/group/show.phtml:75
|
||||||
#: /vagrant/application/views/scripts/user/list.phtml:53
|
#: /vagrant/application/views/scripts/user/list.phtml:51
|
||||||
#: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:115
|
#: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:115
|
||||||
#: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:118
|
#: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:118
|
||||||
#: /vagrant/library/Icinga/Authentication/User/LdapUserBackend.php:255
|
#: /vagrant/library/Icinga/Authentication/User/LdapUserBackend.php:255
|
||||||
@ -2932,7 +2944,7 @@ msgstr "Benutzername"
|
|||||||
#: /vagrant/application/forms/Navigation/NavigationConfigForm.php:613
|
#: /vagrant/application/forms/Navigation/NavigationConfigForm.php:613
|
||||||
#: /vagrant/application/forms/Security/RoleForm.php:117
|
#: /vagrant/application/forms/Security/RoleForm.php:117
|
||||||
#: /vagrant/application/views/scripts/role/list.phtml:23
|
#: /vagrant/application/views/scripts/role/list.phtml:23
|
||||||
#: /vagrant/library/Icinga/Web/Menu.php:288
|
#: /vagrant/library/Icinga/Web/Menu.php:289
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr "Benutzer"
|
msgstr "Benutzer"
|
||||||
|
|
||||||
@ -2967,6 +2979,17 @@ msgctxt "app.config.logging.level"
|
|||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr "Warnung"
|
msgstr "Warnung"
|
||||||
|
|
||||||
|
#: /vagrant/application/views/scripts/authentication/login.phtml:5
|
||||||
|
msgid ""
|
||||||
|
"Welcome to Icinga Web 2. For users of the screen reader Jaws full and "
|
||||||
|
"expectant compliant accessibility is possible only with use of the Firefox "
|
||||||
|
"browser. VoiceOver on Mac OS X is tested on Chrome, Safari and Firefox."
|
||||||
|
msgstr ""
|
||||||
|
"Willkommen zu Icinga Web 2. Für Nutzer des Screenreader JAWS ist die "
|
||||||
|
"vollständige und erwartungskonforme Zugänglichkeit nur mit Nutzung des "
|
||||||
|
"Browsers Firefox möglich. VoiceOver unter Mac OS X ist getestet mit Chrome, "
|
||||||
|
"Safari und Firefox."
|
||||||
|
|
||||||
#: /vagrant/application/views/scripts/dashboard/index.phtml:12
|
#: /vagrant/application/views/scripts/dashboard/index.phtml:12
|
||||||
msgid "Welcome to Icinga Web!"
|
msgid "Welcome to Icinga Web!"
|
||||||
msgstr "Willkommen zu Icinga Web 2!"
|
msgstr "Willkommen zu Icinga Web 2!"
|
||||||
|
Binary file not shown.
@ -2517,16 +2517,6 @@ msgstr "Tipo del backend gruppo utenti"
|
|||||||
msgid "The unique name of this resource"
|
msgid "The unique name of this resource"
|
||||||
msgstr "Nome univoco per questa risorsa"
|
msgstr "Nome univoco per questa risorsa"
|
||||||
|
|
||||||
#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:56
|
|
||||||
msgid ""
|
|
||||||
"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)"
|
|
||||||
msgstr ""
|
|
||||||
"La url di questo oggetto di navigazione. Lasciare vuoto se si vuole solo "
|
|
||||||
"mostrare il nome. Per url esterne ricordare di anteporre l'appropriato "
|
|
||||||
"identificatore di protocollo (ex. http://esempio.tld)"
|
|
||||||
|
|
||||||
#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:29
|
#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:29
|
||||||
msgid ""
|
msgid ""
|
||||||
"The url to load in the dashlet. For external urls, make sure to prepend an "
|
"The url to load in the dashlet. For external urls, make sure to prepend an "
|
||||||
@ -2696,7 +2686,7 @@ msgstr ""
|
|||||||
#: /usr/share/php/Icinga/Web/Form.php:951
|
#: /usr/share/php/Icinga/Web/Form.php:951
|
||||||
#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upon its value has changed, this field issues an automatic update of this "
|
"Upon its value changing, this field issues an automatic update of this "
|
||||||
"page."
|
"page."
|
||||||
msgstr "Cambiando questo valore la pagina verrà ricaricata automaticamente."
|
msgstr "Cambiando questo valore la pagina verrà ricaricata automaticamente."
|
||||||
|
|
||||||
|
@ -2417,16 +2417,6 @@ msgstr ""
|
|||||||
msgid "The unique name of this resource"
|
msgid "The unique name of this resource"
|
||||||
msgstr "Уникальное название ресурса"
|
msgstr "Уникальное название ресурса"
|
||||||
|
|
||||||
#: /vagrant/application/forms/Navigation/NavigationItemForm.php:56
|
|
||||||
msgid ""
|
|
||||||
"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)"
|
|
||||||
msgstr ""
|
|
||||||
"URL адрес элемента навигации. Оставьте пустым, если необходимо только "
|
|
||||||
"отображение имени. Для внешних адресов убедитесь, что в начале ссылке указан "
|
|
||||||
"корректный протокол (например, http://example.tld)"
|
|
||||||
|
|
||||||
#: /vagrant/application/forms/Navigation/DashletForm.php:29
|
#: /vagrant/application/forms/Navigation/DashletForm.php:29
|
||||||
msgid ""
|
msgid ""
|
||||||
"The url to load in the dashlet. For external urls, make sure to prepend an "
|
"The url to load in the dashlet. For external urls, make sure to prepend an "
|
||||||
@ -2581,7 +2571,7 @@ msgstr ""
|
|||||||
#: /vagrant/library/Icinga/Web/Form.php:951
|
#: /vagrant/library/Icinga/Web/Form.php:951
|
||||||
#: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
#: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upon its value has changed, this field issues an automatic update of this "
|
"Upon its value changing, this field issues an automatic update of this "
|
||||||
"page."
|
"page."
|
||||||
msgstr "В случае изменения значения поля страница будет обновлена"
|
msgstr "В случае изменения значения поля страница будет обновлена"
|
||||||
|
|
||||||
|
@ -2,13 +2,7 @@
|
|||||||
<?= $tabs ?>
|
<?= $tabs ?>
|
||||||
</div>
|
</div>
|
||||||
<div id="about" class="content content-centered">
|
<div id="about" class="content content-centered">
|
||||||
<?= $this->img(
|
<?= $this->img('img/icinga-logo-big-dark.png', null, array('width' => 320)) ?>
|
||||||
'img/logo_icinga_big_dark.png',
|
|
||||||
null,
|
|
||||||
array(
|
|
||||||
'width' => 320
|
|
||||||
)
|
|
||||||
) ?>
|
|
||||||
<dl class="name-value-list">
|
<dl class="name-value-list">
|
||||||
<?php if (isset($version['appVersion'])): ?>
|
<?php if (isset($version['appVersion'])): ?>
|
||||||
<dt><?= $this->translate('Version') ?></dt>
|
<dt><?= $this->translate('Version') ?></dt>
|
||||||
@ -82,7 +76,7 @@
|
|||||||
null,
|
null,
|
||||||
array(
|
array(
|
||||||
'target' => '_blank',
|
'target' => '_blank',
|
||||||
'icon' => 'chat',
|
'icon' => 'chat-empty',
|
||||||
'title' => $this->translate('Support / Mailinglists')
|
'title' => $this->translate('Support / Mailinglists')
|
||||||
)
|
)
|
||||||
) ?>
|
) ?>
|
||||||
|
11
application/views/scripts/account/index.phtml
Normal file
11
application/views/scripts/account/index.phtml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="controls">
|
||||||
|
<?= $tabs ?>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<?php if (isset($changePasswordForm)): ?>
|
||||||
|
<h1><?= $this->translate('Account') ?></h1>
|
||||||
|
<?= $changePasswordForm ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<h1><?= $this->translate('Preferences') ?></h1>
|
||||||
|
<?= $form ?>
|
||||||
|
</div>
|
61
application/views/scripts/announcements/index.phtml
Normal file
61
application/views/scripts/announcements/index.phtml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php if (! $compact): ?>
|
||||||
|
<div class="controls">
|
||||||
|
<?= $tabs ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="content">
|
||||||
|
<?php if ($this->hasPermission('admin')) {
|
||||||
|
echo $this->qlink(
|
||||||
|
$this->translate('Create a New Announcement') ,
|
||||||
|
'announcements/new',
|
||||||
|
null,
|
||||||
|
array(
|
||||||
|
'class' => 'button-link',
|
||||||
|
'data-base-target' => '_next',
|
||||||
|
'icon' => 'plus',
|
||||||
|
'title' => $this->translate('Create a new announcement')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} ?>
|
||||||
|
<?php if (! $announcements->hasResult()): ?>
|
||||||
|
<p><?= $this->translate('No announcements found.') ?></p>
|
||||||
|
</div>
|
||||||
|
<?php return; endif ?>
|
||||||
|
<table data-base-target="_next" class="table-row-selectable common-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?= $this->translate('Author') ?></th>
|
||||||
|
<th><?= $this->translate('Message') ?></th>
|
||||||
|
<th><?= $this->translate('Start') ?></th>
|
||||||
|
<th><?= $this->translate('End') ?></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($announcements as $announcement): /** @var object $announcement */ ?>
|
||||||
|
<?php if ($this->hasPermission('admin')): ?>
|
||||||
|
<tr href="<?= $this->href('announcements/update', array('id' => $announcement->id)) ?>">
|
||||||
|
<?php else: ?>
|
||||||
|
<tr>
|
||||||
|
<?php endif ?>
|
||||||
|
<td><?= $this->escape($announcement->author) ?></td>
|
||||||
|
<td><?= $this->ellipsis($announcement->message, 100) ?></td>
|
||||||
|
<td><?= $this->formatDateTime($announcement->start->getTimestamp()) ?></td>
|
||||||
|
<td><?= $this->formatDateTime($announcement->end->getTimestamp()) ?></td>
|
||||||
|
<?php if ($this->hasPermission('admin')): ?>
|
||||||
|
<td class="icon-col"><?= $this->qlink(
|
||||||
|
null,
|
||||||
|
'announcements/remove',
|
||||||
|
array('id' => $announcement->id),
|
||||||
|
array(
|
||||||
|
'class' => 'action-link',
|
||||||
|
'icon' => 'cancel',
|
||||||
|
'title' => $this->translate('Remove this announcement')
|
||||||
|
)
|
||||||
|
) ?></td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
@ -1,5 +1,12 @@
|
|||||||
<div id="login" class="centered-ghost">
|
<div id="login" class="centered-ghost">
|
||||||
<div class="centered-content" data-base-target="layout">
|
<div class="centered-content" data-base-target="layout">
|
||||||
|
<div role="status" class="sr-only">
|
||||||
|
<?= $this->translate(
|
||||||
|
'Welcome to Icinga Web 2. For users of the screen reader Jaws full and expectant compliant'
|
||||||
|
. ' accessibility is possible only with use of the Firefox browser. VoiceOver on Mac OS X is tested on'
|
||||||
|
. ' Chrome, Safari and Firefox.'
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
<div id="icinga-logo" aria-hidden="true"></div>
|
<div id="icinga-logo" aria-hidden="true"></div>
|
||||||
<?php if ($requiresSetup): ?>
|
<?php if ($requiresSetup): ?>
|
||||||
<p class="config-note"><?= sprintf(
|
<p class="config-note"><?= sprintf(
|
||||||
|
@ -37,10 +37,20 @@
|
|||||||
</td>
|
</td>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?= $this->escape($this->translate('Version')) ?></th>
|
<th><?= $this->escape($this->translate('Version')) ?></th>
|
||||||
<td><?= $this->escape($module->getVersion()) ?></td></tr>
|
<td><?= $this->escape($module->getVersion()) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php if (isset($moduleGitCommitId) && $moduleGitCommitId !== false): ?>
|
||||||
|
<tr>
|
||||||
|
<th><?= $this->escape($this->translate('Git commit')) ?></th>
|
||||||
|
<td><?= $this->escape($moduleGitCommitId) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?= $this->escape($this->translate('Description')) ?></th>
|
<th><?= $this->escape($this->translate('Description')) ?></th>
|
||||||
<td><?= nl2br($this->escape($module->getDescription())) ?></td>
|
<td>
|
||||||
|
<strong><?= $this->escape($module->getTitle()) ?></strong><br>
|
||||||
|
<?= nl2br($this->escape($module->getDescription())) ?>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?= $this->escape($this->translate('Dependencies')) ?></th>
|
<th><?= $this->escape($this->translate('Dependencies')) ?></th>
|
||||||
|
1
application/views/scripts/layout/announcements.phtml
Normal file
1
application/views/scripts/layout/announcements.phtml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?= $this->widget('announcements') ?>
|
@ -6,11 +6,11 @@ $searchDashboard = new SearchDashboard();
|
|||||||
$searchDashboard->setUser($this->Auth()->getUser());
|
$searchDashboard->setUser($this->Auth()->getUser());
|
||||||
|
|
||||||
if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
|
if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
|
||||||
<form action="<?= $this->href('search') ?>" method="get" role="search">
|
<form action="<?= $this->href('search') ?>" method="get" role="search" class="search-control">
|
||||||
<input
|
<input type="text" name="q" id="search" class="search search-input"
|
||||||
type="text" name="q" id="search" class="search" placeholder="<?= $this->translate('Search') ?> …"
|
placeholder="<?= $this->translate('Search') ?> …"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
|
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" required="required">
|
||||||
/>
|
<button class="search-reset icon-cancel" type="reset"></button>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?= $menuRenderer->setCssClass('primary-nav')->setElementTag('nav')->setHeading(t('Navigation')); ?>
|
<?= $menuRenderer->setCssClass('primary-nav')->setElementTag('nav')->setHeading(t('Navigation')); ?>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $tabs; ?>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<?= $form; ?>
|
|
||||||
</div>
|
|
47
changelog.py
47
changelog.py
@ -16,7 +16,7 @@
|
|||||||
# along with this program; if not, write to the Free Software Foundation
|
# along with this program; if not, write to the Free Software Foundation
|
||||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
import urllib2, json, sys, string
|
import urllib2, json, sys, string, collections
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
DESCRIPTION="update release changes"
|
DESCRIPTION="update release changes"
|
||||||
@ -43,14 +43,14 @@ def format_header(text, lvl, ftype = ftype):
|
|||||||
def format_logentry(log_entry, args = args, issue_url = ISSUE_URL):
|
def format_logentry(log_entry, args = args, issue_url = ISSUE_URL):
|
||||||
if args.links:
|
if args.links:
|
||||||
if args.html:
|
if args.html:
|
||||||
return "<li> {0} <a href=\"{3}{1}\">{1}</a>: {2}</li>".format(log_entry[0], log_entry[1], log_entry[2], issue_url)
|
return "<li> {0} <a href=\"{4}{1}\">{1}</a> ({2}): {3}</li>".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3],issue_url)
|
||||||
else:
|
else:
|
||||||
return "* {0} [{1}]({3}{1} \"{0} {1}\"): {2}".format(log_entry[0], log_entry[1], log_entry[2], issue_url)
|
return "* {0} [{1}]({4}{1} \"{0} {1}\") ({2}): {3}".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3], issue_url)
|
||||||
else:
|
else:
|
||||||
if args.html:
|
if args.html:
|
||||||
return "<li>%s %d: %s</li>" % log_entry
|
return "<li>%s %d (%s): %s</li>" % log_entry
|
||||||
else:
|
else:
|
||||||
return "* %s %d: %s" % log_entry
|
return "* %s %d (%s): %s" % log_entry
|
||||||
|
|
||||||
def print_category(category, entries):
|
def print_category(category, entries):
|
||||||
if len(entries) > 0:
|
if len(entries) > 0:
|
||||||
@ -60,8 +60,11 @@ def print_category(category, entries):
|
|||||||
if args.html:
|
if args.html:
|
||||||
print "<ul>"
|
print "<ul>"
|
||||||
|
|
||||||
for entry in sorted(entries):
|
tmp_entries = collections.OrderedDict(sorted(entries.items()))
|
||||||
print format_logentry(entry)
|
|
||||||
|
for cat, entry_list in tmp_entries.iteritems():
|
||||||
|
for entry in entry_list:
|
||||||
|
print format_logentry(entry)
|
||||||
|
|
||||||
if args.html:
|
if args.html:
|
||||||
print "</ul>"
|
print "</ul>"
|
||||||
@ -108,9 +111,10 @@ if changes:
|
|||||||
|
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
features = []
|
features = {}
|
||||||
bugfixes = []
|
bugfixes = {}
|
||||||
support = []
|
support = {}
|
||||||
|
category = ""
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# We could filter using &cf_13=1, however this doesn't currently work because the custom field isn't set
|
# We could filter using &cf_13=1, however this doesn't currently work because the custom field isn't set
|
||||||
@ -135,14 +139,29 @@ while True:
|
|||||||
if ignore_issue:
|
if ignore_issue:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entry = (issue["tracker"]["name"], issue["id"], issue["subject"].strip())
|
if "category" in issue:
|
||||||
|
category = str(issue["category"]["name"])
|
||||||
|
else:
|
||||||
|
category = "no category"
|
||||||
|
|
||||||
|
# the order is important for print_category()
|
||||||
|
entry = (issue["tracker"]["name"], issue["id"], category, issue["subject"].strip())
|
||||||
|
|
||||||
if issue["tracker"]["name"] == "Feature":
|
if issue["tracker"]["name"] == "Feature":
|
||||||
features.append(entry)
|
try:
|
||||||
|
features[category].append(entry)
|
||||||
|
except KeyError:
|
||||||
|
features[category] = [ entry ]
|
||||||
elif issue["tracker"]["name"] == "Bug":
|
elif issue["tracker"]["name"] == "Bug":
|
||||||
bugfixes.append(entry)
|
try:
|
||||||
|
bugfixes[category].append(entry)
|
||||||
|
except KeyError:
|
||||||
|
bugfixes[category] = [ entry ]
|
||||||
elif issue["tracker"]["name"] == "Support":
|
elif issue["tracker"]["name"] == "Support":
|
||||||
support.append(entry)
|
try:
|
||||||
|
support[category].append(entry)
|
||||||
|
except KeyError:
|
||||||
|
support[category] = [ entry ]
|
||||||
|
|
||||||
print_category("Feature", features)
|
print_category("Feature", features)
|
||||||
print_category("Bugfixes", bugfixes)
|
print_category("Bugfixes", bugfixes)
|
||||||
|
@ -19,20 +19,20 @@ Most such actions (like rescheduling a check) can be done with just a single cli
|
|||||||
Icinga Web 2 can be installed easily from packages from the official package repositories.
|
Icinga Web 2 can be installed easily from packages from the official package repositories.
|
||||||
Setting it up is also easy with the web based setup wizard.
|
Setting it up is also easy with the web based setup wizard.
|
||||||
|
|
||||||
See [here](installation#installation) for more information about the installation.
|
See [here](02-Installation.md#installation) for more information about the installation.
|
||||||
|
|
||||||
## <a id="about-configuration"></a> Configuration
|
## <a id="about-configuration"></a> Configuration
|
||||||
|
|
||||||
Icinga Web 2 can be configured via the user interface and .ini files.
|
Icinga Web 2 can be configured via the user interface and .ini files.
|
||||||
|
|
||||||
See [here](configuration#configuration) for more information about the configuration.
|
See [here](03-Configuration.md#configuration) for more information about the configuration.
|
||||||
|
|
||||||
## <a id="about-authentication"></a> Authentication
|
## <a id="about-authentication"></a> Authentication
|
||||||
|
|
||||||
With Icinga Web 2 you can authenticate against relational databases, LDAP and more.
|
With Icinga Web 2 you can authenticate against relational databases, LDAP and more.
|
||||||
These authentication methods can be easily configured (via the corresponding .ini file).
|
These authentication methods can be easily configured (via the corresponding .ini file).
|
||||||
|
|
||||||
See [here](authentication#authentication) for more information about
|
See [here](05-Authentication.md#authentication) for more information about
|
||||||
the different authentication methods available and how to configure them.
|
the different authentication methods available and how to configure them.
|
||||||
|
|
||||||
## <a id="about-authorization"></a> Authorization
|
## <a id="about-authorization"></a> Authorization
|
||||||
@ -41,7 +41,7 @@ In Icinga Web 2 there are permissions and restrictions to allow and deny (respec
|
|||||||
roles to view or to do certain things.
|
roles to view or to do certain things.
|
||||||
These roles can be assigned to users and groups.
|
These roles can be assigned to users and groups.
|
||||||
|
|
||||||
See [here](security#security) for more information about authorization
|
See [here](06-Security.md#security) for more information about authorization
|
||||||
and how to configure roles.
|
and how to configure roles.
|
||||||
|
|
||||||
## <a id="about-preferences"></a> User preferences
|
## <a id="about-preferences"></a> User preferences
|
||||||
@ -50,7 +50,7 @@ Besides the global configuration each user has individual configuration options
|
|||||||
like the interface's language or the current timezone.
|
like the interface's language or the current timezone.
|
||||||
They can be stored either in a database or in .ini files.
|
They can be stored either in a database or in .ini files.
|
||||||
|
|
||||||
See [here](preferences#preferences) for more information about a user's preferences
|
See [here](07-Preferences.md#preferences) for more information about a user's preferences
|
||||||
and how to configure their storage type.
|
and how to configure their storage type.
|
||||||
|
|
||||||
## <a id="about-documentation"></a> Documentation
|
## <a id="about-documentation"></a> Documentation
|
||||||
|
@ -15,6 +15,7 @@ thoroughly.
|
|||||||
* Icinga 1.x w/ IDO; Icinga 2.x w/ IDO feature enabled
|
* Icinga 1.x w/ IDO; Icinga 2.x w/ IDO feature enabled
|
||||||
* The IDO table prefix must be icinga_ which is the default
|
* The IDO table prefix must be icinga_ which is the default
|
||||||
* MySQL or PostgreSQL PHP libraries
|
* MySQL or PostgreSQL PHP libraries
|
||||||
|
* cURL PHP library when using the Icinga 2 API for transmitting external commands
|
||||||
|
|
||||||
### <a id="pagespeed-incompatibility"></a> PageSpeed Module Incompatibility
|
### <a id="pagespeed-incompatibility"></a> PageSpeed Module Incompatibility
|
||||||
|
|
||||||
@ -35,16 +36,16 @@ pagespeed Disallow "*/icingaweb2/*";
|
|||||||
|
|
||||||
Below is a list of official package repositories for installing Icinga Web 2 for various operating systems.
|
Below is a list of official package repositories for installing Icinga Web 2 for various operating systems.
|
||||||
|
|
||||||
Distribution | Repository
|
| Distribution | Repository |
|
||||||
------------------------|---------------------------
|
| ------------- | ---------- |
|
||||||
Debian | [debmon](http://debmon.org/packages/debmon-wheezy/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/)
|
| Debian | [Icinga Repository](http://packages.icinga.org/debian/) |
|
||||||
Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/)
|
| Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/) |
|
||||||
RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/)
|
| RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) |
|
||||||
openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/)
|
| openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) |
|
||||||
SLES | [Icinga Repository](http://packages.icinga.org/SUSE/)
|
| SLES | [Icinga Repository](http://packages.icinga.org/SUSE/) |
|
||||||
Gentoo | -
|
| Gentoo | [Upstream](https://packages.gentoo.org/packages/www-apps/icingaweb2) |
|
||||||
FreeBSD | -
|
| FreeBSD | [Upstream](http://portsmon.freebsd.org/portoverview.py?category=net-mgmt&portname=icingaweb2) |
|
||||||
ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2)
|
| ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2) |
|
||||||
|
|
||||||
Packages for distributions other than the ones listed above may also be available.
|
Packages for distributions other than the ones listed above may also be available.
|
||||||
Please contact your distribution packagers.
|
Please contact your distribution packagers.
|
||||||
@ -52,23 +53,29 @@ Please contact your distribution packagers.
|
|||||||
### <a id="package-repositories"></a> Setting up Package Repositories
|
### <a id="package-repositories"></a> Setting up Package Repositories
|
||||||
|
|
||||||
You need to add the Icinga repository to your package management configuration for installing Icinga Web 2.
|
You need to add the Icinga repository to your package management configuration for installing Icinga Web 2.
|
||||||
Below is a list with examples for various distributions.
|
If you've already configured your OS to use the Icinga repository for installing Icinga 2, you may skip this step.
|
||||||
|
Below is a list with **examples** for various distributions.
|
||||||
|
|
||||||
**Debian (debmon)**:
|
**Debian Jessie**:
|
||||||
```
|
|
||||||
wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add -
|
|
||||||
echo 'deb http://debmon.org/debmon debmon-wheezy main' >/etc/apt/sources.list.d/debmon.list
|
|
||||||
apt-get update
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ubuntu Trusty**:
|
|
||||||
```
|
```
|
||||||
wget -O - http://packages.icinga.org/icinga.key | apt-key add -
|
wget -O - http://packages.icinga.org/icinga.key | apt-key add -
|
||||||
add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-trusty main'
|
echo 'deb http://packages.icinga.org/debian icinga-jessie main' >/etc/apt/sources.list.d/icinga.list
|
||||||
apt-get update
|
apt-get update
|
||||||
```
|
```
|
||||||
|
|
||||||
For other Ubuntu versions just replace trusty with your distribution\'s code name.
|
> INFO
|
||||||
|
>
|
||||||
|
> For other Debian versions just replace jessie with your distribution's code name.
|
||||||
|
|
||||||
|
**Ubuntu Xenial**:
|
||||||
|
```
|
||||||
|
wget -O - http://packages.icinga.org/icinga.key | apt-key add -
|
||||||
|
add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-xenial main'
|
||||||
|
apt-get update
|
||||||
|
```
|
||||||
|
> INFO
|
||||||
|
>
|
||||||
|
> For other Ubuntu versions just replace xenial with your distribution's code name.
|
||||||
|
|
||||||
**RHEL and CentOS**:
|
**RHEL and CentOS**:
|
||||||
```
|
```
|
||||||
@ -108,14 +115,7 @@ The packages for RHEL/CentOS depend on other packages which are distributed as p
|
|||||||
[EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following
|
[EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following
|
||||||
[these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F).
|
[these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F).
|
||||||
|
|
||||||
> Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and
|
> Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and PostgreSQL.
|
||||||
> PostgreSQL.
|
|
||||||
|
|
||||||
#### <a id="package-repositories-wheezy-notes"></a> Debian wheezy Notes
|
|
||||||
|
|
||||||
The packages for Debian wheezy depend on other packages which are distributed as part of the
|
|
||||||
[wheezy-backports](http://backports.debian.org/) repository. Please make sure to enable this repository by following
|
|
||||||
[these instructions](http://backports.debian.org/Instructions/).
|
|
||||||
|
|
||||||
### <a id="installing-from-package-example"></a> Installing Icinga Web 2
|
### <a id="installing-from-package-example"></a> Installing Icinga Web 2
|
||||||
|
|
||||||
@ -126,7 +126,6 @@ Below is a list with examples for various distributions. The additional package
|
|||||||
```
|
```
|
||||||
apt-get install icingaweb2
|
apt-get install icingaweb2
|
||||||
```
|
```
|
||||||
For Debian wheezy please read the [package repositories notes](#package-repositories-wheezy-notes).
|
|
||||||
|
|
||||||
**RHEL, CentOS and Fedora**:
|
**RHEL, CentOS and Fedora**:
|
||||||
```
|
```
|
||||||
@ -449,24 +448,24 @@ path = "/var/run/icinga2/cmd/icinga2.cmd"
|
|||||||
Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/icingaweb2`.
|
Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/icingaweb2`.
|
||||||
|
|
||||||
|
|
||||||
# <a id="upgrading"></a> Upgrading Icinga Web 2
|
## <a id="upgrading"></a> Upgrading Icinga Web 2
|
||||||
|
|
||||||
## <a id="upgrading-to-2.3.x"></a> Upgrading to Icinga Web 2 2.3.x
|
### <a id="upgrading-to-2.3.x"></a> Upgrading to Icinga Web 2 2.3.x
|
||||||
|
|
||||||
* Icinga Web 2 version 2.3.x does not introduce any backward incompatible change.
|
* Icinga Web 2 version 2.3.x does not introduce any backward incompatible change.
|
||||||
|
|
||||||
## <a id="upgrading-to-2.2.0"></a> Upgrading to Icinga Web 2 2.2.0
|
### <a id="upgrading-to-2.2.0"></a> Upgrading to Icinga Web 2 2.2.0
|
||||||
|
|
||||||
* The menu entry `Authorization` beneath `Config` has been renamed to `Authentication`. The role, user backend and user
|
* The menu entry `Authorization` beneath `Config` has been renamed to `Authentication`. The role, user backend and user
|
||||||
group backend configuration which was previously found beneath `Authentication` has been moved to `Application`.
|
group backend configuration which was previously found beneath `Authentication` has been moved to `Application`.
|
||||||
|
|
||||||
## <a id="upgrading-to-2.1.x"></a> Upgrading to Icinga Web 2 2.1.x
|
### <a id="upgrading-to-2.1.x"></a> Upgrading to Icinga Web 2 2.1.x
|
||||||
|
|
||||||
* Since Icinga Web 2 version 2.1.3 LDAP user group backends respect the configuration option `group_filter`.
|
* Since Icinga Web 2 version 2.1.3 LDAP user group backends respect the configuration option `group_filter`.
|
||||||
Users who changed the configuration manually and used the option `filter` instead
|
Users who changed the configuration manually and used the option `filter` instead
|
||||||
have to change it back to `group_filter`.
|
have to change it back to `group_filter`.
|
||||||
|
|
||||||
## <a id="upgrading-to-2.0.0"></a> Upgrading to Icinga Web 2 2.0.0
|
### <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
|
* 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
|
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
|
||||||
@ -488,7 +487,7 @@ Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/ici
|
|||||||
**<config-dir>/preferences/<username>/config.ini**.
|
**<config-dir>/preferences/<username>/config.ini**.
|
||||||
The content of the file remains unchanged.
|
The content of the file remains unchanged.
|
||||||
|
|
||||||
## <a id="upgrading-to-rc1"></a> Upgrading to Icinga Web 2 Release Candidate 1
|
### <a id="upgrading-to-rc1"></a> Upgrading to Icinga Web 2 Release Candidate 1
|
||||||
|
|
||||||
The first release candidate of Icinga Web 2 introduces the following non-backward compatible changes:
|
The first release candidate of Icinga Web 2 introduces the following non-backward compatible changes:
|
||||||
|
|
||||||
@ -507,12 +506,12 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
|
|||||||
predefined subset of filter columns. Please see the module's security
|
predefined subset of filter columns. Please see the module's security
|
||||||
related documentation for more details.
|
related documentation for more details.
|
||||||
|
|
||||||
## <a id="upgrading-to-beta3"></a> Upgrading to Icinga Web 2 Beta 3
|
### <a id="upgrading-to-beta3"></a> Upgrading to Icinga Web 2 Beta 3
|
||||||
|
|
||||||
Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your
|
Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your
|
||||||
configuration files after upgrading to Icinga Web 2 Beta 3.
|
configuration files after upgrading to Icinga Web 2 Beta 3.
|
||||||
|
|
||||||
## <a id="upgrading-to-beta2"></a> Upgrading to Icinga Web 2 Beta 2
|
### <a id="upgrading-to-beta2"></a> Upgrading to Icinga Web 2 Beta 2
|
||||||
|
|
||||||
Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2,
|
Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2,
|
||||||
you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following
|
you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following
|
||||||
@ -526,5 +525,6 @@ permissions = "*"
|
|||||||
After please log out from Icinga Web 2 and log in again for having all permissions granted.
|
After please log out from Icinga Web 2 and log in again for having all permissions granted.
|
||||||
|
|
||||||
If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external`
|
If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external`
|
||||||
authentication backend to be able to log in again. The new name better reflects what’s going on. A similar change
|
authentication backend to be able to log in again. The new name better reflects
|
||||||
|
what's going on. A similar change
|
||||||
affects environments that opted for not storing preferences, your new backend is `none`.
|
affects environments that opted for not storing preferences, your new backend is `none`.
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
# <a id="configuration"></a> Configuration
|
# <a id="configuration"></a> Configuration
|
||||||
|
|
||||||
## Overview
|
## <a id="configuration-overview"></a> Overview
|
||||||
|
|
||||||
Apart from its web configuration capabilities, the local configuration is
|
Apart from its web configuration capabilities, the local configuration is
|
||||||
stored in `/etc/icingaweb2` by default (depending on your config setup).
|
stored in `/etc/icingaweb2` by default (depending on your config setup).
|
||||||
|
|
||||||
File/Directory | Description
|
| File/Directory | Description/Purpose |
|
||||||
---------------------------------------------------------
|
| ------------------------------------------------- | ------------------- |
|
||||||
config.ini | General configuration (logging, preferences)
|
| **config.ini** | general configuration (logging, preferences, etc.) |
|
||||||
[resources.ini](04-Ressources.md) | Global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database)
|
| [**resources.ini**](04-Ressources.md) | global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database) |
|
||||||
roles.ini | User specific roles (e.g. `administrators`) and permissions
|
| **roles.ini** | user specific roles (e.g. `administrators`) and permissions |
|
||||||
[authentication.ini](05-Authentication.md) | Authentication backends (e.g. database)
|
| [**authentication.ini**](05-Authentication.md) | authentication backends (e.g. database) |
|
||||||
enabledModules | Contains symlinks to enabled modules
|
| **enabledModules** | contains symlinks to enabled modules |
|
||||||
modules | Directory for module specific configuration
|
| **modules** | directory for module specific configuration |
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# <a id="resources"></a> Resources
|
# <a id="resources"></a> Resources
|
||||||
|
|
||||||
The INI configuration file **config/resources.ini** contains information about data sources that can be referenced in other
|
The configuration file `config/resources.ini` contains information about data sources that can be referenced in other
|
||||||
configuration files. This allows you to manage all data sources at one central place, avoiding the need to edit several
|
configuration files. This allows you to manage all data sources at one central place, avoiding the need to edit several
|
||||||
different files, when the information about a data source changes.
|
different files, when the information about a data source changes.
|
||||||
|
|
||||||
## <a id="resources-configuration"></a> Configuration
|
## <a id="resources-configuration"></a> Configuration
|
||||||
|
|
||||||
Each section in **config/resources.ini** represents a data source with the section name being the identifier used to
|
Each section in `config/resources.ini` represents a data source with the section name being the identifier used to
|
||||||
reference this specific data source. Depending on the data source type, the sections define different directives.
|
reference this specific data source. Depending on the data source type, the sections define different directives.
|
||||||
The available data source types are *db*, *ldap*, *ssh* and *livestatus* which will described in detail in the following
|
The available data source types are *db*, *ldap*, *ssh* and *livestatus* which will described in detail in the following
|
||||||
paragraphs.
|
paragraphs.
|
||||||
@ -16,19 +16,19 @@ paragraphs.
|
|||||||
A Database resource defines a connection to a SQL databases which can contain users and groups
|
A Database resource defines a connection to a SQL databases which can contain users and groups
|
||||||
to handle authentication and authorization, monitoring data or user preferences.
|
to handle authentication and authorization, monitoring data or user preferences.
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
----------------|------------
|
| ------------- | ----------- |
|
||||||
**type** | `db`
|
| **type** | `db` |
|
||||||
**db** | Database management system. In most cases `mysql` or `pgsql`.
|
| **db** | Database management system. In most cases `mysql` or `pgsql`. |
|
||||||
**host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL.
|
| **host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL. |
|
||||||
**port** | Port number to use. Mandatory for connections to a PostgreSQL database.
|
| **port** | Port number to use. Mandatory for connections to a PostgreSQL database. |
|
||||||
**username** | The username to use when connecting to the server.
|
| **username** | The username to use when connecting to the server. |
|
||||||
**password** | The password to use when connecting to the server.
|
| **password** | The password to use when connecting to the server. |
|
||||||
**dbname** | The database to use.
|
| **dbname** | The database to use. |
|
||||||
|
|
||||||
**Example:**
|
#### <a id="resources-configuration-database-example"></a> Example
|
||||||
|
|
||||||
````
|
```
|
||||||
[icingaweb-mysql-tcp]
|
[icingaweb-mysql-tcp]
|
||||||
type = db
|
type = db
|
||||||
db = mysql
|
db = mysql
|
||||||
@ -60,60 +60,45 @@ dbname = icingaweb
|
|||||||
|
|
||||||
A LDAP resource represents a tree in a LDAP directory. LDAP is usually used for authentication and authorization.
|
A LDAP resource represents a tree in a LDAP directory. LDAP is usually used for authentication and authorization.
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
----------------|------------
|
| ----------------- | ----------- |
|
||||||
**type** | `ldap`
|
| **type** | `ldap` |
|
||||||
**hostname** | Connect to the LDAP server on the given host.
|
| **hostname** | Connect to the LDAP server on the given host. |
|
||||||
**port** | Port number to use for the connection.
|
| **port** | Port number to use for the connection. |
|
||||||
**root_dn** | Root object of the tree, e.g. "ou=people,dc=icinga,dc=org"
|
| **root_dn** | Root object of the tree, e.g. `ou=people,dc=icinga,dc=org` |
|
||||||
**bind_dn** | The user to use when connecting to the server.
|
| **bind_dn** | The user to use when connecting to the server. |
|
||||||
**bind_pw** | The password to use when connecting to the server.
|
| **bind_pw** | The password to use when connecting to the server. |
|
||||||
|
| **encryption** | Type of encryption to use: `none` (default), `starttls`, `ldaps`. |
|
||||||
|
|
||||||
**Example:**
|
#### <a id="resources-configuration-ldap-example"></a> Example
|
||||||
|
|
||||||
````
|
```
|
||||||
[ad]
|
[ad]
|
||||||
type = ldap
|
type = ldap
|
||||||
hostname = localhost
|
hostname = localhost
|
||||||
port = 389
|
port = 389
|
||||||
root_dn = "ou=people,dc=icinga,dc=org"
|
root_dn = "ou=people,dc=icinga,dc=org"
|
||||||
bind_dn = "cn=admin,ou=people,dc=icinga,dc=org"
|
bind_dn = "cn=admin,ou=people,dc=icinga,dc=org"
|
||||||
bind_pw = admin`
|
bind_pw = admin
|
||||||
````
|
```
|
||||||
|
|
||||||
### <a id="resources-configuration-ssh"></a> SSH
|
### <a id="resources-configuration-ssh"></a> SSH
|
||||||
|
|
||||||
A SSH resource contains the information about the user and the private key location, which can be used for the key-based
|
A SSH resource contains the information about the user and the private key location, which can be used for the key-based
|
||||||
ssh authentication.
|
ssh authentication.
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
--------------------|------------
|
| ----------------- | ----------- |
|
||||||
**type** | `ssh`
|
| **type** | `ssh` |
|
||||||
**user** | The username to use when connecting to the server.
|
| **user** | The username to use when connecting to the server. |
|
||||||
**private_key** | The path to the private key of the user.
|
| **private_key** | The path to the private key of the user. |
|
||||||
|
|
||||||
**Example:**
|
#### <a id="resources-configuration-ssh-example"></a> Example
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
````
|
|
||||||
[ssh]
|
[ssh]
|
||||||
type = "ssh"
|
type = "ssh"
|
||||||
user = "ssh-user"
|
user = "ssh-user"
|
||||||
private_key = "/etc/icingaweb2/ssh/ssh-user"
|
private_key = "/etc/icingaweb2/ssh/ssh-user"
|
||||||
````
|
```
|
||||||
|
|
||||||
### <a id="resources-configuration-livestatus"></a> Livestatus
|
|
||||||
|
|
||||||
A Livestatus resource represents the location of a Livestatus socket which is used for fetching monitoring data.
|
|
||||||
|
|
||||||
Directive | Description
|
|
||||||
----------------|------------
|
|
||||||
**type** | `livestatus`
|
|
||||||
**socket** | Location of the Livestatus socket. Either a path to a local Livestatus socket or a path to a remote Livestatus socket in the format `tcp://<host>:<port>`.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
````
|
|
||||||
[livestatus]
|
|
||||||
type = livestatus
|
|
||||||
socket = /var/run/icinga2/cmd/livestatus
|
|
||||||
````
|
|
||||||
|
@ -68,13 +68,13 @@ Active Directory or LDAP configuration method.
|
|||||||
|
|
||||||
### <a id="authentication-configuration-ldap-authentication"></a> LDAP
|
### <a id="authentication-configuration-ldap-authentication"></a> LDAP
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
------------------------|------------
|
| ------------------------- | ----------- |
|
||||||
**backend** | `ldap`
|
| **backend** | `ldap` |
|
||||||
**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources).
|
| **resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). |
|
||||||
**user_class** | LDAP user class.
|
| **user_class** | LDAP user class. |
|
||||||
**user_name_attribute** | LDAP attribute which contains the username.
|
| **user_name_attribute** | LDAP attribute which contains the username. |
|
||||||
**filter** | LDAP search filter.
|
| **filter** | LDAP search filter. |
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
@ -93,10 +93,10 @@ with Icinga Web 2 (e.g. an alias) no matter what the primary user id might actua
|
|||||||
|
|
||||||
### <a id="authentication-configuration-ad-authentication"></a> Active Directory
|
### <a id="authentication-configuration-ad-authentication"></a> Active Directory
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
------------------------|------------
|
| ------------- | ----------- |
|
||||||
**backend** | `msldap`
|
| **backend** | `msldap` |
|
||||||
**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources).
|
| **resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). |
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
@ -112,10 +112,10 @@ If you want to authenticate against a MySQL or a PostgreSQL database, you have t
|
|||||||
[database resource](04-Resources.md#resources-configuration-database) which will be referenced as data source for the database
|
[database resource](04-Resources.md#resources-configuration-database) which will be referenced as data source for the database
|
||||||
authentication method.
|
authentication method.
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
------------------------|------------
|
| ------------------------| ----------- |
|
||||||
**backend** | `db`
|
| **backend** | `db` |
|
||||||
**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources).
|
| **resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). |
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
|
@ -11,14 +11,14 @@ environment they are in charge of.
|
|||||||
This chapter will describe how to do the security configuration of Icinga Web 2
|
This chapter will describe how to do the security configuration of Icinga Web 2
|
||||||
and how to apply permissions and restrictions to users or groups of users.
|
and how to apply permissions and restrictions to users or groups of users.
|
||||||
|
|
||||||
## Basics
|
## <a id="security-basics"></a> Basics
|
||||||
|
|
||||||
Icinga Web 2 access control is done by defining **roles** that associate permissions
|
Icinga Web 2 access control is done by defining **roles** that associate permissions
|
||||||
and restrictions with **users** and **groups**. There are two general kinds of
|
and restrictions with **users** and **groups**. There are two general kinds of
|
||||||
things to which access can be managed: actions and objects.
|
things to which access can be managed: actions and objects.
|
||||||
|
|
||||||
|
|
||||||
### Actions
|
### <a id="security-basics-actions"></a>Actions
|
||||||
|
|
||||||
Actions are all the things an Icinga Web 2 user can do, like changing a certain configuration,
|
Actions are all the things an Icinga Web 2 user can do, like changing a certain configuration,
|
||||||
changing permissions or sending a command to the Icinga instance through the Icinga command pipe.
|
changing permissions or sending a command to the Icinga instance through the Icinga command pipe.
|
||||||
@ -28,7 +28,7 @@ A permission is a simple list of identifiers of actions a user is
|
|||||||
allowed to do. Permissions are described in greater detail in the
|
allowed to do. Permissions are described in greater detail in the
|
||||||
section [Permissions](#permissions).
|
section [Permissions](#permissions).
|
||||||
|
|
||||||
### Objects
|
### <a id="security-basics-objects"></a> Objects
|
||||||
|
|
||||||
There are all kinds of different objects in Icinga Web 2: Hosts, Services, Notifications, Downtimes and Events.
|
There are all kinds of different objects in Icinga Web 2: Hosts, Services, Notifications, Downtimes and Events.
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ By default, a user can **see everything**, but it is possible to **explicitly re
|
|||||||
Restrictions are complex filter queries that describe what objects should be displayed to a user. Restrictions are described
|
Restrictions are complex filter queries that describe what objects should be displayed to a user. Restrictions are described
|
||||||
in greater detail in the section [Restrictions](#restrictions).
|
in greater detail in the section [Restrictions](#restrictions).
|
||||||
|
|
||||||
### Users
|
### <a id="security-basics-users"></a>Users
|
||||||
|
|
||||||
Anyone who can **login** to Icinga Web 2 is considered a user and can be referenced to by the
|
Anyone who can **login** to Icinga Web 2 is considered a user and can be referenced to by the
|
||||||
**user name** used during login.
|
**user name** used during login.
|
||||||
@ -55,13 +55,13 @@ an **authentication backend**. For extended information on setting up authentica
|
|||||||
backend to fetch users and groups from, which must be configured separately.
|
backend to fetch users and groups from, which must be configured separately.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
#### Managing Users
|
#### <a id="security-basics-users-managing"></a>Managing Users
|
||||||
|
|
||||||
When using a [Database
|
When using a [Database
|
||||||
as authentication backend](05-Authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration
|
as authentication backend](05-Authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration
|
||||||
can be found at **Configuration > Authentication > Users **.
|
can be found at **Configuration > Authentication > Users **.
|
||||||
|
|
||||||
### Groups
|
### <a id="security-basics-groups"></a>Groups
|
||||||
|
|
||||||
If there is a big amount of users to manage, it would be tedious to specify each user
|
If there is a big amount of users to manage, it would be tedious to specify each user
|
||||||
separately when regularly referring to the same group of users. Because of that, it is possible to group users.
|
separately when regularly referring to the same group of users. Because of that, it is possible to group users.
|
||||||
@ -72,13 +72,13 @@ Like users, groups are identified solely by their **name** that is provided by
|
|||||||
please read the chapter [Authentication](05-Authentication.md#authentication).
|
please read the chapter [Authentication](05-Authentication.md#authentication).
|
||||||
|
|
||||||
|
|
||||||
#### Managing Groups
|
#### <a id="security-basics-groups-managing"></a>Managing Groups
|
||||||
|
|
||||||
When using a [Database as an authentication backend](05-Authentication.md#authentication-configuration-db-authentication),
|
When using a [Database as an authentication backend](05-Authentication.md#authentication-configuration-db-authentication),
|
||||||
it is possible to manage groups and group memberships directly in the frontend. This configuration
|
it is possible to manage groups and group memberships directly in the frontend. This configuration
|
||||||
can be found at **Configuration > Authentication > Groups **.
|
can be found at **Configuration > Authentication > Groups **.
|
||||||
|
|
||||||
## Roles
|
## <a id="security-roles"></a>Roles
|
||||||
|
|
||||||
A role defines a set of **permissions** and **restrictions** and assigns
|
A role defines a set of **permissions** and **restrictions** and assigns
|
||||||
those to **users** and **groups**. For example, a role **admins** could define that certain
|
those to **users** and **groups**. For example, a role **admins** could define that certain
|
||||||
@ -91,17 +91,16 @@ and restrictions of the user itself and all the groups the user is member of. Pe
|
|||||||
be simply added up, while restrictions follow a slighty more complex pattern, that is described
|
be simply added up, while restrictions follow a slighty more complex pattern, that is described
|
||||||
in the section [Stacking Filters](#stacking-filters).
|
in the section [Stacking Filters](#stacking-filters).
|
||||||
|
|
||||||
### Configuration
|
### <a id="security-roles-configuration"></a>Configuration
|
||||||
|
|
||||||
Roles can be changed either through the icingaweb2 interface, by navigation
|
Roles can be changed either through the icingaweb2 interface, by navigation
|
||||||
to the page **Configuration > Authentication > Roles**, or through editing the
|
to the page **Configuration > Authentication > Roles**, or through editing the
|
||||||
configuration file:
|
configuration file:
|
||||||
|
|
||||||
|
/etc/icingaweb2/roles.ini
|
||||||
/etc/icingaweb2/roles.ini
|
|
||||||
|
|
||||||
|
|
||||||
#### Introducing Example
|
#### <a id="security-roles-configuration-example"></a>Introducing Example
|
||||||
|
|
||||||
To get you a quick start, here is an example of what a role definition could look like:
|
To get you a quick start, here is an example of what a role definition could look like:
|
||||||
|
|
||||||
@ -125,12 +124,12 @@ Each role is defined as a section, with the name of the role as section name. Th
|
|||||||
attributes can be defined for each role in a default Icinga Web 2 installation:
|
attributes can be defined for each role in a default Icinga Web 2 installation:
|
||||||
|
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
---------------------------|-----------------------------------------------------------------------------
|
| ----------------------------- | ----------- |
|
||||||
users | A comma-separated list of user **user names** that are affected by this role
|
| **users** | a comma-separated list of user **user names** that are affected by this role |
|
||||||
groups | A comma-separated list of **group names** that are affected by this role
|
| **groups** | a comma-separated list of **group names** that are affected by this role |
|
||||||
permissions | A comma-separated list of **permissions** granted by this role
|
| **permissions** | a comma-separated list of **permissions** granted by this role |
|
||||||
monitoring/filter/objects | A **filter expression** that restricts the access to services and hosts
|
| **monitoring/filter/objects** | a **filter expression** that restricts the access to services and hosts |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -154,17 +153,17 @@ a module permission in the format `module/<moduleName>` for each installed modul
|
|||||||
When multiple roles assign permissions to the same user (either directly or indirectly
|
When multiple roles assign permissions to the same user (either directly or indirectly
|
||||||
through a group) all permissions are added together to get the users actual permission set.
|
through a group) all permissions are added together to get the users actual permission set.
|
||||||
|
|
||||||
### Global Permissions
|
### <a id="permissions-global"></a> Global Permissions
|
||||||
|
|
||||||
Name | Permits
|
| Name | Permits |
|
||||||
--------------------------|--------------------------------------------------------
|
| ----------------------------- | ------------ |
|
||||||
* | Allow everything, including module-specific permissions
|
| **\*** | allow everything, including module-specific permissions |
|
||||||
config/* | Allow all configuration actions
|
| **config/\*** | allow all configuration actions |
|
||||||
config/modules | Allow enabling or disabling modules
|
| **config/modules** | allow enabling or disabling modules |
|
||||||
module/<moduleName> | Allow access to module <moduleName>
|
| **module/<moduleName>** | allow access to module <moduleName> |
|
||||||
|
|
||||||
|
|
||||||
### Monitoring Module Permissions
|
### <a id="permissions-module"></a> Monitoring Module Permissions
|
||||||
|
|
||||||
The built-in monitoring module defines an additional set of permissions, that
|
The built-in monitoring module defines an additional set of permissions, that
|
||||||
is described in detail in the monitoring module documentation.
|
is described in detail in the monitoring module documentation.
|
||||||
@ -183,7 +182,7 @@ in a default installation, is the `monitoring/filter/objects` directive, defined
|
|||||||
that can be used to apply filter to hosts and services. This directive was previously
|
that can be used to apply filter to hosts and services. This directive was previously
|
||||||
mentioned in the section [Syntax](#syntax).
|
mentioned in the section [Syntax](#syntax).
|
||||||
|
|
||||||
### Filter Expressions
|
### <a id="restrictions-filter"></a>Filter Expressions
|
||||||
|
|
||||||
Filters operate on columns. A complete list of all available filter columns on hosts and services can be found in
|
Filters operate on columns. A complete list of all available filter columns on hosts and services can be found in
|
||||||
the monitoring module documentation.
|
the monitoring module documentation.
|
||||||
@ -235,7 +234,7 @@ expression:
|
|||||||
As a result, a user is be able to see hosts that are matched by **ANY** of
|
As a result, a user is be able to see hosts that are matched by **ANY** of
|
||||||
the filter expressions. The following examples will show the usefulness of this behavior:
|
the filter expressions. The following examples will show the usefulness of this behavior:
|
||||||
|
|
||||||
#### Example 1: Negation
|
#### <a id="restrictions-filter-example1"></a>Example 1: Negation
|
||||||
|
|
||||||
[winadmin]
|
[winadmin]
|
||||||
groups = "windows-admins"
|
groups = "windows-admins"
|
||||||
@ -251,7 +250,7 @@ Will only match hosts and services whose host name does **not** contain **win**
|
|||||||
|
|
||||||
Notice that because of the behavior of two stacking filters, a user that is member of **windows-admins** and **web-admins**, will now be able to see both, Windows and non-Windows hosts and services.
|
Notice that because of the behavior of two stacking filters, a user that is member of **windows-admins** and **web-admins**, will now be able to see both, Windows and non-Windows hosts and services.
|
||||||
|
|
||||||
#### Example 2: Hostgroups
|
#### <a id="restrictions-filter-example2"></a>Example 2: Hostgroups
|
||||||
|
|
||||||
[unix-server]
|
[unix-server]
|
||||||
groups = "unix-admins"
|
groups = "unix-admins"
|
||||||
|
@ -22,7 +22,7 @@ For storing preferences in INI files you have to add the following section to th
|
|||||||
```
|
```
|
||||||
[preferences]
|
[preferences]
|
||||||
type = ini
|
type = ini
|
||||||
````
|
```
|
||||||
|
|
||||||
### <a id="preferences-configuration-db"></a> Store Preferences in a Database
|
### <a id="preferences-configuration-db"></a> Store Preferences in a Database
|
||||||
|
|
||||||
@ -30,10 +30,10 @@ In order to be more flexible in distributed setups you can store preferences in
|
|||||||
For storing preferences in a database, you have to define a [database resource](04-Resources.md#resources-configuration-database)
|
For storing preferences in a database, you have to define a [database resource](04-Resources.md#resources-configuration-database)
|
||||||
which will be referenced as resource for the preferences storage.
|
which will be referenced as resource for the preferences storage.
|
||||||
|
|
||||||
Directive | Description
|
| Directive | Description |
|
||||||
------------------------|------------
|
| ------------- | ----------- |
|
||||||
**type** | `db`
|
| **type** | `db` |
|
||||||
**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources).
|
| **resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). |
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# Vagrant
|
# <a id="vagrant"></a> Vagrant
|
||||||
|
|
||||||
## Requirements
|
This chapter shows how to set up and use our [Icinga Vagrant
|
||||||
|
boxes](https://github.com/icinga/icinga-vagrant) that we've created for
|
||||||
|
development, tests and demo cases.
|
||||||
|
|
||||||
|
## <a id="vagrant-requirements"></a>Requirements
|
||||||
|
|
||||||
* Vagrant >= version 1.5
|
* Vagrant >= version 1.5
|
||||||
* VirtualBox or Parallels Desktop
|
* VirtualBox or Parallels Desktop
|
||||||
@ -13,7 +17,7 @@ Parallels requires the additional provider plugin
|
|||||||
|
|
||||||
$ vagrant plugin install vagrant-parallels
|
$ vagrant plugin install vagrant-parallels
|
||||||
|
|
||||||
## General
|
## <a id="vagrant-general"></a>General
|
||||||
|
|
||||||
The Icinga Web 2 project ships with a Vagrant virtual machine that integrates
|
The Icinga Web 2 project ships with a Vagrant virtual machine that integrates
|
||||||
the source code with various services and example data in a controlled
|
the source code with various services and example data in a controlled
|
||||||
@ -21,9 +25,9 @@ environment. This enables developers and users to test Livestatus,
|
|||||||
MySQL and PostgreSQL backends as well as the LDAP authentication. All you
|
MySQL and PostgreSQL backends as well as the LDAP authentication. All you
|
||||||
have to do is install Vagrant and run:
|
have to do is install Vagrant and run:
|
||||||
|
|
||||||
````
|
```
|
||||||
vagrant up
|
vagrant up
|
||||||
````
|
```
|
||||||
|
|
||||||
> **Note:** The first boot of the vm takes a fairly long time because
|
> **Note:** The first boot of the vm takes a fairly long time because
|
||||||
> you'll download a plain CentOS base box and Vagrant will automatically
|
> you'll download a plain CentOS base box and Vagrant will automatically
|
||||||
@ -31,7 +35,7 @@ vagrant up
|
|||||||
|
|
||||||
After you should be able to browse [localhost:8080/icingaweb2](http://localhost:8080/icingaweb2).
|
After you should be able to browse [localhost:8080/icingaweb2](http://localhost:8080/icingaweb2).
|
||||||
|
|
||||||
## Log into Icinga Web 2
|
## <a id="vagrant-login"></a>Log into Icinga Web 2
|
||||||
|
|
||||||
Both LDAP and a MySQL are configured as authentication backend. Please use one of the following login credentials:
|
Both LDAP and a MySQL are configured as authentication backend. Please use one of the following login credentials:
|
||||||
|
|
||||||
@ -47,11 +51,11 @@ Both LDAP and a MySQL are configured as authentication backend. Please use one o
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Testing the Source Code
|
## <a id="vagrant-testing"></a>Testing the Source Code
|
||||||
|
|
||||||
All software required to run tests is installed in the virtual machine.
|
All software required to run tests is installed in the virtual machine.
|
||||||
In order to run all tests you have to execute the following command:
|
In order to run all tests you have to execute the following command:
|
||||||
|
|
||||||
````
|
```
|
||||||
vagrant ssh -c "icingacli test php unit"
|
vagrant ssh -c "icingacli test php unit"
|
||||||
````
|
```
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
%define revision 1
|
%define revision 1
|
||||||
|
|
||||||
Name: icingaweb2
|
Name: icingaweb2
|
||||||
Version: 2.3.2
|
Version: 2.3.4
|
||||||
Release: %{revision}%{?dist}
|
Release: %{revision}%{?dist}
|
||||||
Summary: Icinga Web 2
|
Summary: Icinga Web 2
|
||||||
Group: Applications/System
|
Group: Applications/System
|
||||||
@ -41,11 +41,11 @@ Requires: apache2-mod_php5
|
|||||||
%{?suse_version:Requires(pre): pwdutils}
|
%{?suse_version:Requires(pre): pwdutils}
|
||||||
Requires: %{name}-common = %{version}-%{release}
|
Requires: %{name}-common = %{version}-%{release}
|
||||||
Requires: php-Icinga = %{version}-%{release}
|
Requires: php-Icinga = %{version}-%{release}
|
||||||
Requires: %{name}-vendor-dompdf
|
Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist}
|
||||||
Requires: %{name}-vendor-HTMLPurifier
|
Requires: %{name}-vendor-HTMLPurifier = 4.8.0-1%{?dist}
|
||||||
Requires: %{name}-vendor-JShrink
|
Requires: %{name}-vendor-JShrink = 1.1.0-1%{?dist}
|
||||||
Requires: %{name}-vendor-lessphp
|
Requires: %{name}-vendor-lessphp = 0.4.0-1%{?dist}
|
||||||
Requires: %{name}-vendor-Parsedown
|
Requires: %{name}-vendor-Parsedown = 1.6.0-1%{?dist}
|
||||||
|
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@ -106,7 +106,7 @@ Icinga CLI
|
|||||||
|
|
||||||
|
|
||||||
%package vendor-dompdf
|
%package vendor-dompdf
|
||||||
Version: 0.6.2
|
Version: 0.7.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Icinga Web 2 vendor library dompdf
|
Summary: Icinga Web 2 vendor library dompdf
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
@ -118,7 +118,7 @@ Icinga Web 2 vendor library dompdf
|
|||||||
|
|
||||||
|
|
||||||
%package vendor-HTMLPurifier
|
%package vendor-HTMLPurifier
|
||||||
Version: 4.7.0
|
Version: 4.8.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Icinga Web 2 vendor library HTMLPurifier
|
Summary: Icinga Web 2 vendor library HTMLPurifier
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
@ -130,7 +130,7 @@ Icinga Web 2 vendor library HTMLPurifier
|
|||||||
|
|
||||||
|
|
||||||
%package vendor-JShrink
|
%package vendor-JShrink
|
||||||
Version: 1.0.1
|
Version: 1.1.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Icinga Web 2 vendor library JShrink
|
Summary: Icinga Web 2 vendor library JShrink
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
@ -154,7 +154,7 @@ Icinga Web 2 vendor library lessphp
|
|||||||
|
|
||||||
|
|
||||||
%package vendor-Parsedown
|
%package vendor-Parsedown
|
||||||
Version: 1.0.0
|
Version: 1.6.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Icinga Web 2 vendor library Parsedown
|
Summary: Icinga Web 2 vendor library Parsedown
|
||||||
Group: Development/Libraries
|
Group: Development/Libraries
|
||||||
|
@ -487,6 +487,7 @@ abstract class ApplicationBootstrap
|
|||||||
case E_NOTICE:
|
case E_NOTICE:
|
||||||
case E_WARNING:
|
case E_WARNING:
|
||||||
case E_STRICT:
|
case E_STRICT:
|
||||||
|
case E_RECOVERABLE_ERROR:
|
||||||
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||||
}
|
}
|
||||||
return false; // Continue with the normal error handler
|
return false; // Continue with the normal error handler
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
namespace Icinga\Application;
|
namespace Icinga\Application;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Authentication\Auth;
|
||||||
|
use Icinga\Application\Modules\Manager;
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,6 +149,46 @@ class Hook
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the Icinga module name from a given namespaced class name
|
||||||
|
*
|
||||||
|
* Does no validation, prefix must have been checked before
|
||||||
|
*
|
||||||
|
* Shameless copy of ClassLoader::extractModuleName()
|
||||||
|
*
|
||||||
|
* @param string $class The hook's class path
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function extractModuleName($class)
|
||||||
|
{
|
||||||
|
return lcfirst(
|
||||||
|
substr(
|
||||||
|
$class,
|
||||||
|
ClassLoader::MODULE_PREFIX_LENGTH,
|
||||||
|
strpos(
|
||||||
|
$class,
|
||||||
|
ClassLoader::NAMESPACE_SEPARATOR,
|
||||||
|
ClassLoader::MODULE_PREFIX_LENGTH + 1
|
||||||
|
) - ClassLoader::MODULE_PREFIX_LENGTH
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the user has the permission to access the module which provides the given hook
|
||||||
|
*
|
||||||
|
* @param string $class The hook's class path
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function hasPermission($class)
|
||||||
|
{
|
||||||
|
return Auth::getInstance()->hasPermission(
|
||||||
|
Manager::MODULE_PERMISSION_NS . self::extractModuleName($class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for a valid class name
|
* Test for a valid class name
|
||||||
*
|
*
|
||||||
@ -213,17 +254,19 @@ class Hook
|
|||||||
public static function all($name)
|
public static function all($name)
|
||||||
{
|
{
|
||||||
$name = self::normalizeHookName($name);
|
$name = self::normalizeHookName($name);
|
||||||
if (!self::has($name)) {
|
if (! self::has($name)) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (self::$hooks[$name] as $key => $hook) {
|
foreach (self::$hooks[$name] as $key => $hook) {
|
||||||
if (self::createInstance($name, $key) === null) {
|
if (self::hasPermission($key)) {
|
||||||
return array();
|
if (self::createInstance($name, $key) === null) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$instances[$name];
|
return isset(self::$instances[$name]) ? self::$instances[$name] : array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,7 +281,11 @@ class Hook
|
|||||||
$name = self::normalizeHookName($name);
|
$name = self::normalizeHookName($name);
|
||||||
|
|
||||||
if (self::has($name)) {
|
if (self::has($name)) {
|
||||||
return self::createInstance($name, key(self::$hooks[$name]));
|
foreach (self::$hooks[$name] as $key => $hook) {
|
||||||
|
if (self::hasPermission($key)) {
|
||||||
|
return self::createInstance($name, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ namespace Icinga\Application\Logger\Writer;
|
|||||||
use Icinga\Data\ConfigObject;
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Application\Logger\LogWriter;
|
use Icinga\Application\Logger\LogWriter;
|
||||||
|
use Icinga\Exception\ConfigurationError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log to the syslog service
|
* Log to the syslog service
|
||||||
@ -32,7 +33,15 @@ class SyslogWriter extends LogWriter
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public static $facilities = array(
|
public static $facilities = array(
|
||||||
'user' => LOG_USER
|
'user' => LOG_USER,
|
||||||
|
'local0' => LOG_LOCAL0,
|
||||||
|
'local1' => LOG_LOCAL1,
|
||||||
|
'local2' => LOG_LOCAL2,
|
||||||
|
'local3' => LOG_LOCAL3,
|
||||||
|
'local4' => LOG_LOCAL4,
|
||||||
|
'local5' => LOG_LOCAL5,
|
||||||
|
'local6' => LOG_LOCAL6,
|
||||||
|
'local7' => LOG_LOCAL7
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,7 +64,16 @@ class SyslogWriter extends LogWriter
|
|||||||
public function __construct(ConfigObject $config)
|
public function __construct(ConfigObject $config)
|
||||||
{
|
{
|
||||||
$this->ident = $config->get('application', 'icingaweb2');
|
$this->ident = $config->get('application', 'icingaweb2');
|
||||||
$this->facility = static::$facilities['user'];
|
|
||||||
|
$configuredFacility = $config->get('facility', 'user');
|
||||||
|
if (! isset(static::$facilities[$configuredFacility])) {
|
||||||
|
throw new ConfigurationError(
|
||||||
|
'Invalid logging facility: "%s" (expected one of: %s)',
|
||||||
|
$configuredFacility,
|
||||||
|
implode(', ', array_keys(static::$facilities))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->facility = static::$facilities[$configuredFacility];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,6 +249,15 @@ class Manager
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strtolower(substr($name, 0, 18)) === 'icingaweb2-module-') {
|
||||||
|
throw new ConfigurationError(
|
||||||
|
'Cannot enable module "%s": Directory name does not match the module\'s name.'
|
||||||
|
. ' Please rename the module to "%s" before enabling.',
|
||||||
|
$name,
|
||||||
|
substr($name, 18)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
clearstatcache(true);
|
clearstatcache(true);
|
||||||
$target = $this->installedBaseDirs[$name];
|
$target = $this->installedBaseDirs[$name];
|
||||||
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
|
$link = $this->enableDir . DIRECTORY_SEPARATOR . $name;
|
||||||
|
@ -362,25 +362,28 @@ class Module
|
|||||||
public function getMenu()
|
public function getMenu()
|
||||||
{
|
{
|
||||||
$this->launchConfigScript();
|
$this->launchConfigScript();
|
||||||
return $this->createMenu($this->menuItems);
|
return Navigation::fromArray($this->createMenu($this->menuItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and return a new navigation for the given menu items
|
* Create and return an array structure for the given menu items
|
||||||
*
|
*
|
||||||
* @param MenuItemContainer[] $items
|
* @param MenuItemContainer[] $items
|
||||||
*
|
*
|
||||||
* @return Navigation
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function createMenu(array $items)
|
private function createMenu(array $items)
|
||||||
{
|
{
|
||||||
$navigation = new Navigation();
|
$navigation = array();
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
/** @var MenuItemContainer $item */
|
/** @var MenuItemContainer $item */
|
||||||
$navigationItem = $navigation->createItem($item->getName(), $item->getProperties());
|
$properties = $item->getProperties();
|
||||||
$navigationItem->setChildren($this->createMenu($item->getChildren()));
|
$properties['children'] = $this->createMenu($item->getChildren());
|
||||||
$navigationItem->setLabel($this->translate($item->getName()));
|
if (! isset($properties['label'])) {
|
||||||
$navigation->addItem($navigationItem);
|
$properties['label'] = $this->translate($item->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
$navigation[$item->getName()] = $properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $navigation;
|
return $navigation;
|
||||||
@ -671,6 +674,8 @@ class Module
|
|||||||
$metadata->description .= $line;
|
$metadata->description .= $line;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} elseif (empty($line)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
list($key, $val) = preg_split('/:\s+/', $line, 2);
|
list($key, $val) = preg_split('/:\s+/', $line, 2);
|
||||||
|
@ -8,7 +8,7 @@ namespace Icinga\Application;
|
|||||||
*/
|
*/
|
||||||
class Version
|
class Version
|
||||||
{
|
{
|
||||||
const VERSION = '2.3.2';
|
const VERSION = '2.3.4';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the version of this instance of Icinga Web 2
|
* Get the version of this instance of Icinga Web 2
|
||||||
@ -25,24 +25,38 @@ class Version
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$gitDir = Icinga::app()->getBaseDir('.git');
|
$gitCommitId = static::getGitHead(Icinga::app()->getBaseDir());
|
||||||
$gitHead = @file_get_contents($gitDir . DIRECTORY_SEPARATOR . 'HEAD');
|
if ($gitCommitId !== false) {
|
||||||
if (false !== $gitHead) {
|
$version['gitCommitID'] = $gitCommitId;
|
||||||
$matches = array();
|
|
||||||
if (@preg_match('/(?<!.)ref:\s+(.+?)$/ms', $gitHead, $matches)) {
|
|
||||||
$gitCommitID = @file_get_contents($gitDir . DIRECTORY_SEPARATOR . $matches[1]);
|
|
||||||
} else {
|
|
||||||
$gitCommitID = $gitHead;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false !== $gitCommitID) {
|
|
||||||
$matches = array();
|
|
||||||
if (@preg_match('/(?<!.)(?P<gitCommitID>[0-9a-f]+)$/ms', $gitCommitID, $matches)) {
|
|
||||||
return array_merge($version, $matches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $version;
|
return $version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current commit of the Git repository in the given path
|
||||||
|
*
|
||||||
|
* @param string $repo Path to the Git repository
|
||||||
|
* @param bool $bare Whether the Git repository is bare
|
||||||
|
*
|
||||||
|
* @return string|bool False if not available
|
||||||
|
*/
|
||||||
|
public static function getGitHead($repo, $bare = false)
|
||||||
|
{
|
||||||
|
if (! $bare) {
|
||||||
|
$repo .= '/.git';
|
||||||
|
}
|
||||||
|
|
||||||
|
$head = @file_get_contents($repo . '/HEAD');
|
||||||
|
|
||||||
|
if ($head !== false) {
|
||||||
|
if (preg_match('/^ref: (.+)/', $head, $matches)) {
|
||||||
|
return @file_get_contents($repo . '/' . $matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $head;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,12 @@ class Web extends EmbeddedWeb
|
|||||||
'about' => array(
|
'about' => array(
|
||||||
'label' => t('About'),
|
'label' => t('About'),
|
||||||
'url' => 'about',
|
'url' => 'about',
|
||||||
'priority' => 701
|
'priority' => 700
|
||||||
|
),
|
||||||
|
'announcements' => array(
|
||||||
|
'label' => t('Announcements'),
|
||||||
|
'url' => 'announcements',
|
||||||
|
'priority' => 710
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -346,10 +351,10 @@ class Web extends EmbeddedWeb
|
|||||||
'icon' => 'user',
|
'icon' => 'user',
|
||||||
'priority' => 900,
|
'priority' => 900,
|
||||||
'children' => array(
|
'children' => array(
|
||||||
'preferences' => array(
|
'account' => array(
|
||||||
'label' => t('Preferences'),
|
'label' => t('My Account'),
|
||||||
'priority' => 100,
|
'priority' => 100,
|
||||||
'url' => 'preference'
|
'url' => 'account'
|
||||||
),
|
),
|
||||||
'logout' => array(
|
'logout' => array(
|
||||||
'label' => t('Logout'),
|
'label' => t('Logout'),
|
||||||
@ -366,7 +371,7 @@ class Web extends EmbeddedWeb
|
|||||||
'label' => t('Application Log'),
|
'label' => t('Application Log'),
|
||||||
'url' => 'list/applicationlog',
|
'url' => 'list/applicationlog',
|
||||||
'permission' => 'application/log',
|
'permission' => 'application/log',
|
||||||
'priority' => 710
|
'priority' => 900
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -411,6 +416,9 @@ class Web extends EmbeddedWeb
|
|||||||
private function setupUser()
|
private function setupUser()
|
||||||
{
|
{
|
||||||
$auth = Auth::getInstance();
|
$auth = Auth::getInstance();
|
||||||
|
if (! $this->request->isXmlHttpRequest() && $this->request->isApiRequest() && ! $auth->isAuthenticated()) {
|
||||||
|
$auth->authHttp();
|
||||||
|
}
|
||||||
if ($auth->isAuthenticated()) {
|
if ($auth->isAuthenticated()) {
|
||||||
$user = $auth->getUser();
|
$user = $auth->getUser();
|
||||||
$this->getRequest()->setUser($user);
|
$this->getRequest()->setUser($user);
|
||||||
|
@ -79,19 +79,18 @@ class Auth
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the user is authenticated
|
* Get whether the user is authenticated
|
||||||
*
|
|
||||||
* @param bool $ignoreSession True to prevent session authentication
|
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isAuthenticated($ignoreSession = false)
|
public function isAuthenticated()
|
||||||
{
|
{
|
||||||
if ($this->user === null && ! $ignoreSession) {
|
if ($this->user !== null) {
|
||||||
$this->authenticateFromSession();
|
return true;
|
||||||
}
|
}
|
||||||
|
$this->authenticateFromSession();
|
||||||
if ($this->user === null && ! $this->authExternal()) {
|
if ($this->user === null && ! $this->authExternal()) {
|
||||||
return $this->authHttp();
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -275,15 +274,12 @@ class Auth
|
|||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function authHttp()
|
public function authHttp()
|
||||||
{
|
{
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
if ($request->isXmlHttpRequest() || ! $request->isApiRequest()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$header = $request->getHeader('Authorization');
|
$header = $request->getHeader('Authorization');
|
||||||
if (empty($header)) {
|
if (empty($header)) {
|
||||||
$this->challengeHttp();
|
return false;
|
||||||
}
|
}
|
||||||
list($scheme) = explode(' ', $header, 2);
|
list($scheme) = explode(' ', $header, 2);
|
||||||
if ($scheme !== 'Basic') {
|
if ($scheme !== 'Basic') {
|
||||||
@ -294,7 +290,7 @@ class Auth
|
|||||||
$credentials = array_filter(explode(':', $credentials, 2));
|
$credentials = array_filter(explode(':', $credentials, 2));
|
||||||
if (count($credentials) !== 2) {
|
if (count($credentials) !== 2) {
|
||||||
// Deny empty username and/or password
|
// Deny empty username and/or password
|
||||||
$this->challengeHttp();
|
return false;
|
||||||
}
|
}
|
||||||
$user = new User($credentials[0]);
|
$user = new User($credentials[0]);
|
||||||
$password = $credentials[1];
|
$password = $credentials[1];
|
||||||
@ -303,7 +299,7 @@ class Auth
|
|||||||
$user->setIsHttpUser(true);
|
$user->setIsHttpUser(true);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$this->challengeHttp();
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +308,7 @@ class Auth
|
|||||||
*
|
*
|
||||||
* Sends the response w/ the 401 Unauthorized status code and WWW-Authenticate header.
|
* Sends the response w/ the 401 Unauthorized status code and WWW-Authenticate header.
|
||||||
*/
|
*/
|
||||||
protected function challengeHttp()
|
public function challengeHttp()
|
||||||
{
|
{
|
||||||
$response = $this->getResponse();
|
$response = $this->getResponse();
|
||||||
$response->setHttpResponseCode(401);
|
$response->setHttpResponseCode(401);
|
||||||
|
@ -118,6 +118,8 @@ class AuthChain implements Authenticatable, Iterator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($authenticated) {
|
if ($authenticated) {
|
||||||
|
$user->setAdditional('backend_name', $backend->getName());
|
||||||
|
$user->setAdditional('backend_type', $this->config->current()->get('backend'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
namespace Icinga\Authentication\User;
|
namespace Icinga\Authentication\User;
|
||||||
|
|
||||||
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Data\ConfigObject;
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
|
|
||||||
@ -11,6 +12,13 @@ use Icinga\User;
|
|||||||
*/
|
*/
|
||||||
class ExternalBackend implements UserBackendInterface
|
class ExternalBackend implements UserBackendInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Possible variables where to read the user from
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
public static $remoteUserEnvvars = array('REMOTE_USER', 'REDIRECT_REMOTE_USER');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this backend
|
* The name of this backend
|
||||||
*
|
*
|
||||||
@ -55,7 +63,7 @@ class ExternalBackend implements UserBackendInterface
|
|||||||
/**
|
/**
|
||||||
* Get the remote user from environment or $_SERVER, if any
|
* Get the remote user from environment or $_SERVER, if any
|
||||||
*
|
*
|
||||||
* @param string $variable The name variable where to read the user from
|
* @param string $variable The name of the variable where to read the user from
|
||||||
*
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
@ -65,29 +73,46 @@ class ExternalBackend implements UserBackendInterface
|
|||||||
if ($username !== false) {
|
if ($username !== false) {
|
||||||
return $username;
|
return $username;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists($variable, $_SERVER)) {
|
if (array_key_exists($variable, $_SERVER)) {
|
||||||
return $_SERVER[$variable];
|
return $_SERVER[$variable];
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the remote user information from environment or $_SERVER, if any
|
||||||
|
*
|
||||||
|
* @return array Contains always two entries, the username and origin which may both set to null.
|
||||||
|
*/
|
||||||
|
public static function getRemoteUserInformation()
|
||||||
|
{
|
||||||
|
foreach (static::$remoteUserEnvvars as $envVar) {
|
||||||
|
$username = static::getRemoteUser($envVar);
|
||||||
|
if ($username !== null) {
|
||||||
|
return array($username, $envVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function authenticate(User $user, $password = null)
|
public function authenticate(User $user, $password = null)
|
||||||
{
|
{
|
||||||
$username = static::getRemoteUser();
|
list($username, $field) = static::getRemoteUserInformation();
|
||||||
if ($username !== null) {
|
if ($username !== null) {
|
||||||
$user->setExternalUserInformation($username, 'REMOTE_USER');
|
$user->setExternalUserInformation($username, $field);
|
||||||
|
|
||||||
if ($this->stripUsernameRegexp) {
|
if ($this->stripUsernameRegexp) {
|
||||||
$stripped = preg_replace($this->stripUsernameRegexp, '', $username);
|
$stripped = @preg_replace($this->stripUsernameRegexp, '', $username);
|
||||||
if ($stripped !== false) {
|
if ($stripped === false) {
|
||||||
// TODO(el): PHP issues a warning when PHP cannot compile the regular expression. Should we log an
|
Logger::error('Failed to strip external username. The configured regular expression is invalid.');
|
||||||
// additional message in that case?
|
return false;
|
||||||
$username = $stripped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$username = $stripped;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->setUsername($username);
|
$user->setUsername($username);
|
||||||
|
@ -254,7 +254,7 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
|
|||||||
$this->requireTable('group_membership'),
|
$this->requireTable('group_membership'),
|
||||||
'g.id = gm.group_id',
|
'g.id = gm.group_id',
|
||||||
array()
|
array()
|
||||||
);
|
)->group('g.id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,8 +9,8 @@ use Icinga\Data\Inspection;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use Iterator;
|
use Iterator;
|
||||||
use Zend_Db;
|
use Zend_Db;
|
||||||
|
use Zend_Db_Expr;
|
||||||
use Icinga\Data\ConfigObject;
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\Data\Db\DbQuery;
|
|
||||||
use Icinga\Data\Extensible;
|
use Icinga\Data\Extensible;
|
||||||
use Icinga\Data\Filter\Filter;
|
use Icinga\Data\Filter\Filter;
|
||||||
use Icinga\Data\Filter\FilterAnd;
|
use Icinga\Data\Filter\FilterAnd;
|
||||||
@ -462,7 +462,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
|
|||||||
if ($sign === '=') {
|
if ($sign === '=') {
|
||||||
return $column . ' IN (' . $this->dbAdapter->quote($value) . ')';
|
return $column . ' IN (' . $this->dbAdapter->quote($value) . ')';
|
||||||
} elseif ($sign === '!=') {
|
} elseif ($sign === '!=') {
|
||||||
return $column . ' NOT IN (' . $this->dbAdapter->quote($value) . ')';
|
return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $column, $this->dbAdapter->quote($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ProgrammingError(
|
throw new ProgrammingError(
|
||||||
@ -470,10 +470,10 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
|
|||||||
);
|
);
|
||||||
} elseif ($sign === '=' && strpos($value, '*') !== false) {
|
} elseif ($sign === '=' && strpos($value, '*') !== false) {
|
||||||
if ($value === '*') {
|
if ($value === '*') {
|
||||||
// We'll ignore such filters as it prevents index usage and because "*" means anything, anything means
|
// We'll ignore such filters as it prevents index usage and because "*" means anything, so whether we're
|
||||||
// all whereas all means that whether we use a filter to match anything or no filter at all makes no
|
// using a real column with a valid comparison here or just an expression which can only be evaluated to
|
||||||
// difference, except for performance reasons...
|
// true makes no difference, except for performance reasons...
|
||||||
return '';
|
return new Zend_Db_Expr('TRUE');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $column . ' LIKE ' . $this->dbAdapter->quote(preg_replace('~\*~', '%', $value));
|
return $column . ' LIKE ' . $this->dbAdapter->quote(preg_replace('~\*~', '%', $value));
|
||||||
@ -482,12 +482,18 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
|
|||||||
// We'll ignore such filters as it prevents index usage and because "*" means nothing, so whether we're
|
// We'll ignore such filters as it prevents index usage and because "*" means nothing, so whether we're
|
||||||
// using a real column with a valid comparison here or just an expression which cannot be evaluated to
|
// using a real column with a valid comparison here or just an expression which cannot be evaluated to
|
||||||
// true makes no difference, except for performance reasons...
|
// true makes no difference, except for performance reasons...
|
||||||
return $this->dbAdapter->quote(0);
|
return new Zend_Db_Expr('FALSE');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $column . ' NOT LIKE ' . $this->dbAdapter->quote(preg_replace('~\*~', '%', $value));
|
return sprintf(
|
||||||
|
'(%1$s NOT LIKE %2$s OR %1$s IS NULL)',
|
||||||
|
$column,
|
||||||
|
$this->dbAdapter->quote(preg_replace('~\*~', '%', $value))
|
||||||
|
);
|
||||||
|
} elseif ($sign === '!=') {
|
||||||
|
return sprintf('(%1$s != %2$s OR %1$s IS NULL)', $column, $this->dbAdapter->quote($value));
|
||||||
} else {
|
} else {
|
||||||
return $column . ' ' . $sign . ' ' . $this->dbAdapter->quote($value);
|
return sprintf('%s %s %s', $column, $sign, $this->dbAdapter->quote($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
namespace Icinga\Data\Db;
|
namespace Icinga\Data\Db;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Zend_Db_Expr;
|
||||||
use Zend_Db_Select;
|
use Zend_Db_Select;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Data\Filter\FilterAnd;
|
use Icinga\Data\Filter\FilterAnd;
|
||||||
@ -300,30 +301,30 @@ class DbQuery extends SimpleQuery
|
|||||||
if ($sign === '=') {
|
if ($sign === '=') {
|
||||||
return $col . ' IN (' . $this->escapeForSql($expression) . ')';
|
return $col . ' IN (' . $this->escapeForSql($expression) . ')';
|
||||||
} elseif ($sign === '!=') {
|
} elseif ($sign === '!=') {
|
||||||
return $col . ' NOT IN (' . $this->escapeForSql($expression) . ')';
|
return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $col, $this->escapeForSql($expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new QueryException('Unable to render array expressions with operators other than equal or not equal');
|
throw new QueryException('Unable to render array expressions with operators other than equal or not equal');
|
||||||
} elseif ($sign === '=' && strpos($expression, '*') !== false) {
|
} elseif ($sign === '=' && strpos($expression, '*') !== false) {
|
||||||
if ($expression === '*') {
|
if ($expression === '*') {
|
||||||
// We'll ignore such filters as it prevents index usage and because "*" means anything, anything means
|
return new Zend_Db_Expr('TRUE');
|
||||||
// all whereas all means that whether we use a filter to match anything or no filter at all makes no
|
|
||||||
// difference, except for performance reasons...
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $col . ' LIKE ' . $this->escapeForSql($this->escapeWildcards($expression));
|
return $col . ' LIKE ' . $this->escapeForSql($this->escapeWildcards($expression));
|
||||||
} elseif ($sign === '!=' && strpos($expression, '*') !== false) {
|
} elseif ($sign === '!=' && strpos($expression, '*') !== false) {
|
||||||
if ($expression === '*') {
|
if ($expression === '*') {
|
||||||
// We'll ignore such filters as it prevents index usage and because "*" means nothing, so whether we're
|
return new Zend_Db_Expr('FALSE');
|
||||||
// using a real column with a valid comparison here or just an expression which cannot be evaluated to
|
|
||||||
// true makes no difference, except for performance reasons...
|
|
||||||
return $this->escapeForSql(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $col . ' NOT LIKE ' . $this->escapeForSql($this->escapeWildcards($expression));
|
return sprintf(
|
||||||
|
'(%1$s NOT LIKE %2$s OR %1$s IS NULL)',
|
||||||
|
$col,
|
||||||
|
$this->escapeForSql($this->escapeWildcards($expression))
|
||||||
|
);
|
||||||
|
} elseif ($sign === '!=') {
|
||||||
|
return sprintf('(%1$s %2$s %3$s OR %1$s IS NULL)', $col, $sign, $this->escapeForSql($expression));
|
||||||
} else {
|
} else {
|
||||||
return $col . ' ' . $sign . ' ' . $this->escapeForSql($expression);
|
return sprintf('%s %s %s', $col, $sign, $this->escapeForSql($expression));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,10 @@ class FilterExpression extends Filter
|
|||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
|
if ($this->isBooleanTrue()) {
|
||||||
|
return $this->column;
|
||||||
|
}
|
||||||
|
|
||||||
$expression = is_array($this->expression) ?
|
$expression = is_array($this->expression) ?
|
||||||
'( ' . implode(' | ', $this->expression) . ' )' :
|
'( ' . implode(' | ', $this->expression) . ' )' :
|
||||||
$this->expression;
|
$this->expression;
|
||||||
@ -121,6 +125,10 @@ class FilterExpression extends Filter
|
|||||||
|
|
||||||
public function toQueryString()
|
public function toQueryString()
|
||||||
{
|
{
|
||||||
|
if ($this->isBooleanTrue()) {
|
||||||
|
return $this->column;
|
||||||
|
}
|
||||||
|
|
||||||
$expression = is_array($this->expression) ?
|
$expression = is_array($this->expression) ?
|
||||||
'(' . implode('|', array_map('rawurlencode', $this->expression)) . ')' :
|
'(' . implode('|', array_map('rawurlencode', $this->expression)) . ')' :
|
||||||
rawurlencode($this->expression);
|
rawurlencode($this->expression);
|
||||||
@ -128,6 +136,11 @@ class FilterExpression extends Filter
|
|||||||
return $this->column . $this->sign . $expression;
|
return $this->column . $this->sign . $expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isBooleanTrue()
|
||||||
|
{
|
||||||
|
return $this->sign === '=' && $this->expression === true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If $var is a scalar, do the same as strtolower() would do.
|
* If $var is a scalar, do the same as strtolower() would do.
|
||||||
* If $var is an array, map $this->strtolowerRecursive() to its elements.
|
* If $var is an array, map $this->strtolowerRecursive() to its elements.
|
||||||
|
@ -56,14 +56,25 @@ class ResourceFactory implements ConfigAwareFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configuration of all existing resources, or get all resources of a given type.
|
* Get the configuration of all existing resources, or all resources of the given type
|
||||||
*
|
*
|
||||||
* @return Config The configuration containing all resources
|
* @param string $type Filter for resource type
|
||||||
|
*
|
||||||
|
* @return Config The resources configuration
|
||||||
*/
|
*/
|
||||||
public static function getResourceConfigs()
|
public static function getResourceConfigs($type = null)
|
||||||
{
|
{
|
||||||
self::assertResourcesExist();
|
self::assertResourcesExist();
|
||||||
return self::$resources;
|
if ($type === null) {
|
||||||
|
return self::$resources;
|
||||||
|
}
|
||||||
|
$resources = array();
|
||||||
|
foreach (self::$resources as $name => $resource) {
|
||||||
|
if ($resource->get('type') === $type) {
|
||||||
|
$resources[$name] = $resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Config::fromArray($resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,15 +3,17 @@
|
|||||||
|
|
||||||
namespace Icinga\File;
|
namespace Icinga\File;
|
||||||
|
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
class Csv
|
class Csv
|
||||||
{
|
{
|
||||||
protected $query;
|
protected $query;
|
||||||
|
|
||||||
protected function __construct() {}
|
protected function __construct() {}
|
||||||
|
|
||||||
public static function fromQuery($query)
|
public static function fromQuery(Traversable $query)
|
||||||
{
|
{
|
||||||
$csv = new Csv();
|
$csv = new static();
|
||||||
$csv->query = $query;
|
$csv->query = $query;
|
||||||
return $csv;
|
return $csv;
|
||||||
}
|
}
|
||||||
@ -26,7 +28,7 @@ class Csv
|
|||||||
{
|
{
|
||||||
$first = true;
|
$first = true;
|
||||||
$csv = '';
|
$csv = '';
|
||||||
foreach ($this->query->fetchAll() as $row) {
|
foreach ($this->query as $row) {
|
||||||
if ($first) {
|
if ($first) {
|
||||||
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
|
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
|
||||||
$first = false;
|
$first = false;
|
||||||
|
@ -149,6 +149,10 @@ class IniWriter
|
|||||||
$domSection = $doc->getSection($section);
|
$domSection = $doc->getSection($section);
|
||||||
}
|
}
|
||||||
foreach ($directives as $key => $value) {
|
foreach ($directives as $key => $value) {
|
||||||
|
if ($value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ($value instanceof ConfigObject) {
|
if ($value instanceof ConfigObject) {
|
||||||
throw new ProgrammingError('Cannot diff recursive configs');
|
throw new ProgrammingError('Cannot diff recursive configs');
|
||||||
}
|
}
|
||||||
|
@ -3,28 +3,16 @@
|
|||||||
|
|
||||||
namespace Icinga\File;
|
namespace Icinga\File;
|
||||||
|
|
||||||
use DOMPDF;
|
use Dompdf\Dompdf;
|
||||||
use DOMDocument;
|
use Dompdf\Options;
|
||||||
use DOMXPath;
|
|
||||||
use Font_Metrics;
|
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Web\StyleSheet;
|
|
||||||
use Icinga\Web\Url;
|
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
use Icinga\Web\Url;
|
||||||
|
|
||||||
require_once 'dompdf/dompdf_config.inc.php';
|
require_once 'dompdf/autoload.inc.php';
|
||||||
require_once 'dompdf/include/autoload.inc.php';
|
|
||||||
|
|
||||||
class Pdf extends DOMPDF
|
class Pdf
|
||||||
{
|
{
|
||||||
public $paginateTable = false;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->set_paper('A4', 'portrait');
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function assertNoHeadersSent()
|
protected function assertNoHeadersSent()
|
||||||
{
|
{
|
||||||
if (headers_sent()) {
|
if (headers_sent()) {
|
||||||
@ -50,12 +38,15 @@ class Pdf extends DOMPDF
|
|||||||
$html = $layout->render();
|
$html = $layout->render();
|
||||||
$imgDir = Url::fromPath('img');
|
$imgDir = Url::fromPath('img');
|
||||||
$html = preg_replace('~src="' . $imgDir . '/~', 'src="' . Icinga::app()->getBootstrapDirectory() . '/img/', $html);
|
$html = preg_replace('~src="' . $imgDir . '/~', 'src="' . Icinga::app()->getBootstrapDirectory() . '/img/', $html);
|
||||||
$this->load_html($html);
|
$options = new Options();
|
||||||
$this->render();
|
$options->set('defaultPaperSize', 'A4');
|
||||||
|
$dompdf = new Dompdf($options);
|
||||||
|
$dompdf->loadHtml($html);
|
||||||
|
$dompdf->render();
|
||||||
$request = $controller->getRequest();
|
$request = $controller->getRequest();
|
||||||
$this->stream(
|
$dompdf->stream(
|
||||||
sprintf(
|
sprintf(
|
||||||
'%s-%s-%d.pdf',
|
'%s-%s-%d',
|
||||||
$request->getControllerName(),
|
$request->getControllerName(),
|
||||||
$request->getActionName(),
|
$request->getActionName(),
|
||||||
time()
|
time()
|
||||||
|
@ -5,6 +5,7 @@ namespace Icinga\Repository;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Icinga\Application\Config;
|
use Icinga\Application\Config;
|
||||||
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\Data\Extensible;
|
use Icinga\Data\Extensible;
|
||||||
use Icinga\Data\Filter\Filter;
|
use Icinga\Data\Filter\Filter;
|
||||||
use Icinga\Data\Updatable;
|
use Icinga\Data\Updatable;
|
||||||
@ -18,33 +19,218 @@ use Icinga\Exception\StatementException;
|
|||||||
* Additionally provided features:
|
* Additionally provided features:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Insert, update and delete capabilities</li>
|
* <li>Insert, update and delete capabilities</li>
|
||||||
|
* <li>Triggers for inserts, updates and deletions</li>
|
||||||
|
* <li>Lazy initialization of table specific configs</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible
|
abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The datasource being used
|
* The configuration files used as table specific datasources
|
||||||
*
|
*
|
||||||
* @var Config
|
* This must be initialized by concrete repository implementations, in the following format
|
||||||
|
* <code>
|
||||||
|
* array(
|
||||||
|
* 'table_name' => array(
|
||||||
|
* 'config' => 'name_of_the_ini_file_without_extension',
|
||||||
|
* 'keyColumn' => 'the_name_of_the_column_to_use_as_key_column',
|
||||||
|
* ['module' => 'the_name_of_the_module_if_any']
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $ds;
|
protected $configs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tables for which triggers are available when inserting, updating or deleting rows
|
||||||
|
*
|
||||||
|
* This may be initialized by concrete repository implementations and describes for which table names triggers
|
||||||
|
* are available. The repository attempts to find a method depending on the type of event and table for which
|
||||||
|
* to run the trigger. The name of such a method is expected to be declared using lowerCamelCase.
|
||||||
|
* (e.g. group_membership will be translated to onUpdateGroupMembership and groupmembership will be translated
|
||||||
|
* to onUpdateGroupmembership) The available events are onInsert, onUpdate and onDelete.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $triggers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new INI repository object
|
* Create a new INI repository object
|
||||||
*
|
*
|
||||||
* @param Config $ds The data source to use
|
* @param Config|null $ds The data source to use
|
||||||
*
|
*
|
||||||
* @throws ProgrammingError In case the given data source does not provide a valid key column
|
* @throws ProgrammingError In case the given data source does not provide a valid key column
|
||||||
*/
|
*/
|
||||||
public function __construct(Config $ds)
|
public function __construct(Config $ds = null)
|
||||||
{
|
{
|
||||||
parent::__construct($ds); // First! Due to init().
|
parent::__construct($ds); // First! Due to init().
|
||||||
|
|
||||||
if (! $ds->getConfigObject()->getKeyColumn()) {
|
if ($ds !== null && !$ds->getConfigObject()->getKeyColumn()) {
|
||||||
throw new ProgrammingError('INI repositories require their data source to provide a valid key column');
|
throw new ProgrammingError('INI repositories require their data source to provide a valid key column');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @return Config
|
||||||
|
*/
|
||||||
|
public function getDataSource($table = null)
|
||||||
|
{
|
||||||
|
if ($this->ds !== null) {
|
||||||
|
return parent::getDataSource($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $table ?: $this->getBaseTable();
|
||||||
|
$configs = $this->getConfigs();
|
||||||
|
if (! isset($configs[$table])) {
|
||||||
|
throw new ProgrammingError('Config for table "%s" missing', $table);
|
||||||
|
} elseif (! $configs[$table] instanceof Config) {
|
||||||
|
$configs[$table] = $this->createConfig($configs[$table], $table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $configs[$table]->getConfigObject()->getKeyColumn()) {
|
||||||
|
throw new ProgrammingError(
|
||||||
|
'INI repositories require their data source to provide a valid key column'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $configs[$table];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configuration files used as table specific datasources
|
||||||
|
*
|
||||||
|
* Calls $this->initializeConfigs() in case $this->configs is null.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getConfigs()
|
||||||
|
{
|
||||||
|
if ($this->configs === null) {
|
||||||
|
$this->configs = $this->initializeConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->configs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite this in your repository implementation in case you need to initialize the configs lazily
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function initializeConfigs()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the tables for which triggers are available when inserting, updating or deleting rows
|
||||||
|
*
|
||||||
|
* Calls $this->initializeTriggers() in case $this->triggers is null.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getTriggers()
|
||||||
|
{
|
||||||
|
if ($this->triggers === null) {
|
||||||
|
$this->triggers = $this->initializeTriggers();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->triggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite this in your repository implementation in case you need to initialize the triggers lazily
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function initializeTriggers()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a trigger for the given table and row which is about to be inserted
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @param ConfigObject $new
|
||||||
|
*
|
||||||
|
* @return ConfigObject
|
||||||
|
*/
|
||||||
|
public function onInsert($table, ConfigObject $new)
|
||||||
|
{
|
||||||
|
$trigger = $this->getTrigger($table, 'onInsert');
|
||||||
|
if ($trigger !== null) {
|
||||||
|
$row = $this->$trigger($new);
|
||||||
|
if ($row !== null) {
|
||||||
|
$new = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a trigger for the given table and row which is about to be updated
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @param ConfigObject $old
|
||||||
|
* @param ConfigObject $new
|
||||||
|
*
|
||||||
|
* @return ConfigObject
|
||||||
|
*/
|
||||||
|
public function onUpdate($table, ConfigObject $old, ConfigObject $new)
|
||||||
|
{
|
||||||
|
$trigger = $this->getTrigger($table, 'onUpdate');
|
||||||
|
if ($trigger !== null) {
|
||||||
|
$row = $this->$trigger($old, $new);
|
||||||
|
if ($row !== null) {
|
||||||
|
$new = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a trigger for the given table and row which has been deleted
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* @param ConfigObject $old
|
||||||
|
*
|
||||||
|
* @return ConfigObject
|
||||||
|
*/
|
||||||
|
public function onDelete($table, ConfigObject $old)
|
||||||
|
{
|
||||||
|
$trigger = $this->getTrigger($table, 'onDelete');
|
||||||
|
if ($trigger !== null) {
|
||||||
|
$this->$trigger($old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the trigger method for the given table and event-type
|
||||||
|
*
|
||||||
|
* @param string $table The table name for which to return a trigger method
|
||||||
|
* @param string $event The name of the event type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getTrigger($table, $event)
|
||||||
|
{
|
||||||
|
if (! in_array($table, $this->getTriggers())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$identifier = join('', array_map('ucfirst', explode('_', $table)));
|
||||||
|
if (method_exists($this, $event . $identifier)) {
|
||||||
|
return $event . $identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the given data for the given target
|
* Insert the given data for the given target
|
||||||
*
|
*
|
||||||
@ -57,17 +243,20 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
|
|||||||
*/
|
*/
|
||||||
public function insert($target, array $data)
|
public function insert($target, array $data)
|
||||||
{
|
{
|
||||||
|
$ds = $this->getDataSource($target);
|
||||||
$newData = $this->requireStatementColumns($target, $data);
|
$newData = $this->requireStatementColumns($target, $data);
|
||||||
$section = $this->extractSectionName($newData);
|
|
||||||
|
|
||||||
if ($this->ds->hasSection($section)) {
|
$config = $this->onInsert($target, new ConfigObject($newData));
|
||||||
|
$section = $this->extractSectionName($config, $ds->getConfigObject()->getKeyColumn());
|
||||||
|
|
||||||
|
if ($ds->hasSection($section)) {
|
||||||
throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section);
|
throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ds->setSection($section, $newData);
|
$ds->setSection($section, $config);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ds->saveIni();
|
$ds->saveIni();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new StatementException(t('Failed to insert. An error occurred: %s'), $e->getMessage());
|
throw new StatementException(t('Failed to insert. An error occurred: %s'), $e->getMessage());
|
||||||
}
|
}
|
||||||
@ -84,8 +273,10 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
|
|||||||
*/
|
*/
|
||||||
public function update($target, array $data, Filter $filter = null)
|
public function update($target, array $data, Filter $filter = null)
|
||||||
{
|
{
|
||||||
|
$ds = $this->getDataSource($target);
|
||||||
$newData = $this->requireStatementColumns($target, $data);
|
$newData = $this->requireStatementColumns($target, $data);
|
||||||
$keyColumn = $this->ds->getConfigObject()->getKeyColumn();
|
|
||||||
|
$keyColumn = $ds->getConfigObject()->getKeyColumn();
|
||||||
if ($filter === null && isset($newData[$keyColumn])) {
|
if ($filter === null && isset($newData[$keyColumn])) {
|
||||||
throw new StatementException(
|
throw new StatementException(
|
||||||
t('Cannot update. Column "%s" holds a section\'s name which must be unique'),
|
t('Cannot update. Column "%s" holds a section\'s name which must be unique'),
|
||||||
@ -93,16 +284,14 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$query = $ds->select();
|
||||||
if ($filter !== null) {
|
if ($filter !== null) {
|
||||||
$filter = $this->requireFilter($target, $filter);
|
$query->addFilter($this->requireFilter($target, $filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var ConfigObject $config */
|
||||||
$newSection = null;
|
$newSection = null;
|
||||||
foreach (iterator_to_array($this->ds) as $section => $config) {
|
foreach ($query as $section => $config) {
|
||||||
if ($filter !== null && !$filter->matches($config)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($newSection !== null) {
|
if ($newSection !== null) {
|
||||||
throw new StatementException(
|
throw new StatementException(
|
||||||
t('Cannot update. Column "%s" holds a section\'s name which must be unique'),
|
t('Cannot update. Column "%s" holds a section\'s name which must be unique'),
|
||||||
@ -110,27 +299,37 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$newConfig = clone $config;
|
||||||
foreach ($newData as $column => $value) {
|
foreach ($newData as $column => $value) {
|
||||||
if ($column === $keyColumn) {
|
if ($column === $keyColumn) {
|
||||||
$newSection = $value;
|
$newSection = $value;
|
||||||
} else {
|
} else {
|
||||||
$config->$column = $value;
|
$newConfig->$column = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is necessary as the query result set contains the key column.
|
||||||
|
unset($newConfig->$keyColumn);
|
||||||
|
|
||||||
if ($newSection) {
|
if ($newSection) {
|
||||||
if ($this->ds->hasSection($newSection)) {
|
if ($ds->hasSection($newSection)) {
|
||||||
throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection);
|
throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->ds->removeSection($section)->setSection($newSection, $config);
|
$ds->removeSection($section)->setSection(
|
||||||
|
$newSection,
|
||||||
|
$this->onUpdate($target, $config, $newConfig)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->ds->setSection($section, $config);
|
$ds->setSection(
|
||||||
|
$section,
|
||||||
|
$this->onUpdate($target, $config, $newConfig)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ds->saveIni();
|
$ds->saveIni();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new StatementException(t('Failed to update. An error occurred: %s'), $e->getMessage());
|
throw new StatementException(t('Failed to update. An error occurred: %s'), $e->getMessage());
|
||||||
}
|
}
|
||||||
@ -146,41 +345,74 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
|
|||||||
*/
|
*/
|
||||||
public function delete($target, Filter $filter = null)
|
public function delete($target, Filter $filter = null)
|
||||||
{
|
{
|
||||||
|
$ds = $this->getDataSource($target);
|
||||||
|
|
||||||
|
$query = $ds->select();
|
||||||
if ($filter !== null) {
|
if ($filter !== null) {
|
||||||
$filter = $this->requireFilter($target, $filter);
|
$query->addFilter($this->requireFilter($target, $filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (iterator_to_array($this->ds) as $section => $config) {
|
/** @var ConfigObject $config */
|
||||||
if ($filter === null || $filter->matches($config)) {
|
foreach ($query as $section => $config) {
|
||||||
$this->ds->removeSection($section);
|
$ds->removeSection($section);
|
||||||
}
|
$this->onDelete($target, $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->ds->saveIni();
|
$ds->saveIni();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new StatementException(t('Failed to delete. An error occurred: %s'), $e->getMessage());
|
throw new StatementException(t('Failed to delete. An error occurred: %s'), $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract and return the section name off of the given $data
|
* Create and return a Config for the given meta and table
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $meta
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
|
* @return Config
|
||||||
|
*
|
||||||
|
* @throws ProgrammingError In case the given meta is invalid
|
||||||
|
*/
|
||||||
|
protected function createConfig(array $meta, $table)
|
||||||
|
{
|
||||||
|
if (! isset($meta['name'])) {
|
||||||
|
throw new ProgrammingError('Config file name missing for table "%s"', $table);
|
||||||
|
} elseif (! isset($meta['keyColumn'])) {
|
||||||
|
throw new ProgrammingError('Config key column name missing for table "%s"', $table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($meta['module'])) {
|
||||||
|
$config = Config::module($meta['module'], $meta['name']);
|
||||||
|
} else {
|
||||||
|
$config = Config::app($meta['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$config->getConfigObject()->setKeyColumn($meta['keyColumn']);
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract and return the section name off of the given $config
|
||||||
|
*
|
||||||
|
* @param array|ConfigObject $config
|
||||||
|
* @param string $keyColumn
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*
|
*
|
||||||
* @throws ProgrammingError In case no valid section name is available
|
* @throws ProgrammingError In case no valid section name is available
|
||||||
*/
|
*/
|
||||||
protected function extractSectionName(array & $data)
|
protected function extractSectionName( & $config, $keyColumn)
|
||||||
{
|
{
|
||||||
$keyColumn = $this->ds->getConfigObject()->getKeyColumn();
|
if (! is_array($config) && !$config instanceof ConfigObject) {
|
||||||
if (! isset($data[$keyColumn])) {
|
throw new ProgrammingError('$config is neither an array nor a ConfigObject');
|
||||||
throw new ProgrammingError('$data does not provide a value for key column "%s"', $keyColumn);
|
} elseif (! isset($config[$keyColumn])) {
|
||||||
|
throw new ProgrammingError('$config does not provide a value for key column "%s"', $keyColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
$section = $data[$keyColumn];
|
$section = $config[$keyColumn];
|
||||||
unset($data[$keyColumn]);
|
unset($config[$keyColumn]);
|
||||||
return $section;
|
return $section;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,13 @@ abstract class Repository implements Selectable
|
|||||||
*/
|
*/
|
||||||
protected $blacklistedQueryColumns;
|
protected $blacklistedQueryColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the blacklisted query columns are in the legacy format
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $legacyBlacklistedQueryColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The filter columns being provided
|
* The filter columns being provided
|
||||||
*
|
*
|
||||||
@ -105,6 +112,13 @@ abstract class Repository implements Selectable
|
|||||||
*/
|
*/
|
||||||
protected $filterColumns;
|
protected $filterColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the provided filter columns are in the legacy format
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $legacyFilterColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The search columns (or aliases) being provided
|
* The search columns (or aliases) being provided
|
||||||
*
|
*
|
||||||
@ -112,6 +126,13 @@ abstract class Repository implements Selectable
|
|||||||
*/
|
*/
|
||||||
protected $searchColumns;
|
protected $searchColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the provided search columns are in the legacy format
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $legacySearchColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sort rules to be applied on a query
|
* The sort rules to be applied on a query
|
||||||
*
|
*
|
||||||
@ -140,6 +161,13 @@ abstract class Repository implements Selectable
|
|||||||
*/
|
*/
|
||||||
protected $sortRules;
|
protected $sortRules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the provided sort rules are in the legacy format
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $legacySortRules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value conversion rules to apply on a query or statement
|
* The value conversion rules to apply on a query or statement
|
||||||
*
|
*
|
||||||
@ -186,9 +214,10 @@ abstract class Repository implements Selectable
|
|||||||
/**
|
/**
|
||||||
* Create a new repository object
|
* Create a new repository object
|
||||||
*
|
*
|
||||||
* @param Selectable $ds The datasource to use
|
* @param Selectable|null $ds The datasource to use.
|
||||||
|
* Only pass null if you have overridden {@link getDataSource()}!
|
||||||
*/
|
*/
|
||||||
public function __construct(Selectable $ds)
|
public function __construct(Selectable $ds = null)
|
||||||
{
|
{
|
||||||
$this->ds = $ds;
|
$this->ds = $ds;
|
||||||
$this->aliasTableMap = array();
|
$this->aliasTableMap = array();
|
||||||
@ -235,12 +264,23 @@ abstract class Repository implements Selectable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the datasource being used
|
* Return the datasource being used for the given table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
*
|
*
|
||||||
* @return Selectable
|
* @return Selectable
|
||||||
|
*
|
||||||
|
* @throws ProgrammingError In case no datasource is available
|
||||||
*/
|
*/
|
||||||
public function getDataSource()
|
public function getDataSource($table = null)
|
||||||
{
|
{
|
||||||
|
if ($this->ds === null) {
|
||||||
|
throw new ProgrammingError(
|
||||||
|
'No data source available. It is required to either pass it'
|
||||||
|
. ' at initialization time or by overriding this method.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->ds;
|
return $this->ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,25 +363,45 @@ abstract class Repository implements Selectable
|
|||||||
*
|
*
|
||||||
* Calls $this->initializeBlacklistedQueryColumns() in case $this->blacklistedQueryColumns is null.
|
* Calls $this->initializeBlacklistedQueryColumns() in case $this->blacklistedQueryColumns is null.
|
||||||
*
|
*
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getBlacklistedQueryColumns()
|
public function getBlacklistedQueryColumns($table = null)
|
||||||
{
|
{
|
||||||
if ($this->blacklistedQueryColumns === null) {
|
if ($this->blacklistedQueryColumns === null) {
|
||||||
$this->blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns();
|
$this->legacyBlacklistedQueryColumns = false;
|
||||||
|
|
||||||
|
$blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns($table);
|
||||||
|
if (is_int(key($blacklistedQueryColumns))) {
|
||||||
|
$this->blacklistedQueryColumns[$table] = $blacklistedQueryColumns;
|
||||||
|
} else {
|
||||||
|
$this->blacklistedQueryColumns = $blacklistedQueryColumns;
|
||||||
|
}
|
||||||
|
} elseif ($this->legacyBlacklistedQueryColumns === null) {
|
||||||
|
$this->legacyBlacklistedQueryColumns = is_int(key($this->blacklistedQueryColumns));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->blacklistedQueryColumns;
|
if ($this->legacyBlacklistedQueryColumns) {
|
||||||
|
return $this->blacklistedQueryColumns;
|
||||||
|
} elseif (! isset($this->blacklistedQueryColumns[$table])) {
|
||||||
|
$this->blacklistedQueryColumns[$table] = $this->initializeBlacklistedQueryColumns($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->blacklistedQueryColumns[$table];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrite this in your repository implementation in case you
|
* Overwrite this in your repository implementation in case you need to initialize the
|
||||||
* need to initialize the blacklisted query columns lazily
|
* blacklisted query columns lazily or dependent on a query's current base table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function initializeBlacklistedQueryColumns()
|
protected function initializeBlacklistedQueryColumns()
|
||||||
{
|
{
|
||||||
|
// $table is not part of the signature due to PHP strict standards
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,24 +410,47 @@ abstract class Repository implements Selectable
|
|||||||
*
|
*
|
||||||
* Calls $this->initializeFilterColumns() in case $this->filterColumns is null.
|
* Calls $this->initializeFilterColumns() in case $this->filterColumns is null.
|
||||||
*
|
*
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getFilterColumns()
|
public function getFilterColumns($table = null)
|
||||||
{
|
{
|
||||||
if ($this->filterColumns === null) {
|
if ($this->filterColumns === null) {
|
||||||
$this->filterColumns = $this->initializeFilterColumns();
|
$this->legacyFilterColumns = false;
|
||||||
|
|
||||||
|
$filterColumns = $this->initializeFilterColumns($table);
|
||||||
|
$foundTables = array_intersect_key($this->getQueryColumns(), $filterColumns);
|
||||||
|
if (empty($foundTables)) {
|
||||||
|
$this->filterColumns[$table] = $filterColumns;
|
||||||
|
} else {
|
||||||
|
$this->filterColumns = $filterColumns;
|
||||||
|
}
|
||||||
|
} elseif ($this->legacyFilterColumns === null) {
|
||||||
|
$foundTables = array_intersect_key($this->getQueryColumns(), $this->filterColumns);
|
||||||
|
$this->legacyFilterColumns = empty($foundTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->filterColumns;
|
if ($this->legacyFilterColumns) {
|
||||||
|
return $this->filterColumns;
|
||||||
|
} elseif (! isset($this->filterColumns[$table])) {
|
||||||
|
$this->filterColumns[$table] = $this->initializeFilterColumns($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->filterColumns[$table];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrite this in your repository implementation in case you need to initialize the filter columns lazily
|
* Overwrite this in your repository implementation in case you need to initialize
|
||||||
|
* the filter columns lazily or dependent on a query's current base table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function initializeFilterColumns()
|
protected function initializeFilterColumns()
|
||||||
{
|
{
|
||||||
|
// $table is not part of the signature due to PHP strict standards
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,24 +459,45 @@ abstract class Repository implements Selectable
|
|||||||
*
|
*
|
||||||
* Calls $this->initializeSearchColumns() in case $this->searchColumns is null.
|
* Calls $this->initializeSearchColumns() in case $this->searchColumns is null.
|
||||||
*
|
*
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getSearchColumns()
|
public function getSearchColumns($table = null)
|
||||||
{
|
{
|
||||||
if ($this->searchColumns === null) {
|
if ($this->searchColumns === null) {
|
||||||
$this->searchColumns = $this->initializeSearchColumns();
|
$this->legacySearchColumns = false;
|
||||||
|
|
||||||
|
$searchColumns = $this->initializeSearchColumns($table);
|
||||||
|
if (is_int(key($searchColumns))) {
|
||||||
|
$this->searchColumns[$table] = $searchColumns;
|
||||||
|
} else {
|
||||||
|
$this->searchColumns = $searchColumns;
|
||||||
|
}
|
||||||
|
} elseif ($this->legacySearchColumns === null) {
|
||||||
|
$this->legacySearchColumns = is_int(key($this->searchColumns));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->searchColumns;
|
if ($this->legacySearchColumns) {
|
||||||
|
return $this->searchColumns;
|
||||||
|
} elseif (! isset($this->searchColumns[$table])) {
|
||||||
|
$this->searchColumns[$table] = $this->initializeSearchColumns($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchColumns[$table];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrite this in your repository implementation in case you need to initialize the search columns lazily
|
* Overwrite this in your repository implementation in case you need to initialize
|
||||||
|
* the search columns lazily or dependent on a query's current base table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function initializeSearchColumns()
|
protected function initializeSearchColumns()
|
||||||
{
|
{
|
||||||
|
// $table is not part of the signature due to PHP strict standards
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,24 +506,47 @@ abstract class Repository implements Selectable
|
|||||||
*
|
*
|
||||||
* Calls $this->initializeSortRules() in case $this->sortRules is null.
|
* Calls $this->initializeSortRules() in case $this->sortRules is null.
|
||||||
*
|
*
|
||||||
|
* @param string $table
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getSortRules()
|
public function getSortRules($table = null)
|
||||||
{
|
{
|
||||||
if ($this->sortRules === null) {
|
if ($this->sortRules === null) {
|
||||||
$this->sortRules = $this->initializeSortRules();
|
$this->legacySortRules = false;
|
||||||
|
|
||||||
|
$sortRules = $this->initializeSortRules($table);
|
||||||
|
$foundTables = array_intersect_key($this->getQueryColumns(), $sortRules);
|
||||||
|
if (empty($foundTables)) {
|
||||||
|
$this->sortRules[$table] = $sortRules;
|
||||||
|
} else {
|
||||||
|
$this->sortRules = $sortRules;
|
||||||
|
}
|
||||||
|
} elseif ($this->legacySortRules === null) {
|
||||||
|
$foundTables = array_intersect_key($this->getQueryColumns(), $this->sortRules);
|
||||||
|
$this->legacyFilterColumns = empty($foundTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->sortRules;
|
if ($this->legacySortRules) {
|
||||||
|
return $this->sortRules;
|
||||||
|
} elseif (! isset($this->sortRules[$table])) {
|
||||||
|
$this->sortRules[$table] = $this->initializeSortRules($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->sortRules[$table];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrite this in your repository implementation in case you need to initialize the sort rules lazily
|
* Overwrite this in your repository implementation in case you need to initialize
|
||||||
|
* the sort rules lazily or dependent on a query's current base table
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function initializeSortRules()
|
protected function initializeSortRules()
|
||||||
{
|
{
|
||||||
|
// $table is not part of the signature due to PHP strict standards
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +1027,7 @@ abstract class Repository implements Selectable
|
|||||||
throw new ProgrammingError('Table name "%s" not found', $table);
|
throw new ProgrammingError('Table name "%s" not found', $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
$blacklist = $this->getBlacklistedQueryColumns();
|
$blacklist = $this->getBlacklistedQueryColumns($table);
|
||||||
$columns = array();
|
$columns = array();
|
||||||
foreach ($queryColumns[$table] as $alias => $column) {
|
foreach ($queryColumns[$table] as $alias => $column) {
|
||||||
$name = is_string($alias) ? $alias : $column;
|
$name = is_string($alias) ? $alias : $column;
|
||||||
@ -994,7 +1121,7 @@ abstract class Repository implements Selectable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !in_array($alias, $this->getBlacklistedQueryColumns())
|
return !in_array($alias, $this->getBlacklistedQueryColumns($table))
|
||||||
&& $this->validateQueryColumnAssociation($table, $name);
|
&& $this->validateQueryColumnAssociation($table, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,7 +1146,7 @@ abstract class Repository implements Selectable
|
|||||||
throw new QueryException(t('Query column "%s" not found'), $name);
|
throw new QueryException(t('Query column "%s" not found'), $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($alias, $this->getBlacklistedQueryColumns())) {
|
if (in_array($alias, $this->getBlacklistedQueryColumns($table))) {
|
||||||
throw new QueryException(t('Column "%s" cannot be queried'), $name);
|
throw new QueryException(t('Column "%s" cannot be queried'), $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1107,7 +1234,7 @@ abstract class Repository implements Selectable
|
|||||||
throw new StatementException('Statement column "%s" not found', $name);
|
throw new StatementException('Statement column "%s" not found', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($alias, $this->getBlacklistedQueryColumns())) {
|
if (in_array($alias, $this->getBlacklistedQueryColumns($table))) {
|
||||||
throw new StatementException('Column "%s" cannot be referenced in a statement', $name);
|
throw new StatementException('Column "%s" cannot be referenced in a statement', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +69,12 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
*/
|
*/
|
||||||
public function __clone()
|
public function __clone()
|
||||||
{
|
{
|
||||||
$this->query = clone $this->query;
|
if ($this->query !== null) {
|
||||||
$this->iterator = clone $this->iterator;
|
$this->query = clone $this->query;
|
||||||
|
}
|
||||||
|
if ($this->iterator !== null) {
|
||||||
|
$this->iterator = clone $this->iterator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,7 +109,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
*/
|
*/
|
||||||
public function from($target, array $columns = null)
|
public function from($target, array $columns = null)
|
||||||
{
|
{
|
||||||
$this->query = $this->repository->getDataSource()->select();
|
$this->query = $this->repository->getDataSource($target)->select();
|
||||||
$this->query->from($this->repository->requireTable($target, $this));
|
$this->query->from($this->repository->requireTable($target, $this));
|
||||||
$this->query->columns($this->prepareQueryColumns($target, $columns));
|
$this->query->columns($this->prepareQueryColumns($target, $columns));
|
||||||
$this->target = $target;
|
$this->target = $target;
|
||||||
@ -200,7 +204,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
*/
|
*/
|
||||||
public function getFilterColumns()
|
public function getFilterColumns()
|
||||||
{
|
{
|
||||||
return $this->repository->getFilterColumns();
|
return $this->repository->getFilterColumns($this->target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -210,7 +214,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
*/
|
*/
|
||||||
public function getSearchColumns()
|
public function getSearchColumns()
|
||||||
{
|
{
|
||||||
return $this->repository->getSearchColumns();
|
return $this->repository->getSearchColumns($this->target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -290,7 +294,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
*/
|
*/
|
||||||
public function getSortRules()
|
public function getSortRules()
|
||||||
{
|
{
|
||||||
return $this->repository->getSortRules();
|
return $this->repository->getSortRules($this->target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -712,7 +716,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
|
|||||||
if ($this->query instanceof Traversable) {
|
if ($this->query instanceof Traversable) {
|
||||||
$iterator = $this->query;
|
$iterator = $this->query;
|
||||||
} else {
|
} else {
|
||||||
$iterator = $this->repository->getDataSource()->query($this->query);
|
$iterator = $this->repository->getDataSource($this->target)->query($this->query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($iterator instanceof IteratorAggregate) {
|
if ($iterator instanceof IteratorAggregate) {
|
||||||
|
158
library/Icinga/Web/Announcement.php
Normal file
158
library/Icinga/Web/Announcement.php
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An announcement to be displayed prominently in the web UI
|
||||||
|
*/
|
||||||
|
class Announcement
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $author;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $end;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash of the message
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $hash = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announcement constructor
|
||||||
|
*
|
||||||
|
* @param array $properties
|
||||||
|
*/
|
||||||
|
public function __construct(array $properties = array())
|
||||||
|
{
|
||||||
|
foreach ($properties as $key => $value) {
|
||||||
|
$method = 'set' . ucfirst($key);
|
||||||
|
if (method_exists($this, $method)) {
|
||||||
|
$this->$method($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the author of the acknowledged
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAuthor()
|
||||||
|
{
|
||||||
|
return $this->author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the author of the acknowledged
|
||||||
|
*
|
||||||
|
* @param string $author
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAuthor($author)
|
||||||
|
{
|
||||||
|
$this->author = $author;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message of the acknowledged
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMessage()
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the message of the acknowledged
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setMessage($message)
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
$this->hash = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start date and time of the acknowledged
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getStart()
|
||||||
|
{
|
||||||
|
return $this->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the start date and time of the acknowledged
|
||||||
|
*
|
||||||
|
* @param int $start
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setStart($start)
|
||||||
|
{
|
||||||
|
$this->start = $start;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the end date and time of the acknowledged
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEnd()
|
||||||
|
{
|
||||||
|
return $this->end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the end date and time of the acknowledged
|
||||||
|
*
|
||||||
|
* @param int $end
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEnd($end)
|
||||||
|
{
|
||||||
|
$this->end = $end;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hash of the acknowledgement
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHash()
|
||||||
|
{
|
||||||
|
if ($this->hash === null) {
|
||||||
|
$this->hash = md5($this->message);
|
||||||
|
}
|
||||||
|
return $this->hash;
|
||||||
|
}
|
||||||
|
}
|
137
library/Icinga/Web/Announcement/AnnouncementCookie.php
Normal file
137
library/Icinga/Web/Announcement/AnnouncementCookie.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web\Announcement;
|
||||||
|
|
||||||
|
use Icinga\Web\Cookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle acknowledged announcements via cookie
|
||||||
|
*/
|
||||||
|
class AnnouncementCookie extends Cookie
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of hashes representing acknowledged announcements
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $acknowledged = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ETag of the last known announcements.ini
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of the next active acknowledgement, if any
|
||||||
|
*
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
protected $nextActive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AnnouncementCookie constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('icingaweb2-announcements');
|
||||||
|
$this->setExpire(2147483648);
|
||||||
|
if (isset($_COOKIE['icingaweb2-announcements'])) {
|
||||||
|
$cookie = json_decode($_COOKIE['icingaweb2-announcements'], true);
|
||||||
|
if ($cookie !== null) {
|
||||||
|
if (isset($cookie['acknowledged'])) {
|
||||||
|
$this->setAcknowledged($cookie['acknowledged']);
|
||||||
|
}
|
||||||
|
if (isset($cookie['etag'])) {
|
||||||
|
$this->setEtag($cookie['etag']);
|
||||||
|
}
|
||||||
|
if (isset($cookie['next'])) {
|
||||||
|
$this->setNextActive($cookie['next']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hashes of the acknowledged announcements
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getAcknowledged()
|
||||||
|
{
|
||||||
|
return $this->acknowledged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the hashes of the acknowledged announcements
|
||||||
|
*
|
||||||
|
* @param string[] $acknowledged
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAcknowledged(array $acknowledged)
|
||||||
|
{
|
||||||
|
$this->acknowledged = $acknowledged;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ETag
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getEtag()
|
||||||
|
{
|
||||||
|
return $this->etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ETag
|
||||||
|
*
|
||||||
|
* @param string $etag
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setEtag($etag)
|
||||||
|
{
|
||||||
|
$this->etag = $etag;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp of the next active announcement
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getNextActive()
|
||||||
|
{
|
||||||
|
return $this->nextActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timestamp of the next active announcement
|
||||||
|
*
|
||||||
|
* @param int $nextActive
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNextActive($nextActive)
|
||||||
|
{
|
||||||
|
$this->nextActive = $nextActive;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getValue()
|
||||||
|
{
|
||||||
|
return json_encode(array(
|
||||||
|
'acknowledged' => $this->getAcknowledged(),
|
||||||
|
'etag' => $this->getEtag(),
|
||||||
|
'next' => $this->getNextActive()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
162
library/Icinga/Web/Announcement/AnnouncementIniRepository.php
Normal file
162
library/Icinga/Web/Announcement/AnnouncementIniRepository.php
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web\Announcement;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Icinga\Data\ConfigObject;
|
||||||
|
use Icinga\Data\Filter\Filter;
|
||||||
|
use Icinga\Data\Filter\FilterAnd;
|
||||||
|
use Icinga\Data\SimpleQuery;
|
||||||
|
use Icinga\Repository\IniRepository;
|
||||||
|
use Icinga\Web\Announcement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of announcements stored in an INI file
|
||||||
|
*/
|
||||||
|
class AnnouncementIniRepository extends IniRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $queryColumns = array('announcement' => array('id', 'author', 'message', 'hash', 'start', 'end'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $triggers = array('announcement');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected $configs = array('announcement' => array(
|
||||||
|
'name' => 'announcements',
|
||||||
|
'keyColumn' => 'id'
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected $conversionRules = array('announcement' => array(
|
||||||
|
'start' => 'timestamp',
|
||||||
|
'end' => 'timestamp'
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a DateTime from a timestamp
|
||||||
|
*
|
||||||
|
* @param string $timestamp
|
||||||
|
*
|
||||||
|
* @return DateTime|null
|
||||||
|
*/
|
||||||
|
protected function retrieveTimestamp($timestamp)
|
||||||
|
{
|
||||||
|
if ($timestamp !== null) {
|
||||||
|
$dateTime = new DateTime();
|
||||||
|
$dateTime->setTimestamp($timestamp);
|
||||||
|
return $dateTime;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a DateTime's timestamp
|
||||||
|
*
|
||||||
|
* @param DateTime $datetime
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
protected function persistTimestamp(DateTime $datetime)
|
||||||
|
{
|
||||||
|
return $datetime === null ? null : $datetime->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before-insert trigger (per row)
|
||||||
|
*
|
||||||
|
* @param ConfigObject $new The original data to insert
|
||||||
|
*
|
||||||
|
* @return ConfigObject The eventually modified data to insert
|
||||||
|
*/
|
||||||
|
protected function onInsertAnnouncement(ConfigObject $new)
|
||||||
|
{
|
||||||
|
if (! isset($new->id)) {
|
||||||
|
$new->id = uniqid();
|
||||||
|
}
|
||||||
|
if (! isset($new->hash)) {
|
||||||
|
$announcement = new Announcement($new->toArray());
|
||||||
|
$new->hash = $announcement->getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before-update trigger (per row)
|
||||||
|
*
|
||||||
|
* @param ConfigObject $old The original data as currently stored
|
||||||
|
* @param ConfigObject $new The original data to update
|
||||||
|
*
|
||||||
|
* @return ConfigObject The eventually modified data to update
|
||||||
|
*/
|
||||||
|
protected function onUpdateAnnouncement(ConfigObject $old, ConfigObject $new)
|
||||||
|
{
|
||||||
|
if ($new->message !== $old->message) {
|
||||||
|
$announcement = new Announcement($new->toArray());
|
||||||
|
$new->hash = $announcement->getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ETag of the announcements.ini file
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getEtag()
|
||||||
|
{
|
||||||
|
$file = $this->getDataSource('announcement')->getConfigFile();
|
||||||
|
if (@is_readable($file)) {
|
||||||
|
$mtime = filemtime($file);
|
||||||
|
$size = filesize($file);
|
||||||
|
return hash('crc32', $mtime . $size);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query for all active announcements
|
||||||
|
*
|
||||||
|
* @return SimpleQuery
|
||||||
|
*/
|
||||||
|
public function findActive()
|
||||||
|
{
|
||||||
|
$now = new DateTime();
|
||||||
|
$query = $this
|
||||||
|
->select(array('hash', 'message'))
|
||||||
|
->setFilter(new FilterAnd(array(
|
||||||
|
Filter::expression('start', '<=', $now),
|
||||||
|
Filter::expression('end', '>=', $now)
|
||||||
|
)))
|
||||||
|
->order('start');
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp of the next active announcement
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function findNextActive()
|
||||||
|
{
|
||||||
|
$now = new DateTime();
|
||||||
|
$query = $this
|
||||||
|
->select(array('start'))
|
||||||
|
->setFilter(Filter::expression('start', '>', $now))
|
||||||
|
->order('start')
|
||||||
|
->limit(1);
|
||||||
|
$nextActive = $query->fetchRow();
|
||||||
|
return $nextActive !== false ? $nextActive->start->getTimestamp() : null;
|
||||||
|
}
|
||||||
|
}
|
@ -99,6 +99,8 @@ class ActionController extends Zend_Controller_Action
|
|||||||
Zend_Controller_Response_Abstract $response,
|
Zend_Controller_Response_Abstract $response,
|
||||||
array $invokeArgs = array()
|
array $invokeArgs = array()
|
||||||
) {
|
) {
|
||||||
|
/** @var \Icinga\Web\Request $request */
|
||||||
|
/** @var \Icinga\Web\Response $response */
|
||||||
$this->params = UrlParams::fromQueryString();
|
$this->params = UrlParams::fromQueryString();
|
||||||
|
|
||||||
$this->setRequest($request)
|
$this->setRequest($request)
|
||||||
@ -124,7 +126,11 @@ class ActionController extends Zend_Controller_Action
|
|||||||
$this->_helper->layout()->disableLayout();
|
$this->_helper->layout()->disableLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $auth->authenticate($request, $response, $this->requiresLogin());
|
||||||
if ($this->requiresLogin()) {
|
if ($this->requiresLogin()) {
|
||||||
|
if (! $request->isXmlHttpRequest() && $request->isApiRequest()) {
|
||||||
|
Auth::getInstance()->challengeHttp();
|
||||||
|
}
|
||||||
$this->redirectToLogin(Url::fromRequest());
|
$this->redirectToLogin(Url::fromRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +185,7 @@ class ActionController extends Zend_Controller_Action
|
|||||||
*/
|
*/
|
||||||
public function assertPermission($permission)
|
public function assertPermission($permission)
|
||||||
{
|
{
|
||||||
if ($this->requiresAuthentication && ! $this->Auth()->hasPermission($permission)) {
|
if (! $this->Auth()->hasPermission($permission)) {
|
||||||
throw new SecurityException('No permission for %s', $permission);
|
throw new SecurityException('No permission for %s', $permission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,8 +261,9 @@ class ActionController extends Zend_Controller_Action
|
|||||||
/**
|
/**
|
||||||
* Return restriction information for an eventually authenticated user
|
* Return restriction information for an eventually authenticated user
|
||||||
*
|
*
|
||||||
* @param string $name Permission name
|
* @param string $name Restriction name
|
||||||
* @return Array
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getRestrictions($name)
|
public function getRestrictions($name)
|
||||||
{
|
{
|
||||||
@ -268,15 +275,14 @@ class ActionController extends Zend_Controller_Action
|
|||||||
* user is currently not authenticated
|
* user is currently not authenticated
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @see requiresAuthentication
|
|
||||||
*/
|
*/
|
||||||
protected function requiresLogin()
|
protected function requiresLogin()
|
||||||
{
|
{
|
||||||
if (!$this->requiresAuthentication) {
|
if (! $this->requiresAuthentication) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !$this->Auth()->isAuthenticated();
|
return ! $this->Auth()->isAuthenticated();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +26,8 @@ class ModuleActionController extends ActionController
|
|||||||
protected function prepareInit()
|
protected function prepareInit()
|
||||||
{
|
{
|
||||||
$this->moduleInit();
|
$this->moduleInit();
|
||||||
if ($this->getFrontController()->getDefaultModule() !== $this->getModuleName()) {
|
if (($this->Auth()->isAuthenticated() || $this->requiresLogin())
|
||||||
|
&& $this->getFrontController()->getDefaultModule() !== $this->getModuleName()) {
|
||||||
$this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName());
|
$this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -948,7 +948,7 @@ class Form extends Zend_Form
|
|||||||
if ($this->getUseFormAutosubmit()) {
|
if ($this->getUseFormAutosubmit()) {
|
||||||
$warningId = 'autosubmit_warning_' . $el->getId();
|
$warningId = 'autosubmit_warning_' . $el->getId();
|
||||||
$warningText = $this->getView()->escape($this->translate(
|
$warningText = $this->getView()->escape($this->translate(
|
||||||
'Upon its value has changed, this field issues an automatic update of this page.'
|
'Upon its value changing, this field issues an automatic update of this page.'
|
||||||
));
|
));
|
||||||
$autosubmitDecorator = $this->_getDecorator('Callback', array(
|
$autosubmitDecorator = $this->_getDecorator('Callback', array(
|
||||||
'placement' => 'PREPEND',
|
'placement' => 'PREPEND',
|
||||||
@ -1576,30 +1576,38 @@ class Form extends Zend_Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a error notification and prevent the form from being successfully validated
|
* Add a error notification
|
||||||
*
|
*
|
||||||
* @param string|array $message The notification message
|
* @param string|array $message The notification message
|
||||||
|
* @param bool $markAsError Whether to prevent the form from being successfully validated or not
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function error($message)
|
public function error($message, $markAsError = true)
|
||||||
{
|
{
|
||||||
$this->addNotification($message, self::NOTIFICATION_ERROR);
|
$this->addNotification($message, self::NOTIFICATION_ERROR);
|
||||||
$this->markAsError();
|
if ($markAsError) {
|
||||||
|
$this->markAsError();
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a warning notification and prevent the form from being successfully validated
|
* Add a warning notification
|
||||||
*
|
*
|
||||||
* @param string|array $message The notification message
|
* @param string|array $message The notification message
|
||||||
|
* @param bool $markAsError Whether to prevent the form from being successfully validated or not
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function warning($message)
|
public function warning($message, $markAsError = true)
|
||||||
{
|
{
|
||||||
$this->addNotification($message, self::NOTIFICATION_WARNING);
|
$this->addNotification($message, self::NOTIFICATION_WARNING);
|
||||||
$this->markAsError();
|
if ($markAsError) {
|
||||||
|
$this->markAsError();
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class Autosubmit extends Zend_Form_Decorator_Abstract
|
|||||||
$isForm = $this->getElement() instanceof Form;
|
$isForm = $this->getElement() instanceof Form;
|
||||||
$warning = $isForm
|
$warning = $isForm
|
||||||
? t('Upon any of this form\'s fields were changed, this page is being updated automatically.')
|
? t('Upon any of this form\'s fields were changed, this page is being updated automatically.')
|
||||||
: t('Upon its value has changed, this field issues an automatic update of this page.');
|
: t('Upon its value changing, this field issues an automatic update of this page.');
|
||||||
$content .= $this->getView()->icon('cw', $warning, array(
|
$content .= $this->getView()->icon('cw', $warning, array(
|
||||||
'aria-hidden' => $isForm ? 'false' : 'true',
|
'aria-hidden' => $isForm ? 'false' : 'true',
|
||||||
'class' => 'spinner autosubmit-info'
|
'class' => 'spinner autosubmit-info'
|
||||||
|
39
library/Icinga/Web/Form/Validator/InternalUrlValidator.php
Normal file
39
library/Icinga/Web/Form/Validator/InternalUrlValidator.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web\Form\Validator;
|
||||||
|
|
||||||
|
use Zend_Validate_Abstract;
|
||||||
|
use Icinga\Web\Url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator that checks whether a textfield doesn't contain an external URL
|
||||||
|
*/
|
||||||
|
class InternalUrlValidator extends Zend_Validate_Abstract
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isValid($value)
|
||||||
|
{
|
||||||
|
if (Url::fromPath($value)->isExternal()) {
|
||||||
|
$this->_error('IS_EXTERNAL');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function _error($messageKey, $value = null)
|
||||||
|
{
|
||||||
|
if ($messageKey === 'IS_EXTERNAL') {
|
||||||
|
$this->_messages[$messageKey] = t('The url must not be external.');
|
||||||
|
} else {
|
||||||
|
parent::_error($messageKey, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -117,7 +117,7 @@ class JavaScript
|
|||||||
|
|
||||||
// We do not minify vendor files
|
// We do not minify vendor files
|
||||||
foreach ($vendorFiles as $file) {
|
foreach ($vendorFiles as $file) {
|
||||||
$out .= file_get_contents($file);
|
$out .= ';' . ltrim(trim(file_get_contents($file)), ';') . "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($jsFiles as $file) {
|
foreach ($jsFiles as $file) {
|
||||||
|
@ -565,7 +565,7 @@ class Navigation implements ArrayAccess, Countable, IteratorAggregate
|
|||||||
{
|
{
|
||||||
$navigation = new static();
|
$navigation = new static();
|
||||||
foreach ($array as $name => $properties) {
|
foreach ($array as $name => $properties) {
|
||||||
$navigation->addItem($name, $properties);
|
$navigation->addItem((string) $name, $properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $navigation;
|
return $navigation;
|
||||||
|
@ -340,7 +340,7 @@ class NavigationItem implements IteratorAggregate
|
|||||||
*/
|
*/
|
||||||
public function hasChildren()
|
public function hasChildren()
|
||||||
{
|
{
|
||||||
return !$this->getChildren()->isEmpty();
|
return ! $this->getChildren()->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -791,7 +791,7 @@ class NavigationItem implements IteratorAggregate
|
|||||||
public function getRender()
|
public function getRender()
|
||||||
{
|
{
|
||||||
if ($this->render === null) {
|
if ($this->render === null) {
|
||||||
return true;
|
return $this->getUrl() !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render;
|
return $this->render;
|
||||||
|
@ -3,44 +3,70 @@
|
|||||||
|
|
||||||
namespace Icinga\Web\Navigation\Renderer;
|
namespace Icinga\Web\Navigation\Renderer;
|
||||||
|
|
||||||
use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summary badge adding up all badges in the navigation's children that have the same state
|
* Badge renderer summing up the worst state of its children
|
||||||
*/
|
*/
|
||||||
class SummaryNavigationItemRenderer extends BadgeNavigationItemRenderer
|
class SummaryNavigationItemRenderer extends BadgeNavigationItemRenderer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The title of each summarized child
|
* Cached count
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State to severity map
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $titles;
|
protected static $stateSeverityMap = array(
|
||||||
|
self::STATE_OK => 0,
|
||||||
|
self::STATE_PENDING => 1,
|
||||||
|
self::STATE_UNKNOWN => 2,
|
||||||
|
self::STATE_WARNING => 3,
|
||||||
|
self::STATE_CRITICAL => 4,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Severity to state map
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $severityStateMap = array(
|
||||||
|
self::STATE_OK,
|
||||||
|
self::STATE_PENDING,
|
||||||
|
self::STATE_UNKNOWN,
|
||||||
|
self::STATE_WARNING,
|
||||||
|
self::STATE_CRITICAL
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getCount()
|
public function getCount()
|
||||||
{
|
{
|
||||||
$count = 0;
|
if ($this->count === null) {
|
||||||
foreach ($this->getItem()->getChildren() as $child) {
|
$countMap = array_fill(0, 5, 0);
|
||||||
$renderer = $child->getRenderer();
|
$maxSeverity = 0;
|
||||||
if ($renderer instanceof BadgeNavigationItemRenderer) {
|
$titles = array();
|
||||||
if ($renderer->getState() === $this->getState()) {
|
foreach ($this->getItem()->getChildren() as $child) {
|
||||||
$this->titles[] = $renderer->getTitle();
|
$renderer = $child->getRenderer();
|
||||||
$count += $renderer->getCount();
|
if ($renderer instanceof BadgeNavigationItemRenderer) {
|
||||||
|
$count = $renderer->getCount();
|
||||||
|
if ($count) {
|
||||||
|
$severity = static::$stateSeverityMap[$renderer->getState()];
|
||||||
|
$countMap[$severity] += $count;
|
||||||
|
$titles[] = $renderer->getTitle();
|
||||||
|
$maxSeverity = max($maxSeverity, $severity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->count = $countMap[$maxSeverity];
|
||||||
|
$this->state = static::$severityStateMap[$maxSeverity];
|
||||||
|
$this->title = implode('. ', $titles);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $count;
|
return $this->count;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return ! empty($this->titles) ? join(', ', $this->titles) : '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,13 @@ use Icinga\Web\Response\JsonResponse;
|
|||||||
*/
|
*/
|
||||||
class Response extends Zend_Controller_Response_Http
|
class Response extends Zend_Controller_Response_Http
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The default content type being used for responses
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const DEFAULT_CONTENT_TYPE = 'text/html; charset=UTF-8';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto-refresh interval
|
* Auto-refresh interval
|
||||||
*
|
*
|
||||||
@ -146,6 +153,31 @@ class Response extends Zend_Controller_Response_Http
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all header values for the given name
|
||||||
|
*
|
||||||
|
* @param string $name The name of the header
|
||||||
|
* @param bool $lastOnly If this is true, the last value will be returned as a string
|
||||||
|
*
|
||||||
|
* @return null|array|string
|
||||||
|
*/
|
||||||
|
public function getHeader($name, $lastOnly = false)
|
||||||
|
{
|
||||||
|
$result = ($lastOnly ? null : array());
|
||||||
|
$headers = $this->getHeaders();
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
if ($header['name'] === $name) {
|
||||||
|
if ($lastOnly) {
|
||||||
|
$result = $header['value'];
|
||||||
|
} else {
|
||||||
|
$result[] = $header['value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the request
|
* Get the request
|
||||||
*
|
*
|
||||||
@ -210,9 +242,11 @@ class Response extends Zend_Controller_Response_Http
|
|||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public static function json()
|
public function json()
|
||||||
{
|
{
|
||||||
return new JsonResponse();
|
$response = new JsonResponse();
|
||||||
|
$response->copyMetaDataFrom($this);
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -242,6 +276,10 @@ class Response extends Zend_Controller_Response_Http
|
|||||||
$this->setRedirect($redirectUrl->getAbsoluteUrl());
|
$this->setRedirect($redirectUrl->getAbsoluteUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! $this->getHeader('Content-Type', true)) {
|
||||||
|
$this->setHeader('Content-Type', static::DEFAULT_CONTENT_TYPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,4 +330,20 @@ class Response extends Zend_Controller_Response_Http
|
|||||||
}
|
}
|
||||||
return parent::sendHeaders();
|
return parent::sendHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies non-body-related response data from $response
|
||||||
|
*
|
||||||
|
* @param Response $response
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function copyMetaDataFrom(self $response)
|
||||||
|
{
|
||||||
|
$this->_headers = $response->_headers;
|
||||||
|
$this->_headersRaw = $response->_headersRaw;
|
||||||
|
$this->_httpResponseCode = $response->_httpResponseCode;
|
||||||
|
$this->headersSentThrowsException = $response->headersSentThrowsException;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,11 @@ use Icinga\Web\Response;
|
|||||||
*/
|
*/
|
||||||
class JsonResponse extends Response
|
class JsonResponse extends Response
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
const DEFAULT_CONTENT_TYPE = 'application/json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status identifier for failed API calls due to an error on the server
|
* Status identifier for failed API calls due to an error on the server
|
||||||
*
|
*
|
||||||
@ -121,7 +126,7 @@ class JsonResponse extends Response
|
|||||||
*/
|
*/
|
||||||
public function getFailData()
|
public function getFailData()
|
||||||
{
|
{
|
||||||
return $this->failData;
|
return (! is_array($this->failData) || empty($this->failData)) ? null : $this->failData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,9 +178,11 @@ class JsonResponse extends Response
|
|||||||
switch ($this->status) {
|
switch ($this->status) {
|
||||||
case static::STATUS_ERROR:
|
case static::STATUS_ERROR:
|
||||||
$body['message'] = $this->getErrorMessage();
|
$body['message'] = $this->getErrorMessage();
|
||||||
break;
|
|
||||||
case static::STATUS_FAIL:
|
case static::STATUS_FAIL:
|
||||||
$body['data'] = $this->getFailData();
|
$failData = $this->getFailData();
|
||||||
|
if ($failData !== null || $this->status === static::STATUS_FAIL) {
|
||||||
|
$body['data'] = $failData;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case static::STATUS_SUCCESS:
|
case static::STATUS_SUCCESS:
|
||||||
$body['data'] = $this->getSuccessData();
|
$body['data'] = $this->getSuccessData();
|
||||||
@ -184,15 +191,6 @@ class JsonResponse extends Response
|
|||||||
echo json_encode($body, $this->getEncodingOptions());
|
echo json_encode($body, $this->getEncodingOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function sendHeaders()
|
|
||||||
{
|
|
||||||
$this->setHeader('Content-Type', 'application/json', true);
|
|
||||||
parent::sendHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user