commit
8006090108
|
@ -6,6 +6,7 @@ namespace Icinga\Controllers;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Forms\Authentication\LoginForm;
|
use Icinga\Forms\Authentication\LoginForm;
|
||||||
use Icinga\Web\Controller;
|
use Icinga\Web\Controller;
|
||||||
|
use Icinga\Web\Helper\CookieHelper;
|
||||||
use Icinga\Web\Url;
|
use Icinga\Web\Url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,13 +38,14 @@ class AuthenticationController extends Controller
|
||||||
$this->redirectNow($form->getRedirectUrl());
|
$this->redirectNow($form->getRedirectUrl());
|
||||||
}
|
}
|
||||||
if (! $requiresSetup) {
|
if (! $requiresSetup) {
|
||||||
if (! $this->getRequest()->hasCookieSupport()) {
|
$cookies = new CookieHelper($this->getRequest());
|
||||||
|
if (! $cookies->isSupported()) {
|
||||||
$this
|
$this
|
||||||
->getResponse()
|
->getResponse()
|
||||||
->setBody("Cookies must be enabled to run this application.\n")
|
->setBody("Cookies must be enabled to run this application.\n")
|
||||||
->setHttpResponseCode(403)
|
->setHttpResponseCode(403)
|
||||||
->sendResponse();
|
->sendResponse();
|
||||||
exit();
|
exit;
|
||||||
}
|
}
|
||||||
$form->handleRequest();
|
$form->handleRequest();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class ErrorController extends ActionController
|
||||||
Logger::error('Stacktrace: %s', $exception->getTraceAsString());
|
Logger::error('Stacktrace: %s', $exception->getTraceAsString());
|
||||||
|
|
||||||
if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) {
|
if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) {
|
||||||
$this->innerLayout = 'error';
|
$this->innerLayout = 'guest-error';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($error->type) {
|
switch ($error->type) {
|
||||||
|
@ -89,6 +89,7 @@ class ErrorController extends ActionController
|
||||||
if ($this->getInvokeArg('displayExceptions')) {
|
if ($this->getInvokeArg('displayExceptions')) {
|
||||||
$this->view->stackTrace = $exception->getTraceAsString();
|
$this->view->stackTrace = $exception->getTraceAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ use Icinga\Web\Controller;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Web\FileCache;
|
use Icinga\Web\FileCache;
|
||||||
use Icinga\Web\LessCompiler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delivery static content to clients
|
* Delivery static content to clients
|
||||||
|
@ -87,94 +86,4 @@ class StaticController extends Controller
|
||||||
|
|
||||||
readfile($filePath);
|
readfile($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a javascript file from the application's or the module's public folder
|
|
||||||
*/
|
|
||||||
public function javascriptAction()
|
|
||||||
{
|
|
||||||
$module = $this->_getParam('module_name');
|
|
||||||
$file = $this->_getParam('file');
|
|
||||||
|
|
||||||
if ($module == 'app') {
|
|
||||||
$basedir = Icinga::app()->getApplicationDir('../public/js/icinga/components/');
|
|
||||||
$filePath = $basedir . $file;
|
|
||||||
} else {
|
|
||||||
if (! Icinga::app()->getModuleManager()->hasEnabled($module)) {
|
|
||||||
Logger::error(
|
|
||||||
'Non-existing frontend component "' . $module . '/' . $file
|
|
||||||
. '" was requested. The module "' . $module . '" does not exist or is not active.'
|
|
||||||
);
|
|
||||||
echo "/** Module not enabled **/";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir();
|
|
||||||
$filePath = $basedir . '/public/js/' . $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! file_exists($filePath)) {
|
|
||||||
Logger::error(
|
|
||||||
'Non-existing frontend component "' . $module . '/' . $file
|
|
||||||
. '" was requested, which would resolve to the the path: ' . $filePath
|
|
||||||
);
|
|
||||||
echo '/** Module has no js files **/';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$response = $this->getResponse();
|
|
||||||
$response->setHeader('Content-Type', 'text/javascript');
|
|
||||||
$this->setCacheHeader();
|
|
||||||
|
|
||||||
$response->setHeader(
|
|
||||||
'Last-Modified',
|
|
||||||
gmdate('D, d M Y H:i:s', filemtime($filePath)) . ' GMT'
|
|
||||||
);
|
|
||||||
|
|
||||||
readfile($filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set cache header for the response
|
|
||||||
*
|
|
||||||
* @param int $maxAge The maximum age to set
|
|
||||||
*/
|
|
||||||
private function setCacheHeader($maxAge = 3600)
|
|
||||||
{
|
|
||||||
$maxAge = (int) $maxAge;
|
|
||||||
$this
|
|
||||||
->getResponse()
|
|
||||||
->setHeader('Cache-Control', sprintf('max-age=%d', $maxAge), true)
|
|
||||||
->setHeader('Pragma', 'cache', true)
|
|
||||||
->setHeader(
|
|
||||||
'Expires',
|
|
||||||
gmdate('D, d M Y H:i:s', time() + $maxAge) . ' GMT',
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send application's and modules' CSS
|
|
||||||
*/
|
|
||||||
public function stylesheetAction()
|
|
||||||
{
|
|
||||||
$lessCompiler = new LessCompiler();
|
|
||||||
$moduleManager = Icinga::app()->getModuleManager();
|
|
||||||
|
|
||||||
$publicDir = realpath(dirname($_SERVER['SCRIPT_FILENAME']));
|
|
||||||
|
|
||||||
$lessCompiler->addItem($publicDir . '/css/vendor');
|
|
||||||
$lessCompiler->addItem($publicDir . '/css/icinga');
|
|
||||||
|
|
||||||
foreach ($moduleManager->getLoadedModules() as $moduleName) {
|
|
||||||
$cssDir = $moduleName->getCssDir();
|
|
||||||
|
|
||||||
if (is_dir($cssDir)) {
|
|
||||||
$lessCompiler->addItem($cssDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->getResponse()->setHeader('Content-Type', 'text/css');
|
|
||||||
$this->setCacheHeader(3600);
|
|
||||||
|
|
||||||
$lessCompiler->printStack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@ use Icinga\Data\ResourceFactory;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form class to modify the general application configuration
|
* Configuration form for general application options
|
||||||
|
*
|
||||||
|
* This form is not used directly but as subform to the {@link GeneralConfigForm}.
|
||||||
*/
|
*/
|
||||||
class ApplicationConfigForm extends Form
|
class ApplicationConfigForm extends Form
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initialize this form
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
|
@ -21,7 +23,9 @@ class ApplicationConfigForm extends Form
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Form::createElements()
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function createElements(array $formData)
|
public function createElements(array $formData)
|
||||||
{
|
{
|
||||||
|
@ -68,6 +72,7 @@ class ApplicationConfigForm extends Form
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'db') {
|
if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'db') {
|
||||||
$backends = array();
|
$backends = array();
|
||||||
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
|
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
|
||||||
|
|
|
@ -6,10 +6,15 @@ namespace Icinga\Forms\Config\General;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration form for logging options
|
||||||
|
*
|
||||||
|
* This form is not used directly but as subform for the {@link GeneralConfigForm}.
|
||||||
|
*/
|
||||||
class LoggingConfigForm extends Form
|
class LoggingConfigForm extends Form
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initialize this form
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
|
@ -17,8 +22,9 @@ class LoggingConfigForm extends Form
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (non-PHPDoc)
|
* {@inheritdoc}
|
||||||
* @see Form::createElements() For the method documentation.
|
*
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function createElements(array $formData)
|
public function createElements(array $formData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Forms\Config\General;
|
||||||
|
|
||||||
|
use Icinga\Application\Icinga;
|
||||||
|
use Icinga\Application\Logger;
|
||||||
|
use Icinga\Web\Form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration form for theming options
|
||||||
|
*
|
||||||
|
* This form is not used directly but as subform for the {@link GeneralConfigForm}.
|
||||||
|
*/
|
||||||
|
class ThemingConfigForm extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->setName('form_config_general_theming');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function createElements(array $formData)
|
||||||
|
{
|
||||||
|
$this->addElement(
|
||||||
|
'select',
|
||||||
|
'themes_default',
|
||||||
|
array(
|
||||||
|
'description' => $this->translate('The default theme', 'Form element description'),
|
||||||
|
'label' => $this->translate('Default Theme', 'Form element label'),
|
||||||
|
'multiOptions' => Icinga::app()->getThemes()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->addElement(
|
||||||
|
'checkbox',
|
||||||
|
'themes_disabled',
|
||||||
|
array(
|
||||||
|
'description' => $this->translate(
|
||||||
|
'Check this box for disallowing users to change the theme. If a default theme is set, it will be'
|
||||||
|
. ' used nonetheless',
|
||||||
|
'Form element description'
|
||||||
|
),
|
||||||
|
'label' => $this->translate('Disable Themes', 'Form element label')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getValues($suppressArrayNotation = false)
|
||||||
|
{
|
||||||
|
$values = parent::getValues($suppressArrayNotation);
|
||||||
|
if ($values['themes_default'] === 'Icinga') {
|
||||||
|
$values['themes_default'] = null;
|
||||||
|
}
|
||||||
|
if (! $values['themes_disabled']) {
|
||||||
|
$values['themes_disabled'] = null;
|
||||||
|
}
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,15 +5,16 @@ namespace Icinga\Forms\Config;
|
||||||
|
|
||||||
use Icinga\Forms\Config\General\ApplicationConfigForm;
|
use Icinga\Forms\Config\General\ApplicationConfigForm;
|
||||||
use Icinga\Forms\Config\General\LoggingConfigForm;
|
use Icinga\Forms\Config\General\LoggingConfigForm;
|
||||||
|
use Icinga\Forms\Config\General\ThemingConfigForm;
|
||||||
use Icinga\Forms\ConfigForm;
|
use Icinga\Forms\ConfigForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form class for application-wide and logging specific settings
|
* Configuration form for application-wide options
|
||||||
*/
|
*/
|
||||||
class GeneralConfigForm extends ConfigForm
|
class GeneralConfigForm extends ConfigForm
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initialize this configuration form
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
|
@ -22,13 +23,15 @@ class GeneralConfigForm extends ConfigForm
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Form::createElements()
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function createElements(array $formData)
|
public function createElements(array $formData)
|
||||||
{
|
{
|
||||||
$appConfigForm = new ApplicationConfigForm();
|
$appConfigForm = new ApplicationConfigForm();
|
||||||
$loggingConfigForm = new LoggingConfigForm();
|
$loggingConfigForm = new LoggingConfigForm();
|
||||||
$this->addElements($appConfigForm->createElements($formData)->getElements());
|
$themingConfigForm = new ThemingConfigForm();
|
||||||
$this->addElements($loggingConfigForm->createElements($formData)->getElements());
|
$this->addSubForm($appConfigForm->create($formData));
|
||||||
|
$this->addSubForm($loggingConfigForm->create($formData));
|
||||||
|
$this->addSubForm($themingConfigForm->create($formData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,21 +321,6 @@ class UserBackendConfigForm extends ConfigForm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all form element values
|
|
||||||
*
|
|
||||||
* @param bool $suppressArrayNotation Ignored
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['backend_form']);
|
|
||||||
unset($values['backend_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the given values are valid
|
* Return whether the given values are valid
|
||||||
*
|
*
|
||||||
|
|
|
@ -198,19 +198,4 @@ class UserGroupBackendForm extends ConfigForm
|
||||||
$this->populate($data);
|
$this->populate($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all form element values
|
|
||||||
*
|
|
||||||
* @param bool $suppressArrayNotation Ignored
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['backend_form']);
|
|
||||||
unset($values['backend_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ namespace Icinga\Forms;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Zend_Form_Decorator_Abstract;
|
use Zend_Form_Decorator_Abstract;
|
||||||
|
use Icinga\Application\Config;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
use Icinga\Web\Notification;
|
use Icinga\Web\Notification;
|
||||||
use Icinga\Application\Config;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form base-class providing standard functionality for configuration forms
|
* Form base-class providing standard functionality for configuration forms
|
||||||
|
@ -21,6 +21,23 @@ class ConfigForm extends Form
|
||||||
*/
|
*/
|
||||||
protected $config;
|
protected $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* Values from subforms are directly added to the returned values array instead of being grouped by the subforms'
|
||||||
|
* names.
|
||||||
|
*/
|
||||||
|
public function getValues($suppressArrayNotation = false)
|
||||||
|
{
|
||||||
|
$values = parent::getValues($suppressArrayNotation);
|
||||||
|
foreach (array_keys($this->_subForms) as $name) {
|
||||||
|
// Zend returns values from subforms grouped by their names, but we want them flat
|
||||||
|
$values = array_merge($values, $values[$name]);
|
||||||
|
unset($values[$name]);
|
||||||
|
}
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the configuration to use when populating the form or when saving the user's input
|
* Set the configuration to use when populating the form or when saving the user's input
|
||||||
*
|
*
|
||||||
|
|
|
@ -761,17 +761,6 @@ class NavigationConfigForm extends ConfigForm
|
||||||
return $valid;
|
return $valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['item_form']);
|
|
||||||
unset($values['item_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,12 +6,14 @@ namespace Icinga\Forms;
|
||||||
use Exception;
|
use Exception;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use Icinga\Application\Config;
|
use Icinga\Application\Config;
|
||||||
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Authentication\Auth;
|
use Icinga\Authentication\Auth;
|
||||||
use Icinga\User\Preferences;
|
use Icinga\User\Preferences;
|
||||||
use Icinga\User\Preferences\PreferencesStore;
|
use Icinga\User\Preferences\PreferencesStore;
|
||||||
use Icinga\Util\TimezoneDetect;
|
use Icinga\Util\TimezoneDetect;
|
||||||
use Icinga\Util\Translator;
|
use Icinga\Util\Translator;
|
||||||
|
use Icinga\Web\Cookie;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
use Icinga\Web\Notification;
|
use Icinga\Web\Notification;
|
||||||
use Icinga\Web\Session;
|
use Icinga\Web\Session;
|
||||||
|
@ -103,6 +105,14 @@ class PreferenceForm extends Form
|
||||||
|
|
||||||
Session::getSession()->user->setPreferences($this->preferences);
|
Session::getSession()->user->setPreferences($this->preferences);
|
||||||
|
|
||||||
|
if (($theme = $this->getElement('theme')) !== null
|
||||||
|
&& ($theme = $theme->getValue()) !== $this->getRequest()->getCookie('theme')
|
||||||
|
) {
|
||||||
|
$this->getResponse()
|
||||||
|
->setCookie(new Cookie('theme', $theme))
|
||||||
|
->setReloadCss(true);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($this->store && $this->getElement('btn_submit_preferences')->isChecked()) {
|
if ($this->store && $this->getElement('btn_submit_preferences')->isChecked()) {
|
||||||
$this->save();
|
$this->save();
|
||||||
|
@ -142,6 +152,20 @@ class PreferenceForm extends Form
|
||||||
*/
|
*/
|
||||||
public function createElements(array $formData)
|
public function createElements(array $formData)
|
||||||
{
|
{
|
||||||
|
if (! (bool) Config::app()->get('themes', 'disabled', false)) {
|
||||||
|
$themes = Icinga::app()->getThemes();
|
||||||
|
if (count($themes) > 1) {
|
||||||
|
$this->addElement(
|
||||||
|
'select',
|
||||||
|
'theme',
|
||||||
|
array(
|
||||||
|
'label' => $this->translate('Theme', 'Form element label'),
|
||||||
|
'multiOptions' => $themes
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$languages = array();
|
$languages = array();
|
||||||
$languages['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getLocale());
|
$languages['autodetect'] = sprintf($this->translate('Browser (%s)', 'preferences.form'), $this->getLocale());
|
||||||
foreach (Translator::getAvailableLocaleCodes() as $language) {
|
foreach (Translator::getAvailableLocaleCodes() as $language) {
|
||||||
|
|
|
@ -42,33 +42,7 @@ 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)',
|
||||||
'config/*' => $this->translate('Allow config access') . ' (config/*)',
|
'config/*' => $this->translate('Allow config access') . ' (config/*)'
|
||||||
/*
|
|
||||||
// [tg] seems excessive for me, hidden for rc1, tbd
|
|
||||||
'config/application/*' => 'config/application/*',
|
|
||||||
'config/application/general' => 'config/application/general',
|
|
||||||
'config/application/resources' => 'config/application/resources',
|
|
||||||
'config/application/userbackend' => 'config/application/userbackend',
|
|
||||||
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
|
|
||||||
'config/application/navigation' => 'config/application/navigation',
|
|
||||||
'config/authentication/*' => 'config/authentication/*',
|
|
||||||
'config/authentication/users/*' => 'config/authentication/users/*',
|
|
||||||
'config/authentication/users/show' => 'config/authentication/users/show',
|
|
||||||
'config/authentication/users/add' => 'config/authentication/users/add',
|
|
||||||
'config/authentication/users/edit' => 'config/authentication/users/edit',
|
|
||||||
'config/authentication/users/remove' => 'config/authentication/users/remove',
|
|
||||||
'config/authentication/groups/*' => 'config/authentication/groups/*',
|
|
||||||
'config/authentication/groups/show' => 'config/authentication/groups/show',
|
|
||||||
'config/authentication/groups/add' => 'config/authentication/groups/add',
|
|
||||||
'config/authentication/groups/edit' => 'config/authentication/groups/edit',
|
|
||||||
'config/authentication/groups/remove' => 'config/authentication/groups/remove',
|
|
||||||
'config/authentication/roles/*' => 'config/authentication/roles/*',
|
|
||||||
'config/authentication/roles/show' => 'config/authentication/roles/show',
|
|
||||||
'config/authentication/roles/add' => 'config/authentication/roles/add',
|
|
||||||
'config/authentication/roles/edit' => 'config/authentication/roles/edit',
|
|
||||||
'config/authentication/roles/remove' => 'config/authentication/roles/remove',
|
|
||||||
'config/modules' => 'config/modules'
|
|
||||||
*/
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$helper = new Zend_Form_Element('bogus');
|
$helper = new Zend_Form_Element('bogus');
|
||||||
|
|
|
@ -18,22 +18,18 @@ if ($this->layout()->autorefreshInterval) {
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div id="logo">
|
<div id="header-logo-container">
|
||||||
<?php if (Auth::getInstance()->isAuthenticated()): ?>
|
|
||||||
<?= $this->qlink(
|
<?= $this->qlink(
|
||||||
'',
|
'',
|
||||||
'dashboard',
|
Auth::getInstance()->isAuthenticated() ? 'dashboard' : '',
|
||||||
null,
|
null,
|
||||||
array(
|
array(
|
||||||
'icon' => 'img/logo_icinga-inv.png',
|
|
||||||
'data-base-target' => '_main',
|
|
||||||
'aria-hidden' => 'true',
|
'aria-hidden' => 'true',
|
||||||
|
'data-base-target' => '_main',
|
||||||
|
'id' => 'header-logo',
|
||||||
'tabindex' => -1
|
'tabindex' => -1
|
||||||
)
|
)
|
||||||
); ?>
|
); ?>
|
||||||
<?php else: ?>
|
|
||||||
<?= $this->icon('img/logo_icinga-inv.png'); ?>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php if (! $this->layout()->isIframe): ?>
|
<?php if (! $this->layout()->isIframe): ?>
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<div class="logo">
|
|
||||||
<div class="image">
|
|
||||||
<img aria-hidden="true" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="below-logo">
|
|
||||||
<?= $this->render('inline.phtml') ?>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div id="guest-error">
|
||||||
|
<div class="centered-ghost">
|
||||||
|
<div class="centered-content">
|
||||||
|
<div id="icinga-logo" aria-hidden="true"></div>
|
||||||
|
<div id="guest-error-message">
|
||||||
|
<?= $this->render('inline.phtml') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,4 +1,2 @@
|
||||||
<?= $this->layout()->moduleStart ?>
|
|
||||||
<?= $this->layout()->content ?>
|
<?= $this->layout()->content ?>
|
||||||
<?= $this->layout()->benchmark ?>
|
<?= $this->layout()->benchmark ?>
|
||||||
<?= $this->layout()->moduleEnd ?>
|
|
||||||
|
|
|
@ -41,15 +41,14 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<link rel="stylesheet" href="<?= $this->href($cssfile) ?>" media="screen" type="text/css" />
|
<link rel="stylesheet" href="<?= $this->href($cssfile) ?>" media="all" type="text/css" />
|
||||||
<!-- Respond.js IE8 support of media queries -->
|
<!-- Respond.js IE8 support of media queries -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<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') ?>" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body id="body">
|
<body id="body" class="loading">
|
||||||
<pre id="responsive-debug"></pre>
|
<pre id="responsive-debug"></pre>
|
||||||
<div id="layout" class="default-layout<?php if ($showFullscreen): ?> fullscreen-layout<?php endif ?>">
|
<div id="layout" class="default-layout<?php if ($showFullscreen): ?> fullscreen-layout<?php endif ?>">
|
||||||
<?= $this->render($innerLayoutScript); ?>
|
<?= $this->render($innerLayoutScript); ?>
|
||||||
|
|
|
@ -15,7 +15,7 @@ if ($moduleName !== 'default') {
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
<?= StyleSheet::compileForPdf() ?>
|
<?= StyleSheet::forPdf() ?>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
<div id="login">
|
<div id="login" class="centered-ghost">
|
||||||
<div class="logo">
|
<div class="centered-content" data-base-target="layout">
|
||||||
<div class="image">
|
<div id="icinga-logo" aria-hidden="true"></div>
|
||||||
<img aria-hidden="true" class="fade-in one" src="<?= $this->baseUrl('img/logo_icinga_big.png'); ?>" alt="<?= $this->translate('The Icinga logo'); ?>" >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form" data-base-target="layout">
|
|
||||||
<?php if ($requiresSetup): ?>
|
<?php if ($requiresSetup): ?>
|
||||||
<p class="config-note"><?= sprintf(
|
<p class="config-note"><?= sprintf(
|
||||||
$this->translate(
|
$this->translate(
|
||||||
|
@ -12,15 +8,15 @@
|
||||||
. 'authentication method. Please define a authentication method by following the instructions in the'
|
. 'authentication method. Please define a authentication method by following the instructions in the'
|
||||||
. ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
|
. ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
|
||||||
),
|
),
|
||||||
'<a href="http://docs.icinga.org/" title="' . $this->translate('Icinga Web 2 Documentation') . '">', // TODO: More exact documentation link..
|
'<a href="http://docs.icinga.org/" title="' . $this->translate('Icinga Web 2 Documentation') . '">', // TODO: More exact documentation link
|
||||||
'<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
|
'<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
|
||||||
'</a>'
|
'</a>'
|
||||||
); ?></p>
|
) ?></p>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?= $this->form ?>
|
<?= $this->form ?>
|
||||||
<div class="footer">
|
<div id="login-footer">
|
||||||
Icinga Web 2 © 2013-<?= date('Y'); ?><br><br>
|
<p>Icinga Web 2 © 2013-<?= date('Y') ?></p>
|
||||||
<?= $this->qlink($this->translate('The Icinga Project'), 'https://www.icinga.org'); ?>
|
<?= $this->qlink($this->translate('The Icinga Project'), 'https://www.icinga.org') ?>
|
||||||
<?= $this->qlink(
|
<?= $this->qlink(
|
||||||
null,
|
null,
|
||||||
'http://www.twitter.com/icinga',
|
'http://www.twitter.com/icinga',
|
||||||
|
@ -30,7 +26,7 @@
|
||||||
'icon' => 'twitter',
|
'icon' => 'twitter',
|
||||||
'title' => $this->translate('Icinga on Twitter')
|
'title' => $this->translate('Icinga on Twitter')
|
||||||
)
|
)
|
||||||
); ?>
|
) ?>
|
||||||
<?= $this->qlink(
|
<?= $this->qlink(
|
||||||
null,
|
null,
|
||||||
'http://www.facebook.com/icinga',
|
'http://www.facebook.com/icinga',
|
||||||
|
@ -49,7 +45,7 @@
|
||||||
'icon' => 'gplus-squared',
|
'icon' => 'gplus-squared',
|
||||||
'title' => $this->translate('Icinga on Google+')
|
'title' => $this->translate('Icinga on Google+')
|
||||||
)
|
)
|
||||||
); ?>
|
) ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php if (! $this->compact && ! $hideControls): ?>
|
<?php if (! $this->compact && ! $hideControls): ?>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<?= $tabs->showOnlyCloseButton(); ?>
|
<?= $tabs->showOnlyCloseButton() ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p><strong><?= nl2br($this->escape($message)); ?></strong></p>
|
<p class="error-message"><?= nl2br($this->escape($message)) ?></p>
|
||||||
<?php if (isset($stackTrace)): ?>
|
<?php if (isset($stackTrace)): ?>
|
||||||
<hr />
|
<hr />
|
||||||
<pre><?= $this->escape($stackTrace) ?></pre>
|
<pre><?= $this->escape($stackTrace) ?></pre>
|
||||||
|
|
|
@ -4,7 +4,7 @@ use Icinga\Data\Extensible;
|
||||||
use Icinga\Data\Reducible;
|
use Icinga\Data\Reducible;
|
||||||
|
|
||||||
if (! $this->compact): ?>
|
if (! $this->compact): ?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?= $tabs; ?>
|
<?= $tabs; ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<?= $this->sortBox ?>
|
<?= $this->sortBox ?>
|
||||||
|
|
|
@ -23,7 +23,7 @@ if ($this->hasPermission('config/authentication/groups/edit') && $backend instan
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?php if (! $this->compact): ?>
|
<?php if (! $this->compact): ?>
|
||||||
<?= $tabs; ?>
|
<?= $tabs; ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
|
@ -4,7 +4,7 @@ use Icinga\Data\Extensible;
|
||||||
use Icinga\Data\Reducible;
|
use Icinga\Data\Reducible;
|
||||||
|
|
||||||
if (! $this->compact): ?>
|
if (! $this->compact): ?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?= $tabs ?>
|
<?= $tabs ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<?= $this->sortBox ?>
|
<?= $this->sortBox ?>
|
||||||
|
|
|
@ -22,7 +22,7 @@ if ($this->hasPermission('config/authentication/users/edit') && $backend instanc
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?php if (! $this->compact): ?>
|
<?php if (! $this->compact): ?>
|
||||||
<?= $tabs; ?>
|
<?= $tabs; ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
|
@ -20,7 +20,7 @@ use Icinga\Web\Response;
|
||||||
class EmbeddedWeb extends ApplicationBootstrap
|
class EmbeddedWeb extends ApplicationBootstrap
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Request object
|
* Request
|
||||||
*
|
*
|
||||||
* @var Request
|
* @var Request
|
||||||
*/
|
*/
|
||||||
|
@ -57,6 +57,7 @@ class EmbeddedWeb extends ApplicationBootstrap
|
||||||
* Embedded bootstrap parts
|
* Embedded bootstrap parts
|
||||||
*
|
*
|
||||||
* @see ApplicationBootstrap::bootstrap
|
* @see ApplicationBootstrap::bootstrap
|
||||||
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
protected function bootstrap()
|
protected function bootstrap()
|
||||||
|
@ -65,6 +66,8 @@ class EmbeddedWeb extends ApplicationBootstrap
|
||||||
->setupZendAutoloader()
|
->setupZendAutoloader()
|
||||||
->setupErrorHandling()
|
->setupErrorHandling()
|
||||||
->loadConfig()
|
->loadConfig()
|
||||||
|
->setupLogging()
|
||||||
|
->setupLogger()
|
||||||
->setupRequest()
|
->setupRequest()
|
||||||
->setupResponse()
|
->setupResponse()
|
||||||
->setupTimezone()
|
->setupTimezone()
|
||||||
|
|
|
@ -13,6 +13,7 @@ use Zend_Paginator;
|
||||||
use Zend_View_Helper_PaginationControl;
|
use Zend_View_Helper_PaginationControl;
|
||||||
use Icinga\Authentication\Auth;
|
use Icinga\Authentication\Auth;
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
|
use Icinga\Util\DirectoryIterator;
|
||||||
use Icinga\Util\TimezoneDetect;
|
use Icinga\Util\TimezoneDetect;
|
||||||
use Icinga\Util\Translator;
|
use Icinga\Util\Translator;
|
||||||
use Icinga\Web\Controller\Dispatcher;
|
use Icinga\Web\Controller\Dispatcher;
|
||||||
|
@ -20,6 +21,7 @@ use Icinga\Web\Navigation\Navigation;
|
||||||
use Icinga\Web\Notification;
|
use Icinga\Web\Notification;
|
||||||
use Icinga\Web\Session;
|
use Icinga\Web\Session;
|
||||||
use Icinga\Web\Session\Session as BaseSession;
|
use Icinga\Web\Session\Session as BaseSession;
|
||||||
|
use Icinga\Web\StyleSheet;
|
||||||
use Icinga\Web\View;
|
use Icinga\Web\View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,6 +100,33 @@ class Web extends EmbeddedWeb
|
||||||
->setupPagination();
|
->setupPagination();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get themes provided by Web 2 and all enabled modules
|
||||||
|
*
|
||||||
|
* @return string[] Array of theme names as keys and values
|
||||||
|
*/
|
||||||
|
public function getThemes()
|
||||||
|
{
|
||||||
|
$themes = array(StyleSheet::DEFAULT_THEME);
|
||||||
|
$applicationThemePath = $this->getBaseDir('public/css/themes');
|
||||||
|
if (DirectoryIterator::isReadable($applicationThemePath)) {
|
||||||
|
foreach (new DirectoryIterator($applicationThemePath, 'less') as $name => $theme) {
|
||||||
|
$themes[] = substr($name, 0, -5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$mm = $this->getModuleManager();
|
||||||
|
foreach ($mm->listEnabledModules() as $moduleName) {
|
||||||
|
$moduleThemePath = $mm->getModule($moduleName)->getCssDir() . '/themes';
|
||||||
|
if (! DirectoryIterator::isReadable($moduleThemePath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach (new DirectoryIterator($moduleThemePath, 'less') as $name => $theme) {
|
||||||
|
$themes[] = $moduleName . '/' . substr($name, 0, -5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array_combine($themes, $themes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare routing
|
* Prepare routing
|
||||||
*
|
*
|
||||||
|
|
|
@ -61,7 +61,7 @@ if (in_array($path, $special)) {
|
||||||
Stylesheet::send();
|
Stylesheet::send();
|
||||||
exit;
|
exit;
|
||||||
case 'css/icinga.min.css':
|
case 'css/icinga.min.css':
|
||||||
Stylesheet::sendMinified();
|
Stylesheet::send(true);
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
case 'js/icinga.dev.js':
|
case 'js/icinga.dev.js':
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\File;
|
|
||||||
|
|
||||||
use FilterIterator;
|
|
||||||
use Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator over files having a specific file extension
|
|
||||||
*
|
|
||||||
* Usage example:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
*
|
|
||||||
* namespace Icinga\Example;
|
|
||||||
*
|
|
||||||
* use RecursiveDirectoryIterator;
|
|
||||||
* use RecursiveIteratorIterator;
|
|
||||||
* use Icinga\File\FileExtensionFilterIterator;
|
|
||||||
*
|
|
||||||
* $markdownFiles = new FileExtensionFilterIterator(
|
|
||||||
* new RecursiveIteratorIterator(
|
|
||||||
* new RecursiveDirectoryIterator(__DIR__),
|
|
||||||
* RecursiveIteratorIterator::SELF_FIRST
|
|
||||||
* ),
|
|
||||||
* 'md'
|
|
||||||
* );
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
class FileExtensionFilterIterator extends FilterIterator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The extension to filter for
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $extension;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new FileExtensionFilterIterator
|
|
||||||
*
|
|
||||||
* @param Iterator $iterator Apply filter to this iterator
|
|
||||||
* @param string $extension The file extension to filter for. The file extension may not contain the leading dot
|
|
||||||
*/
|
|
||||||
public function __construct(Iterator $iterator, $extension)
|
|
||||||
{
|
|
||||||
$this->extension = '.' . ltrim(strtolower((string) $extension), '.');
|
|
||||||
parent::__construct($iterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accept files which match the file extension to filter for
|
|
||||||
*
|
|
||||||
* @return bool Whether the current element of the iterator is acceptable
|
|
||||||
* through this filter
|
|
||||||
*/
|
|
||||||
public function accept()
|
|
||||||
{
|
|
||||||
$current = $this->current();
|
|
||||||
/** @var $current \SplFileInfo */
|
|
||||||
if (! $current->isFile()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// SplFileInfo::getExtension() is only available since PHP 5 >= 5.3.6
|
|
||||||
$filename = $current->getFilename();
|
|
||||||
$sfx = substr($filename, -strlen($this->extension));
|
|
||||||
return $sfx === false ? false : strtolower($sfx) === $this->extension;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\File;
|
|
||||||
|
|
||||||
use FilterIterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator over non-empty files
|
|
||||||
*
|
|
||||||
* Usage example:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
*
|
|
||||||
* namespace Icinga\Example;
|
|
||||||
*
|
|
||||||
* use RecursiveDirectoryIterator;
|
|
||||||
* use RecursiveIteratorIterator;
|
|
||||||
* use Icinga\File\NonEmptyFilterIterator;
|
|
||||||
*
|
|
||||||
* $nonEmptyFiles = new NonEmptyFileIterator(
|
|
||||||
* new RecursiveIteratorIterator(
|
|
||||||
* new RecursiveDirectoryIterator(__DIR__),
|
|
||||||
* RecursiveIteratorIterator::SELF_FIRST
|
|
||||||
* )
|
|
||||||
* );
|
|
||||||
* </code>
|
|
||||||
*/
|
|
||||||
class NonEmptyFileIterator extends FilterIterator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Accept non-empty files
|
|
||||||
*
|
|
||||||
* @return bool Whether the current element of the iterator is acceptable
|
|
||||||
* through this filter
|
|
||||||
*/
|
|
||||||
public function accept()
|
|
||||||
{
|
|
||||||
$current = $this->current();
|
|
||||||
/** @var $current \SplFileInfo */
|
|
||||||
if (! $current->isFile()
|
|
||||||
|| $current->getSize() === 0
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Util;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator for traversing a directory
|
||||||
|
*/
|
||||||
|
class DirectoryIterator implements Iterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Current directory item
|
||||||
|
*
|
||||||
|
* @var string|false
|
||||||
|
*/
|
||||||
|
private $current;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file extension to filter for
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory handle
|
||||||
|
*
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current key
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path of the directory to traverse
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to skip empty files
|
||||||
|
*
|
||||||
|
* Defaults to true.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $skipEmpty = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to skip hidden files
|
||||||
|
*
|
||||||
|
* Defaults to true.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $skipHidden = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new directory iterator from path
|
||||||
|
*
|
||||||
|
* The given path will not be validated whether it is readable. Use {@link isReadable()} before creating a new
|
||||||
|
* directory iterator instance.
|
||||||
|
*
|
||||||
|
* @param string $path The path of the directory to traverse
|
||||||
|
* @param string $extension The file extension to filter for. A leading dot is optional
|
||||||
|
*/
|
||||||
|
public function __construct($path, $extension = null)
|
||||||
|
{
|
||||||
|
if (empty($path)) {
|
||||||
|
throw new InvalidArgumentException('The path can\'t be empty');
|
||||||
|
}
|
||||||
|
$this->path = $path;
|
||||||
|
if (! empty($extension)) {
|
||||||
|
$this->extension = '.' . ltrim($extension, '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given path is a directory and is readable
|
||||||
|
*
|
||||||
|
* @param string $path The path of the directory
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isReadable($path)
|
||||||
|
{
|
||||||
|
return is_dir($path) && is_readable($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return $this->current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function next()
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
$file = readdir($this->handle);
|
||||||
|
if ($file === false) {
|
||||||
|
$key = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$skip = false;
|
||||||
|
do {
|
||||||
|
if ($this->skipHidden && $file[0] === '.') {
|
||||||
|
$skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $this->path . '/' . $file;
|
||||||
|
|
||||||
|
if (is_dir($path)) {
|
||||||
|
$skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->skipEmpty && ! filesize($path)) {
|
||||||
|
$skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->extension && ! String::endsWith($file, $this->extension)) {
|
||||||
|
$skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $file;
|
||||||
|
$file = $path;
|
||||||
|
} while (0);
|
||||||
|
}
|
||||||
|
} while ($skip);
|
||||||
|
|
||||||
|
$this->current = $file;
|
||||||
|
/** @noinspection PhpUndefinedVariableInspection */
|
||||||
|
$this->key = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return $this->current !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
if ($this->handle === null) {
|
||||||
|
$this->handle = opendir($this->path);
|
||||||
|
} else {
|
||||||
|
rewinddir($this->handle);
|
||||||
|
}
|
||||||
|
$this->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close directory handle if created
|
||||||
|
*/
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if ($this->handle !== null) {
|
||||||
|
closedir($this->handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -101,17 +101,17 @@ class String
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a string ends with a different string
|
* Test whether the given string ends with the given suffix
|
||||||
*
|
*
|
||||||
* @param $haystack string The string to search for matches
|
* @param string $string The string to test
|
||||||
* @param $needle string The string to match at the start of the haystack
|
* @param string $suffix The suffix the string must end with
|
||||||
*
|
*
|
||||||
* @return bool Whether or not needle is at the beginning of haystack
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function endsWith($haystack, $needle)
|
public static function endsWith($string, $suffix)
|
||||||
{
|
{
|
||||||
return $needle === '' ||
|
$stringSuffix = substr($string, -strlen($suffix));
|
||||||
(($temp = strlen($haystack) - strlen($needle)) >= 0 && false !== strpos($haystack, $needle, $temp));
|
return $stringSuffix !== false ? $stringSuffix === $suffix : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,77 +3,260 @@
|
||||||
|
|
||||||
namespace Icinga\Web;
|
namespace Icinga\Web;
|
||||||
|
|
||||||
|
use Icinga\Application\Config;
|
||||||
|
use Icinga\Application\Icinga;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper Class Cookie
|
* A HTTP cookie
|
||||||
*/
|
*/
|
||||||
class Cookie
|
class Cookie
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name of the control cookie
|
* Domain of the cookie
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
const CHECK_COOKIE = '_chc';
|
protected $domain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request
|
* The timestamp at which the cookie expires
|
||||||
*
|
*
|
||||||
* @var Request
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $request;
|
protected $expire;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to protect the cookie against client side script code attempts to read the cookie
|
||||||
|
*
|
||||||
|
* Defaults to true.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $httpOnly = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the cookie
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path on the web server where the cookie is available
|
||||||
|
*
|
||||||
|
* Defaults to the base URL.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to send the cookie only over a secure connection
|
||||||
|
*
|
||||||
|
* Defaults to auto-detection so that if the current request was sent over a secure connection the secure flag will
|
||||||
|
* be set to true.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $secure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the cookie
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new cookie
|
* Create a new cookie
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
*/
|
*/
|
||||||
public function __construct(Request $request)
|
public function __construct($name, $value = null)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
|
||||||
|
throw new InvalidArgumentException(sprintf(
|
||||||
|
'Cookie name can\'t contain these characters: =,; \t\r\n\013\014 (%s)',
|
||||||
|
$name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (empty($name)) {
|
||||||
|
throw new InvalidArgumentException('The cookie name can\'t be empty');
|
||||||
|
}
|
||||||
|
$this->name = $name;
|
||||||
|
$this->value = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether cookies are supported or not
|
* Get the domain of the cookie
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDomain()
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the domain of the cookie
|
||||||
|
*
|
||||||
|
* @param string $domain
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setDomain($domain)
|
||||||
|
{
|
||||||
|
$this->domain = $domain;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp at which the cookie expires
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getExpire()
|
||||||
|
{
|
||||||
|
return $this->expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the timestamp at which the cookie expires
|
||||||
|
*
|
||||||
|
* @param int $expire
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setExpire($expire)
|
||||||
|
{
|
||||||
|
$this->expire = $expire;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether to protect the cookie against client side script code attempts to read the cookie
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isSupported()
|
public function isHttpOnly()
|
||||||
{
|
{
|
||||||
if (! empty($_COOKIE)) {
|
return $this->httpOnly;
|
||||||
$this->cleanupCheck();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = $this->request->getUrl();
|
|
||||||
|
|
||||||
if ($url->hasParam('_checkCookie') && empty($_COOKIE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $url->hasParam('_checkCookie')) {
|
|
||||||
$this->provideCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare check to detect cookie support
|
* Set whether to protect the cookie against client side script code attempts to read the cookie
|
||||||
|
*
|
||||||
|
* @param bool $httpOnly
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function provideCheck()
|
public function setHttpOnly($httpOnly)
|
||||||
{
|
{
|
||||||
setcookie(self::CHECK_COOKIE, '1');
|
$this->httpOnly = $httpOnly;
|
||||||
|
return $this;
|
||||||
$requestUri = $this->request->getUrl()->addParams(array('_checkCookie' => 1));
|
|
||||||
$this->request->getResponse()->redirectAndExit($requestUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup the cookie support check
|
* Get the name of the cookie
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function cleanupCheck()
|
public function getName()
|
||||||
{
|
{
|
||||||
if ($this->request->getUrl()->hasParam('_checkCookie') && isset($_COOKIE[self::CHECK_COOKIE])) {
|
return $this->name;
|
||||||
$requestUri =$this->request->getUrl()->without('_checkCookie');
|
}
|
||||||
$this->request->getResponse()->redirectAndExit($requestUri);
|
|
||||||
}
|
/**
|
||||||
|
* Get the path on the web server where the cookie is available
|
||||||
|
*
|
||||||
|
* If the path has not been set either via {@link setPath()} or via config, the base URL will be returned.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPath()
|
||||||
|
{
|
||||||
|
if ($this->path === null) {
|
||||||
|
$path = Config::app()->get('cookie', 'path');
|
||||||
|
if ($path === null) {
|
||||||
|
// The following call could be used as default for ConfigObject::get(), but we prevent unnecessary
|
||||||
|
// function calls here, if the path is set in the config
|
||||||
|
$path = Icinga::app()->getRequest()->getBaseUrl();
|
||||||
|
}
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
return $this->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path on the web server where the cookie is available
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setPath($path)
|
||||||
|
{
|
||||||
|
$this->path = $path;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether to send the cookie only over a secure connection
|
||||||
|
*
|
||||||
|
* If the secure flag has not been set either via {@link setSecure()} or via config and if the current request was
|
||||||
|
* sent over a secure connection, true will be returned.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSecure()
|
||||||
|
{
|
||||||
|
if ($this->secure === null) {
|
||||||
|
$secure = Config::app()->get('cookie', 'secure');
|
||||||
|
if ($secure === null) {
|
||||||
|
// The following call could be used as default for ConfigObject::get(), but we prevent unnecessary
|
||||||
|
// function calls here, if the secure flag is set in the config
|
||||||
|
$secure = Icinga::app()->getRequest()->isSecure();
|
||||||
|
}
|
||||||
|
return $secure;
|
||||||
|
}
|
||||||
|
return $this->secure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to send the cookie only over a secure connection
|
||||||
|
*
|
||||||
|
* @param bool $secure
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSecure($secure)
|
||||||
|
{
|
||||||
|
$this->secure = $secure;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the cookie
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getValue()
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of the cookie
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setValue($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use IteratorAggregate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain a set of cookies
|
||||||
|
*/
|
||||||
|
class CookieSet implements IteratorAggregate
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Cookies in this set indexed by the cookie names
|
||||||
|
*
|
||||||
|
* @var Cookie[]
|
||||||
|
*/
|
||||||
|
protected $cookies = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an iterator for traversing the cookies in this set
|
||||||
|
*
|
||||||
|
* @return ArrayIterator An iterator for traversing the cookies in this set
|
||||||
|
*/
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
return new ArrayIterator($this->cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a cookie to the set
|
||||||
|
*
|
||||||
|
* If a cookie with the same name already exists, the cookie will be overridden.
|
||||||
|
*
|
||||||
|
* @param Cookie $cookie The cookie to add
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function add(Cookie $cookie)
|
||||||
|
{
|
||||||
|
$this->cookies[$cookie->getName()] = $cookie;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cookie with the given name from the set
|
||||||
|
*
|
||||||
|
* @param string $name The name of the cookie
|
||||||
|
*
|
||||||
|
* @return Cookie|null The cookie with the given name or null if the cookie does not exist
|
||||||
|
*/
|
||||||
|
public function get($name)
|
||||||
|
{
|
||||||
|
return isset($this->cookies[$name]) ? $this->cookies[$name] : null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -190,9 +190,6 @@ class FileCache
|
||||||
/**
|
/**
|
||||||
* Whether the given ETag matchesspecific file(s) on disk
|
* Whether the given ETag matchesspecific file(s) on disk
|
||||||
*
|
*
|
||||||
* If no ETag is given we'll try to fetch the one from the current
|
|
||||||
* HTTP request. Respects HTTP Cache-Control: no-cache, if set.
|
|
||||||
*
|
|
||||||
* @param string|array $files file(s) to check
|
* @param string|array $files file(s) to check
|
||||||
* @param string $match ETag to match against
|
* @param string $match ETag to match against
|
||||||
*
|
*
|
||||||
|
@ -208,9 +205,6 @@ class FileCache
|
||||||
if (! $match) {
|
if (! $match) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] === 'no-cache') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$etag = self::etagForFiles($files);
|
$etag = self::etagForFiles($files);
|
||||||
return $match === $etag ? $etag : false;
|
return $match === $etag ? $etag : false;
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Web\Helper;
|
||||||
|
|
||||||
|
use Icinga\Web\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper Class Cookie
|
||||||
|
*/
|
||||||
|
class CookieHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the control cookie
|
||||||
|
*/
|
||||||
|
const CHECK_COOKIE = '_chc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The request
|
||||||
|
*
|
||||||
|
* @var Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new cookie
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*/
|
||||||
|
public function __construct(Request $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether cookies are supported or not
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSupported()
|
||||||
|
{
|
||||||
|
if (! empty($_COOKIE)) {
|
||||||
|
$this->cleanupCheck();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->request->getUrl();
|
||||||
|
|
||||||
|
if ($url->hasParam('_checkCookie') && empty($_COOKIE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $url->hasParam('_checkCookie')) {
|
||||||
|
$this->provideCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare check to detect cookie support
|
||||||
|
*/
|
||||||
|
public function provideCheck()
|
||||||
|
{
|
||||||
|
setcookie(self::CHECK_COOKIE, '1');
|
||||||
|
|
||||||
|
$requestUri = $this->request->getUrl()->addParams(array('_checkCookie' => 1));
|
||||||
|
$this->request->getResponse()->redirectAndExit($requestUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup the cookie support check
|
||||||
|
*/
|
||||||
|
public function cleanupCheck()
|
||||||
|
{
|
||||||
|
if ($this->request->getUrl()->hasParam('_checkCookie') && isset($_COOKIE[self::CHECK_COOKIE])) {
|
||||||
|
$requestUri =$this->request->getUrl()->without('_checkCookie');
|
||||||
|
$this->request->getResponse()->redirectAndExit($requestUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,15 +47,22 @@ class JavaScript
|
||||||
|
|
||||||
public static function sendMinified()
|
public static function sendMinified()
|
||||||
{
|
{
|
||||||
return self::send(true);
|
self::send(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function sendForIe8()
|
public static function sendForIe8()
|
||||||
{
|
{
|
||||||
self::$vendorFiles = self::$ie8VendorFiles;
|
self::$vendorFiles = self::$ie8VendorFiles;
|
||||||
return self::send();
|
self::send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the client side script code to the client
|
||||||
|
*
|
||||||
|
* Does not cache the client side script code if the HTTP header Cache-Control or Pragma is set to no-cache.
|
||||||
|
*
|
||||||
|
* @param bool $minified Whether to compress the client side script code
|
||||||
|
*/
|
||||||
public static function send($minified = false)
|
public static function send($minified = false)
|
||||||
{
|
{
|
||||||
header('Content-Type: application/javascript');
|
header('Content-Type: application/javascript');
|
||||||
|
@ -87,7 +94,10 @@ class JavaScript
|
||||||
}
|
}
|
||||||
$files = array_merge($vendorFiles, $jsFiles);
|
$files = array_merge($vendorFiles, $jsFiles);
|
||||||
|
|
||||||
if ($etag = FileCache::etagMatchesFiles($files)) {
|
$request = Icinga::app()->getRequest();
|
||||||
|
$noCache = $request->getHeader('Cache-Control') === 'no-cache' || $request->getHeader('Pragma') === 'no-cache';
|
||||||
|
|
||||||
|
if (! $noCache && FileCache::etagMatchesFiles($files)) {
|
||||||
header("HTTP/1.1 304 Not Modified");
|
header("HTTP/1.1 304 Not Modified");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -99,7 +109,7 @@ class JavaScript
|
||||||
|
|
||||||
$cacheFile = 'icinga-' . $etag . $min . '.js';
|
$cacheFile = 'icinga-' . $etag . $min . '.js';
|
||||||
$cache = FileCache::instance();
|
$cache = FileCache::instance();
|
||||||
if ($cache->has($cacheFile)) {
|
if (! $noCache && $cache->has($cacheFile)) {
|
||||||
$cache->send($cacheFile);
|
$cache->send($cacheFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,167 +3,170 @@
|
||||||
|
|
||||||
namespace Icinga\Web;
|
namespace Icinga\Web;
|
||||||
|
|
||||||
use Exception;
|
use Icinga\Application\Logger;
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveArrayIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
use RegexIterator;
|
|
||||||
use RecursiveRegexIterator;
|
|
||||||
use Icinga\Application\Icinga;
|
|
||||||
use lessc;
|
use lessc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Less compiler prints files or directories to stdout
|
* Compile LESS into CSS
|
||||||
|
*
|
||||||
|
* Comments will be removed always. lessc is messing them up.
|
||||||
*/
|
*/
|
||||||
class LessCompiler
|
class LessCompiler
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Collection of items: File or directories
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $items = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lessphp compiler
|
* lessphp compiler
|
||||||
*
|
*
|
||||||
* @var \lessc
|
* @var lessc
|
||||||
*/
|
*/
|
||||||
private $lessc;
|
protected $lessc;
|
||||||
|
|
||||||
private $source;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance
|
* Array of LESS files
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $lessFiles = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of module LESS files indexed by module names
|
||||||
|
*
|
||||||
|
* @var array[]
|
||||||
|
*/
|
||||||
|
protected $moduleLessFiles = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LESS source
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of the LESS theme
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new LESS compiler
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
require_once 'lessphp/lessc.inc.php';
|
require_once 'lessphp/lessc.inc.php';
|
||||||
$this->lessc = new lessc();
|
$this->lessc = new lessc();
|
||||||
|
// Discourage usage of import because we're caching based on an explicit list of LESS files to compile
|
||||||
|
$this->lessc->importDisabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the extendend import functionality
|
* Add a Web 2 LESS file
|
||||||
|
*
|
||||||
|
* @param string $lessFile Path to the LESS file
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function disableExtendedImport()
|
public function addLessFile($lessFile)
|
||||||
{
|
{
|
||||||
$this->lessc->importDisabled = true;
|
$this->lessFiles[] = $lessFile;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a module LESS file
|
||||||
|
*
|
||||||
|
* @param string $moduleName Name of the module
|
||||||
|
* @param string $lessFile Path to the LESS file
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addModuleLessFile($moduleName, $lessFile)
|
||||||
|
{
|
||||||
|
if (! isset($this->moduleLessFiles[$moduleName])) {
|
||||||
|
$this->moduleLessFiles[$moduleName] = array();
|
||||||
|
}
|
||||||
|
$this->moduleLessFiles[$moduleName][] = $lessFile;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of LESS files added to the compiler
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getLessFiles()
|
||||||
|
{
|
||||||
|
$lessFiles = iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator(
|
||||||
|
$this->lessFiles + $this->moduleLessFiles
|
||||||
|
)));
|
||||||
|
if ($this->theme !== null) {
|
||||||
|
$lessFiles[] = $this->theme;
|
||||||
|
}
|
||||||
|
return $lessFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path to the LESS theme
|
||||||
|
*
|
||||||
|
* @param string $theme Path to the LESS theme
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTheme($theme)
|
||||||
|
{
|
||||||
|
if (is_file($theme) && is_readable($theme)) {
|
||||||
|
$this->theme = $theme;
|
||||||
|
} else {
|
||||||
|
Logger::error('Can\t load theme %s. Make sure that the theme exists and is readable', $theme);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instruct the compiler to minify CSS
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
public function compress()
|
public function compress()
|
||||||
{
|
{
|
||||||
$this->lessc->setPreserveComments(false);
|
|
||||||
$this->lessc->setFormatter('compressed');
|
$this->lessc->setFormatter('compressed');
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add usable style item to stack
|
* Render to CSS
|
||||||
*
|
*
|
||||||
* @param string $item File or directory
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function addItem($item)
|
public function render()
|
||||||
{
|
{
|
||||||
$this->items[] = $item;
|
foreach ($this->lessFiles as $lessFile) {
|
||||||
|
$this->source .= file_get_contents($lessFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addLoadedModules()
|
$moduleCss = '';
|
||||||
{
|
foreach ($this->moduleLessFiles as $moduleName => $moduleLessFiles) {
|
||||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) {
|
$moduleCss .= '.icinga-module.module-' . $moduleName . ' {';
|
||||||
$this->addModule($name, $module);
|
foreach ($moduleLessFiles as $moduleLessFile) {
|
||||||
|
$moduleCss .= file_get_contents($moduleLessFile);
|
||||||
}
|
}
|
||||||
return $this;
|
$moduleCss .= '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addFile($filename)
|
$moduleCss = preg_replace(
|
||||||
{
|
'/(\.icinga-module\.module-[^\s]+) (#layout\.[^\s]+)/m',
|
||||||
$this->source .= "\n/* CSS: $filename */\n"
|
'\2 \1',
|
||||||
. file_get_contents($filename)
|
$moduleCss
|
||||||
. "\n\n";
|
);
|
||||||
return $this;
|
|
||||||
|
$this->source .= $moduleCss;
|
||||||
|
|
||||||
|
if ($this->theme !== null) {
|
||||||
|
$this->source .= file_get_contents($this->theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function compile()
|
|
||||||
{
|
|
||||||
return $this->lessc->compile($this->source);
|
return $this->lessc->compile($this->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addModule($name, $module)
|
|
||||||
{
|
|
||||||
if ($module->hasCss()) {
|
|
||||||
$contents = array();
|
|
||||||
foreach ($module->getCssFiles() as $path) {
|
|
||||||
if (file_exists($path)) {
|
|
||||||
$contents[] = "/* CSS: modules/$name/$path */\n" . file_get_contents($path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->source .= ''
|
|
||||||
. '.icinga-module.module-'
|
|
||||||
. $name
|
|
||||||
. " {\n"
|
|
||||||
. join("\n\n", $contents)
|
|
||||||
. "}\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile and print a single file
|
|
||||||
*
|
|
||||||
* @param string $file
|
|
||||||
*/
|
|
||||||
public function printFile($file)
|
|
||||||
{
|
|
||||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
|
||||||
echo PHP_EOL. '/* CSS: ' . $file . ' */' . PHP_EOL;
|
|
||||||
|
|
||||||
if ($ext === 'css') {
|
|
||||||
readfile($file);
|
|
||||||
} elseif ($ext === 'less') {
|
|
||||||
try {
|
|
||||||
echo $this->lessc->compileFile($file);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo '/* ' . PHP_EOL . ' ===' . PHP_EOL;
|
|
||||||
echo ' Error in file ' . $file . PHP_EOL;
|
|
||||||
echo ' ' . $e->getMessage() . PHP_EOL . PHP_EOL;
|
|
||||||
echo ' ' . 'This file was dropped cause of errors.' . PHP_EOL;
|
|
||||||
echo ' ===' . PHP_EOL . '*/' . PHP_EOL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile and print a path content (recursive)
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
*/
|
|
||||||
public function printPathRecursive($path)
|
|
||||||
{
|
|
||||||
$directoryInterator = new RecursiveDirectoryIterator($path);
|
|
||||||
$iterator = new RecursiveIteratorIterator($directoryInterator);
|
|
||||||
$filteredIterator = new RegexIterator($iterator, '/\.(css|less)$/', RecursiveRegexIterator::GET_MATCH);
|
|
||||||
foreach ($filteredIterator as $file => $extension) {
|
|
||||||
$this->printFile($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile and print the whole item stack
|
|
||||||
*/
|
|
||||||
public function printStack()
|
|
||||||
{
|
|
||||||
foreach ($this->items as $item) {
|
|
||||||
if (is_dir($item)) {
|
|
||||||
$this->printPathRecursive($item);
|
|
||||||
} elseif (is_file($item)) {
|
|
||||||
$this->printFile($item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,15 +118,4 @@ class Request extends Zend_Controller_Request_Http
|
||||||
}
|
}
|
||||||
return $id . '-' . $this->uniqueId;
|
return $id . '-' . $this->uniqueId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Detect whether cookies are enabled
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasCookieSupport()
|
|
||||||
{
|
|
||||||
$cookie = new Cookie($this);
|
|
||||||
return $cookie->isSupported();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,18 @@ use Zend_Controller_Response_Http;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Web\Response\JsonResponse;
|
use Icinga\Web\Response\JsonResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HTTP response
|
||||||
|
*/
|
||||||
class Response extends Zend_Controller_Response_Http
|
class Response extends Zend_Controller_Response_Http
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Set of cookies which are to be sent to the client
|
||||||
|
*
|
||||||
|
* @var CookieSet
|
||||||
|
*/
|
||||||
|
protected $cookies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect URL
|
* Redirect URL
|
||||||
*
|
*
|
||||||
|
@ -23,6 +33,13 @@ class Response extends Zend_Controller_Response_Http
|
||||||
*/
|
*/
|
||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to instruct client side script code to reload CSS
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $reloadCss;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to send the rerender layout header on XHR
|
* Whether to send the rerender layout header on XHR
|
||||||
*
|
*
|
||||||
|
@ -30,6 +47,44 @@ class Response extends Zend_Controller_Response_Http
|
||||||
*/
|
*/
|
||||||
protected $rerenderLayout = false;
|
protected $rerenderLayout = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the set of cookies which are to be sent to the client
|
||||||
|
*
|
||||||
|
* @return CookieSet
|
||||||
|
*/
|
||||||
|
public function getCookies()
|
||||||
|
{
|
||||||
|
if ($this->cookies === null) {
|
||||||
|
$this->cookies = new CookieSet();
|
||||||
|
}
|
||||||
|
return $this->cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cookie with the given name from the set of cookies which are to be sent to the client
|
||||||
|
*
|
||||||
|
* @param string $name The name of the cookie
|
||||||
|
*
|
||||||
|
* @return Cookie|null The cookie with the given name or null if the cookie does not exist
|
||||||
|
*/
|
||||||
|
public function getCookie($name)
|
||||||
|
{
|
||||||
|
return $this->getCookies()->get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the given cookie for sending it to the client
|
||||||
|
*
|
||||||
|
* @param Cookie $cookie The cookie to send to the client
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setCookie(Cookie $cookie)
|
||||||
|
{
|
||||||
|
$this->getCookies()->add($cookie);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the redirect URL
|
* Get the redirect URL
|
||||||
*
|
*
|
||||||
|
@ -74,6 +129,29 @@ class Response extends Zend_Controller_Response_Http
|
||||||
return $this->request;
|
return $this->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether to instruct client side script code to reload CSS
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isReloadCss()
|
||||||
|
{
|
||||||
|
return $this->reloadCss;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to instruct client side script code to reload CSS
|
||||||
|
*
|
||||||
|
* @param bool $reloadCss
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setReloadCss($reloadCss)
|
||||||
|
{
|
||||||
|
$this->reloadCss = $reloadCss;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether to send the rerender layout header on XHR
|
* Get whether to send the rerender layout header on XHR
|
||||||
*
|
*
|
||||||
|
@ -123,6 +201,9 @@ class Response extends Zend_Controller_Response_Http
|
||||||
if ($this->getRerenderLayout()) {
|
if ($this->getRerenderLayout()) {
|
||||||
$this->setHeader('X-Icinga-Container', 'layout', true);
|
$this->setHeader('X-Icinga-Container', 'layout', true);
|
||||||
}
|
}
|
||||||
|
if ($this->isReloadCss()) {
|
||||||
|
$this->setHeader('X-Icinga-Reload-Css', 'now', true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($redirectUrl !== null) {
|
if ($redirectUrl !== null) {
|
||||||
$this->setRedirect($redirectUrl->getAbsoluteUrl());
|
$this->setRedirect($redirectUrl->getAbsoluteUrl());
|
||||||
|
@ -148,12 +229,34 @@ class Response extends Zend_Controller_Response_Http
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the cookies to the client
|
||||||
|
*/
|
||||||
|
public function sendCookies()
|
||||||
|
{
|
||||||
|
foreach ($this->getCookies() as $cookie) {
|
||||||
|
/** @var Cookie $cookie */
|
||||||
|
setcookie(
|
||||||
|
$cookie->getName(),
|
||||||
|
$cookie->getValue(),
|
||||||
|
$cookie->getExpire(),
|
||||||
|
$cookie->getPath(),
|
||||||
|
$cookie->getDomain(),
|
||||||
|
$cookie->isSecure(),
|
||||||
|
$cookie->isHttpOnly()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function sendHeaders()
|
public function sendHeaders()
|
||||||
{
|
{
|
||||||
$this->prepare();
|
$this->prepare();
|
||||||
|
if (! $this->getRequest()->isApiRequest()) {
|
||||||
|
$this->sendCookies();
|
||||||
|
}
|
||||||
return parent::sendHeaders();
|
return parent::sendHeaders();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,28 @@
|
||||||
|
|
||||||
namespace Icinga\Web;
|
namespace Icinga\Web;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Web\FileCache;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Web\LessCompiler;
|
use Icinga\Exception\IcingaException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send CSS for Web 2 and all loaded modules to the client
|
||||||
|
*/
|
||||||
class StyleSheet
|
class StyleSheet
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The name of the default theme
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const DEFAULT_THEME = 'Icinga';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of core LESS files Web 2 sends to the client
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
protected static $lessFiles = array(
|
protected static $lessFiles = array(
|
||||||
'../application/fonts/fontello-ifont/css/ifont-embedded.css',
|
'../application/fonts/fontello-ifont/css/ifont-embedded.css',
|
||||||
'css/vendor/normalize.css',
|
'css/vendor/normalize.css',
|
||||||
|
@ -21,11 +37,9 @@ class StyleSheet
|
||||||
'css/icinga/nav.less',
|
'css/icinga/nav.less',
|
||||||
'css/icinga/main.less',
|
'css/icinga/main.less',
|
||||||
'css/icinga/animation.less',
|
'css/icinga/animation.less',
|
||||||
'css/icinga/layout-colors.less',
|
'css/icinga/layout.less',
|
||||||
'css/icinga/layout-structure.less',
|
'css/icinga/layout-structure.less',
|
||||||
'css/icinga/menu.less',
|
'css/icinga/menu.less',
|
||||||
'css/icinga/header-elements.less',
|
|
||||||
'css/icinga/footer-elements.less',
|
|
||||||
// 'css/icinga/main-content.less',
|
// 'css/icinga/main-content.less',
|
||||||
'css/icinga/tabs.less',
|
'css/icinga/tabs.less',
|
||||||
'css/icinga/forms.less',
|
'css/icinga/forms.less',
|
||||||
|
@ -37,92 +51,180 @@ class StyleSheet
|
||||||
'css/icinga/dev.less',
|
'css/icinga/dev.less',
|
||||||
// 'css/icinga/logo.less',
|
// 'css/icinga/logo.less',
|
||||||
'css/icinga/spinner.less',
|
'css/icinga/spinner.less',
|
||||||
'css/icinga/compat.less'
|
'css/icinga/compat.less',
|
||||||
|
'css/icinga/print.less'
|
||||||
);
|
);
|
||||||
|
|
||||||
public static function compileForPdf()
|
/**
|
||||||
{
|
* Application instance
|
||||||
self::checkPhp();
|
*
|
||||||
$less = new LessCompiler();
|
* @var \Icinga\Application\EmbeddedWeb
|
||||||
$basedir = Icinga::app()->getBootstrapDirectory();
|
*/
|
||||||
foreach (self::$lessFiles as $file) {
|
protected $app;
|
||||||
$less->addFile($basedir . '/' . $file);
|
|
||||||
}
|
|
||||||
$less->addLoadedModules();
|
|
||||||
$less->addFile($basedir . '/css/pdf/pdfprint.less');
|
|
||||||
return $less->compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function sendMinified()
|
/**
|
||||||
{
|
* Less compiler
|
||||||
self::send(true);
|
*
|
||||||
}
|
* @var LessCompiler
|
||||||
|
*/
|
||||||
|
protected $lessCompiler;
|
||||||
|
|
||||||
protected static function fixModuleLayoutCss($css)
|
/**
|
||||||
{
|
* Path to the public directory
|
||||||
return preg_replace(
|
*
|
||||||
'/(\.icinga-module\.module-[^\s]+) (#layout\.[^\s]+)/m',
|
* @var string
|
||||||
'\2 \1',
|
*/
|
||||||
$css
|
protected $pubPath;
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkPhp()
|
/**
|
||||||
|
* Create the StyleSheet
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
{
|
{
|
||||||
// PHP had a rather conservative PCRE backtrack limit unless 5.3.7
|
// PHP had a rather conservative PCRE backtrack limit unless 5.3.7
|
||||||
if (version_compare(PHP_VERSION, '5.3.7') <= 0) {
|
if (version_compare(PHP_VERSION, '5.3.7') <= 0) {
|
||||||
ini_set('pcre.backtrack_limit', 1000000);
|
ini_set('pcre.backtrack_limit', 1000000);
|
||||||
}
|
}
|
||||||
|
$app = Icinga::app();
|
||||||
|
$this->app = $app;
|
||||||
|
$this->lessCompiler = new LessCompiler();
|
||||||
|
$this->pubPath = $app->getBootstrapDirectory();
|
||||||
|
$this->collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect Web 2 and module LESS files and add them to the LESS compiler
|
||||||
|
*/
|
||||||
|
protected function collect()
|
||||||
|
{
|
||||||
|
foreach (self::$lessFiles as $lessFile) {
|
||||||
|
$this->lessCompiler->addLessFile($this->pubPath . '/' . $lessFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mm = $this->app->getModuleManager();
|
||||||
|
|
||||||
|
foreach ($mm->getLoadedModules() as $moduleName => $module) {
|
||||||
|
if ($module->hasCss()) {
|
||||||
|
foreach ($module->getCssFiles() as $lessFilePath) {
|
||||||
|
$this->lessCompiler->addModuleLessFile($moduleName, $lessFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$themingConfig = $this->app->getConfig()->getSection('themes');
|
||||||
|
$defaultTheme = $themingConfig->get('default', self::DEFAULT_THEME);
|
||||||
|
$theme = null;
|
||||||
|
|
||||||
|
if ((bool) $themingConfig->get('disabled', false)) {
|
||||||
|
if ($defaultTheme !== self::DEFAULT_THEME) {
|
||||||
|
$theme = $defaultTheme;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (($userTheme = $this->app->getRequest()->getCookie('theme', $defaultTheme))
|
||||||
|
&& $userTheme !== $defaultTheme
|
||||||
|
) {
|
||||||
|
$theme = $userTheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($theme) {
|
||||||
|
if (($pos = strpos($theme, '/')) !== false) {
|
||||||
|
$moduleName = substr($theme, 0, $pos);
|
||||||
|
$theme = substr($theme, $pos + 1);
|
||||||
|
if ($mm->hasLoaded($moduleName)) {
|
||||||
|
$module = $mm->getModule($moduleName);
|
||||||
|
$this->lessCompiler->setTheme($module->getCssDir() . '/themes/' . $theme . '.less');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->lessCompiler->setTheme($this->pubPath . '/css/themes/' . $theme . '.less');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the stylesheet for PDF export
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public static function forPdf()
|
||||||
|
{
|
||||||
|
$styleSheet = new self();
|
||||||
|
$styleSheet->lessCompiler->addLessFile($styleSheet->pubPath . '/css/pdf/pdfprint.less');
|
||||||
|
// TODO(el): Caching
|
||||||
|
return $styleSheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the stylesheet
|
||||||
|
*
|
||||||
|
* @param bool $minified Whether to compress the stylesheet
|
||||||
|
*
|
||||||
|
* @return string CSS
|
||||||
|
*/
|
||||||
|
public function render($minified = false)
|
||||||
|
{
|
||||||
|
if ($minified) {
|
||||||
|
$this->lessCompiler->compress();
|
||||||
|
}
|
||||||
|
return $this->lessCompiler->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the stylesheet to the client
|
||||||
|
*
|
||||||
|
* Does not cache the stylesheet if the HTTP header Cache-Control or Pragma is set to no-cache.
|
||||||
|
*
|
||||||
|
* @param bool $minified Whether to compress the stylesheet
|
||||||
|
*/
|
||||||
public static function send($minified = false)
|
public static function send($minified = false)
|
||||||
{
|
{
|
||||||
self::checkPhp();
|
$styleSheet = new self();
|
||||||
$app = Icinga::app();
|
|
||||||
$basedir = $app->getBootstrapDirectory();
|
|
||||||
foreach (self::$lessFiles as $file) {
|
|
||||||
$lessFiles[] = $basedir . '/' . $file;
|
|
||||||
}
|
|
||||||
$files = $lessFiles;
|
|
||||||
foreach ($app->getModuleManager()->getLoadedModules() as $name => $module) {
|
|
||||||
if ($module->hasCss()) {
|
|
||||||
foreach ($module->getCssFiles() as $path) {
|
|
||||||
if (file_exists($path)) {
|
|
||||||
$files[] = $path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($etag = FileCache::etagMatchesFiles($files)) {
|
$request = $styleSheet->app->getRequest();
|
||||||
header("HTTP/1.1 304 Not Modified");
|
$response = $styleSheet->app->getResponse();
|
||||||
|
|
||||||
|
$noCache = $request->getHeader('Cache-Control') === 'no-cache' || $request->getHeader('Pragma') === 'no-cache';
|
||||||
|
|
||||||
|
if (! $noCache && FileCache::etagMatchesFiles($styleSheet->lessCompiler->getLessFiles())) {
|
||||||
|
$response
|
||||||
|
->setHttpResponseCode(304)
|
||||||
|
->sendHeaders();
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
$etag = FileCache::etagForFiles($files);
|
|
||||||
}
|
}
|
||||||
header('Cache-Control: public');
|
|
||||||
header('ETag: "' . $etag . '"');
|
|
||||||
header('Content-Type: text/css');
|
|
||||||
|
|
||||||
$min = $minified ? '.min' : '';
|
$etag = FileCache::etagForFiles($styleSheet->lessCompiler->getLessFiles());
|
||||||
$cacheFile = 'icinga-' . $etag . $min . '.css';
|
|
||||||
|
$response
|
||||||
|
->setHeader('Cache-Control', 'public', true)
|
||||||
|
->setHeader('ETag', $etag, true)
|
||||||
|
->setHeader('Content-Type', 'text/css', true);
|
||||||
|
|
||||||
|
$cacheFile = 'icinga-' . $etag . ($minified ? '.min' : '') . '.css';
|
||||||
$cache = FileCache::instance();
|
$cache = FileCache::instance();
|
||||||
if ($cache->has($cacheFile)) {
|
|
||||||
$cache->send($cacheFile);
|
if (! $noCache && $cache->has($cacheFile)) {
|
||||||
return;
|
$response->setBody($cache->get($cacheFile));
|
||||||
|
} else {
|
||||||
|
$css = $styleSheet->render($minified);
|
||||||
|
$response->setBody($css);
|
||||||
|
$cache->store($cacheFile, $css);
|
||||||
}
|
}
|
||||||
|
|
||||||
$less = new LessCompiler();
|
$response->sendResponse();
|
||||||
$less->disableExtendedImport();
|
}
|
||||||
foreach ($lessFiles as $file) {
|
|
||||||
$less->addFile($file);
|
/**
|
||||||
|
* Render the stylesheet
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->render();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Logger::error($e);
|
||||||
|
return IcingaException::describe($e);
|
||||||
}
|
}
|
||||||
$less->addLoadedModules();
|
|
||||||
if ($minified) {
|
|
||||||
$less->compress();
|
|
||||||
}
|
|
||||||
$out = self::fixModuleLayoutCss($less->compile());
|
|
||||||
$cache->store($cacheFile, $out);
|
|
||||||
echo $out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -703,7 +703,7 @@ class FilterEditor extends AbstractWidget
|
||||||
|
|
||||||
public function renderSearch()
|
public function renderSearch()
|
||||||
{
|
{
|
||||||
$html = ' <form method="post" class="search inline dontprint" action="'
|
$html = ' <form method="post" class="search inline" action="'
|
||||||
. $this->preservedUrl()
|
. $this->preservedUrl()
|
||||||
. '"><input type="text" name="q" style="width: 8em" class="search" value="" placeholder="'
|
. '"><input type="text" name="q" style="width: 8em" class="search" value="" placeholder="'
|
||||||
. t('Search...')
|
. t('Search...')
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\Module\Doc;
|
|
||||||
|
|
||||||
use ArrayIterator;
|
|
||||||
use Countable;
|
|
||||||
use IteratorAggregate;
|
|
||||||
use RecursiveDirectoryIterator;
|
|
||||||
use RecursiveIteratorIterator;
|
|
||||||
use Icinga\File\NonEmptyFileIterator;
|
|
||||||
use Icinga\File\FileExtensionFilterIterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator over non-empty Markdown files ordered by the case insensitive "natural order" of file names
|
|
||||||
*/
|
|
||||||
class DocIterator implements Countable, IteratorAggregate
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Ordered files
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $fileInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new DocIterator
|
|
||||||
*
|
|
||||||
* @param string $path Path to the documentation
|
|
||||||
*/
|
|
||||||
public function __construct($path)
|
|
||||||
{
|
|
||||||
$it = new FileExtensionFilterIterator(
|
|
||||||
new NonEmptyFileIterator(
|
|
||||||
new RecursiveIteratorIterator(
|
|
||||||
new RecursiveDirectoryIterator($path),
|
|
||||||
RecursiveIteratorIterator::SELF_FIRST
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'md'
|
|
||||||
);
|
|
||||||
// Unfortunately we have no chance to sort the iterator
|
|
||||||
$fileInfo = iterator_to_array($it);
|
|
||||||
natcasesort($fileInfo);
|
|
||||||
$this->fileInfo = $fileInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
return count($this->fileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getIterator()
|
|
||||||
{
|
|
||||||
return new ArrayIterator($this->fileInfo);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,11 +4,11 @@
|
||||||
namespace Icinga\Module\Doc;
|
namespace Icinga\Module\Doc;
|
||||||
|
|
||||||
use CachingIterator;
|
use CachingIterator;
|
||||||
use LogicException;
|
use SplFileObject;
|
||||||
use SplStack;
|
use SplStack;
|
||||||
use Icinga\Data\Tree\SimpleTree;
|
use Icinga\Data\Tree\SimpleTree;
|
||||||
use Icinga\Exception\NotReadableError;
|
use Icinga\Exception\NotReadableError;
|
||||||
use Icinga\Module\Doc\Exception\DocEmptyException;
|
use Icinga\Util\DirectoryIterator;
|
||||||
use Icinga\Module\Doc\Exception\DocException;
|
use Icinga\Module\Doc\Exception\DocException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,7 @@ class DocParser
|
||||||
/**
|
/**
|
||||||
* Iterator over documentation files
|
* Iterator over documentation files
|
||||||
*
|
*
|
||||||
* @var DocIterator
|
* @var DirectoryIterator
|
||||||
*/
|
*/
|
||||||
protected $docIterator;
|
protected $docIterator;
|
||||||
|
|
||||||
|
@ -51,34 +51,17 @@ class DocParser
|
||||||
*
|
*
|
||||||
* @throws DocException If the documentation directory does not exist
|
* @throws DocException If the documentation directory does not exist
|
||||||
* @throws NotReadableError If the documentation directory is not readable
|
* @throws NotReadableError If the documentation directory is not readable
|
||||||
* @throws DocEmptyException If the documentation directory is empty
|
|
||||||
*/
|
*/
|
||||||
public function __construct($path)
|
public function __construct($path)
|
||||||
{
|
{
|
||||||
if (! is_dir($path)) {
|
if (! DirectoryIterator::isReadable($path)) {
|
||||||
throw new DocException(
|
throw new DocException(
|
||||||
sprintf(mt('doc', 'Documentation directory \'%s\' does not exist'), $path)
|
mt('doc', 'Documentation directory \'%s\' is not readable'),
|
||||||
);
|
|
||||||
}
|
|
||||||
if (! is_readable($path)) {
|
|
||||||
throw new DocException(
|
|
||||||
sprintf(mt('doc', 'Documentation directory \'%s\' is not readable'), $path)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$docIterator = new DocIterator($path);
|
|
||||||
if ($docIterator->count() === 0) {
|
|
||||||
throw new DocEmptyException(
|
|
||||||
sprintf(
|
|
||||||
mt(
|
|
||||||
'doc',
|
|
||||||
'Documentation directory \'%s\' does not contain any non-empty Markdown file (\'.md\' suffix)'
|
|
||||||
),
|
|
||||||
$path
|
$path
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
$this->docIterator = $docIterator;
|
$this->docIterator = new DirectoryIterator($path, 'md');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,9 +128,8 @@ class DocParser
|
||||||
public function getDocTree()
|
public function getDocTree()
|
||||||
{
|
{
|
||||||
$tree = new SimpleTree();
|
$tree = new SimpleTree();
|
||||||
foreach ($this->docIterator as $fileInfo) {
|
foreach ($this->docIterator as $filename) {
|
||||||
/** @var $fileInfo \SplFileInfo */
|
$file = new SplFileObject($filename);
|
||||||
$file = $fileInfo->openFile();
|
|
||||||
$lastLine = null;
|
$lastLine = null;
|
||||||
$stack = new SplStack();
|
$stack = new SplStack();
|
||||||
$cachingIterator = new CachingIterator($file, CachingIterator::TOSTRING_USE_CURRENT);
|
$cachingIterator = new CachingIterator($file, CachingIterator::TOSTRING_USE_CURRENT);
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\Module\Doc\Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception thrown if a documentation directory is empty
|
|
||||||
*/
|
|
||||||
class DocEmptyException extends DocException
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -70,6 +70,9 @@ class DocTocRenderer extends DocRenderer
|
||||||
*/
|
*/
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
if (empty($this->content)) {
|
||||||
|
return '<p>' . mt('doc', 'Documentation is empty.') . '</p>';
|
||||||
|
}
|
||||||
$view = $this->getView();
|
$view = $this->getView();
|
||||||
$zendUrlHelper = $view->getHelper('Url');
|
$zendUrlHelper = $view->getHelper('Url');
|
||||||
foreach ($this as $section) {
|
foreach ($this as $section) {
|
||||||
|
|
|
@ -258,19 +258,4 @@ class TransportConfigForm extends ConfigForm
|
||||||
$this->populate($data);
|
$this->populate($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all form element values
|
|
||||||
*
|
|
||||||
* @param bool $suppressArrayNotation Ignored
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['transport_form']);
|
|
||||||
unset($values['transport_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php if (! $this->compact): ?>
|
<?php if (! $this->compact): ?>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<?= $this->tabs; ?>
|
<?= $this->tabs; ?>
|
||||||
<div style="float: right;" class="dontprint">
|
<div style="float: right;" class="dont-print">
|
||||||
<?= $intervalBox; ?>
|
<?= $intervalBox; ?>
|
||||||
</div>
|
</div>
|
||||||
<?= $this->limiter; ?>
|
<?= $this->limiter; ?>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php if (! $this->compact): ?>
|
<?php if (! $this->compact): ?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?= $tabs ?>
|
<?= $tabs ?>
|
||||||
<?= $this->render('list/components/selectioninfo.phtml') ?>
|
<?= $this->render('list/components/selectioninfo.phtml') ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|
|
@ -7,7 +7,7 @@ if (! $stats instanceof stdClass) {
|
||||||
$stats = $stats->fetchRow();
|
$stats = $stats->fetchRow();
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="hosts-summary">
|
<div class="hosts-summary dont-print">
|
||||||
<span class="hosts-link"><?= $this->qlink(
|
<span class="hosts-link"><?= $this->qlink(
|
||||||
sprintf($this->translatePlural('%u Host', '%u Hosts', $stats->hosts_total), $stats->hosts_total),
|
sprintf($this->translatePlural('%u Host', '%u Hosts', $stats->hosts_total), $stats->hosts_total),
|
||||||
// @TODO(el): Fix that
|
// @TODO(el): Fix that
|
||||||
|
|
|
@ -7,7 +7,7 @@ if (! $stats instanceof stdClass) {
|
||||||
$stats = $stats->fetchRow();
|
$stats = $stats->fetchRow();
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="services-summary">
|
<div class="services-summary dont-print">
|
||||||
<span class="services-link"><?= $this->qlink(
|
<span class="services-link"><?= $this->qlink(
|
||||||
sprintf($this->translatePlural(
|
sprintf($this->translatePlural(
|
||||||
'%u Service', '%u Services', $stats->services_total),
|
'%u Service', '%u Services', $stats->services_total),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use Icinga\Module\Monitoring\Object\Host;
|
||||||
use Icinga\Module\Monitoring\Object\Service;
|
use Icinga\Module\Monitoring\Object\Service;
|
||||||
|
|
||||||
if (! $this->compact): ?>
|
if (! $this->compact): ?>
|
||||||
<div class="controls separated dont-print">
|
<div class="controls separated">
|
||||||
<?= $tabs ?>
|
<?= $tabs ?>
|
||||||
<?= $this->render('list/components/selectioninfo.phtml') ?>
|
<?= $this->render('list/components/selectioninfo.phtml') ?>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
|
|
|
@ -18,9 +18,11 @@ if (! $this->compact): ?>
|
||||||
<?php return; endif ?>
|
<?php return; endif ?>
|
||||||
<table class="table-row-selectable common-table" data-base-target="_next">
|
<table class="table-row-selectable common-table" data-base-target="_next">
|
||||||
<thead>
|
<thead>
|
||||||
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th><?= $this->translate('Service Group') ?></th>
|
<th><?= $this->translate('Service Group') ?></th>
|
||||||
<th><?= $this->translate('Service States') ?></th>
|
<th><?= $this->translate('Service States') ?></th>
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($servicegroups->peekAhead($this->compact) as $serviceGroup): ?>
|
<?php foreach ($servicegroups->peekAhead($this->compact) as $serviceGroup): ?>
|
||||||
|
|
|
@ -186,12 +186,14 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
tr[href] {
|
tr[href] {
|
||||||
|
.transition(background 0.2s ease);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: @gray-lighter;
|
background-color: @tr-active-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: @gray-lightest;
|
background-color: @tr-hover-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,21 +116,6 @@ class AuthBackendPage extends Form
|
||||||
$this->addSubForm($backendForm, 'backend_form');
|
$this->addSubForm($backendForm, 'backend_form');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all form element values
|
|
||||||
*
|
|
||||||
* @param bool $suppressArrayNotation Ignored
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['backend_form']);
|
|
||||||
unset($values['backend_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the given form data and check whether it's possible to authenticate using the configured backend
|
* Validate the given form data and check whether it's possible to authenticate using the configured backend
|
||||||
*
|
*
|
||||||
|
|
|
@ -129,19 +129,4 @@ class UserGroupBackendPage extends Form
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve all form element values
|
|
||||||
*
|
|
||||||
* @param bool $suppressArrayNotation Ignored
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getValues($suppressArrayNotation = false)
|
|
||||||
{
|
|
||||||
$values = parent::getValues();
|
|
||||||
$values = array_merge($values, $values['backend_form']);
|
|
||||||
unset($values['backend_form']);
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
// Text color on <a>
|
// Text color on <a>
|
||||||
@link-color: @text-color;
|
@link-color: @text-color;
|
||||||
|
|
||||||
|
@tr-active-color: #E5F9FF;
|
||||||
|
@tr-hover-color: #F5FDFF;
|
||||||
|
|
||||||
// Font families
|
// Font families
|
||||||
@font-family: Calibri, Helvetica, sans-serif;
|
@font-family: Calibri, Helvetica, sans-serif;
|
||||||
@font-family-fixed: "Liberation Mono", "Lucida Console", Courier, monospace;
|
@font-family-fixed: "Liberation Mono", "Lucida Console", Courier, monospace;
|
||||||
|
@ -138,19 +141,11 @@ td, th {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#layout {
|
// Styles for when the page is loading. JS will remove this class once the document is ready
|
||||||
background-color: @body-bg-color;
|
.loading * {
|
||||||
color: @text-color;
|
// Disable all transition on page load
|
||||||
font-family: @font-family;
|
-webkit-transition: none !important;
|
||||||
}
|
-moz-transition: none !important;
|
||||||
|
-o-transition: none !important;
|
||||||
#main > .container, #menu, #header {
|
transition: none !important;
|
||||||
font-size: @font-size;
|
|
||||||
line-height: @line-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
.dont-print {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
div#footer {
|
|
||||||
position: fixed;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Notifications **/
|
|
||||||
|
|
||||||
#notifications {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications > li {
|
|
||||||
list-style-type: none;
|
|
||||||
display: block;
|
|
||||||
border-top: 1px solid #999;
|
|
||||||
color: white;
|
|
||||||
line-height: 2.5em;
|
|
||||||
padding-left: 3em;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 1em center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications > li:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications > li.info {
|
|
||||||
background-color: @colorFormNotificationInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications > li.warning {
|
|
||||||
background-color: @colorWarningHandled;
|
|
||||||
}
|
|
||||||
#notifications > li.error {
|
|
||||||
background-color: @colorCritical;
|
|
||||||
background-image: url(../img/icons/error_white.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications > li.success {
|
|
||||||
background-color: #fe6;
|
|
||||||
background-image: url(../img/icons/success.png);
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
/** END of Notifications **/
|
|
|
@ -1,18 +0,0 @@
|
||||||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
height: 4.0em;
|
|
||||||
width: 13em;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo a {
|
|
||||||
display: block;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo img {
|
|
||||||
width: 100px;
|
|
||||||
margin-left: 1.5em;
|
|
||||||
margin-top: 0.8em;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
/* Layout colors */
|
|
||||||
|
|
||||||
#sidebar {
|
|
||||||
background-color: @gray-lighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
background-color: @icinga-blue;
|
|
||||||
color: #ddd;
|
|
||||||
color: #d0d0d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header input {
|
|
||||||
background-color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#col1.impact, #col2.impact, #col3.impact {
|
|
||||||
background-color: #ddd;
|
|
||||||
transition: background-color 2s 1s linear;
|
|
||||||
-moz-transition: background-color 2s 1s linear;
|
|
||||||
-o-transition: background-color 2s 1s linear;
|
|
||||||
-webkit-transition: background-color 2s 1s linear;
|
|
||||||
.controls {
|
|
||||||
background-color: #ddd;
|
|
||||||
transition: background-color 2s 1s linear;
|
|
||||||
-moz-transition: background-color 2s 1s linear;
|
|
||||||
-o-transition: background-color 2s 1s linear;
|
|
||||||
-webkit-transition: background-color 2s 1s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -96,7 +96,6 @@ html {
|
||||||
.controls > ul.tabs {
|
.controls > ul.tabs {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
background-color: @icinga-blue;
|
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
padding: 0.2em 0 0;
|
padding: 0.2em 0 0;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +137,6 @@ html {
|
||||||
*/
|
*/
|
||||||
.container .controls {
|
.container .controls {
|
||||||
top: 0;
|
top: 0;
|
||||||
background-color: white;
|
|
||||||
padding: 1em 1em 0;
|
padding: 1em 1em 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
|
@ -267,42 +265,9 @@ html {
|
||||||
margin-right: 1%;
|
margin-right: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo img {
|
|
||||||
/* TODO: Quickfix, this needs improvement */
|
|
||||||
width: 0 !important;
|
|
||||||
top: -100px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
#main {
|
#main {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login {
|
|
||||||
.below-logo label {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin-left: 0px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
form input {
|
|
||||||
margin: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
form input[type=submit] {
|
|
||||||
margin-top: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#guest-error {
|
||||||
|
background-color: @icinga-blue;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#guest-error #icinga-logo {
|
||||||
|
.fadein();
|
||||||
|
}
|
||||||
|
|
||||||
|
#guest-error-message {
|
||||||
|
.fadein();
|
||||||
|
color: @body-bg-color;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header,
|
||||||
|
#main > .container,
|
||||||
|
#menu {
|
||||||
|
font-size: @font-size;
|
||||||
|
line-height: @line-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
background-color: @icinga-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header-logo-container {
|
||||||
|
height: 4em;
|
||||||
|
margin-left: 1.5em;
|
||||||
|
margin-top: 0.2em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header-logo {
|
||||||
|
background-image: url('../img/logo_icinga-inv.png');
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#icinga-logo {
|
||||||
|
background-image: url('../img/logo_icinga_big.png');
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain; // Does not work in IE < 10
|
||||||
|
height: 177px;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layout {
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
color: @text-color;
|
||||||
|
font-family: @font-family;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
background-color: @gray-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification styles
|
||||||
|
|
||||||
|
#notifications {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications > li {
|
||||||
|
list-style-type: none;
|
||||||
|
display: block;
|
||||||
|
border-top: 1px solid #999;
|
||||||
|
color: white;
|
||||||
|
line-height: 2.5em;
|
||||||
|
padding-left: 3em;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 1em center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications > li:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications > li.info {
|
||||||
|
background-color: @colorFormNotificationInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications > li.warning {
|
||||||
|
background-color: @colorWarningHandled;
|
||||||
|
}
|
||||||
|
#notifications > li.error {
|
||||||
|
background-color: @colorCritical;
|
||||||
|
background-image: url(../img/icons/error_white.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications > li.success {
|
||||||
|
background-color: #fe6;
|
||||||
|
background-image: url(../img/icons/success.png);
|
||||||
|
color: #333;
|
||||||
|
}
|
|
@ -1,189 +1,91 @@
|
||||||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
// Login page styles
|
||||||
|
|
||||||
#login {
|
#login {
|
||||||
|
#icinga-logo {
|
||||||
|
.fadein();
|
||||||
|
}
|
||||||
|
|
||||||
background-color: @icinga-blue;
|
background-color: @icinga-blue;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.form-controls {
|
.control-label {
|
||||||
margin: 0;
|
color: @body-bg-color;
|
||||||
}
|
font-size: 1em;
|
||||||
|
line-height: @line-height;
|
||||||
.control-group {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
padding-top: 5%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.errors {
|
.errors {
|
||||||
font-size: 0.9em;
|
background-color: @color-critical;
|
||||||
margin-bottom: 0.5em;
|
color: @body-bg-color;
|
||||||
margin-left:auto;
|
font-size: @font-size-small;
|
||||||
margin-right:auto;
|
margin: 2em 0 0 0;
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image img {
|
> li {
|
||||||
width: 355px;
|
padding: 1em;
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 0.9em;
|
|
||||||
top: 35%;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.5em;
|
|
||||||
margin-left: 2.3em;
|
|
||||||
border: none;
|
|
||||||
color: @text-color-inverted;
|
|
||||||
font-variant: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form div.element {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
ul.errors {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form label {
|
.control-group {
|
||||||
color: @text-color-inverted;
|
margin: 0 auto; // Center horizontally
|
||||||
font-weight: normal;
|
width: 24em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-label-group {
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 2.5em;
|
padding: 0;
|
||||||
width: 15em;
|
|
||||||
margin-right: 2.5em;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
input[type=password],
|
||||||
margin-left: auto;
|
input[type=text] {
|
||||||
margin-right: auto;
|
background-color: @gray-light; // IE8 fallback
|
||||||
width: 18em;
|
background-color: rgba(255,255,255,0.2);
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input {
|
|
||||||
color: @text-color-inverted;
|
|
||||||
width: 18em;
|
|
||||||
padding: 0.5em;
|
|
||||||
background: rgba(255,255,255,0.2);
|
|
||||||
margin-left: 0;
|
|
||||||
padding: 6px 10px;
|
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: solid 3px @body-bg-color;
|
border-bottom: solid 3px @body-bg-color;
|
||||||
-webkit-transition: border 0.3s;
|
color: @body-bg-color;
|
||||||
-moz-transition: border 0.3s;
|
display: block;
|
||||||
-o-transition: border 0.3s;
|
margin: 0;
|
||||||
transition: border 0.3s;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input:focus {
|
.form-controls {
|
||||||
border-bottom: solid 3px @body-bg-color;
|
margin-bottom: 2em;
|
||||||
background: rgba(255,255,255,0.4);
|
margin-top: 2em;
|
||||||
}
|
|
||||||
|
|
||||||
ul.errors {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit] {
|
input[type=submit] {
|
||||||
color: @text-color-inverted;
|
.button(@icinga-blue, @body-bg-color);
|
||||||
background: none;
|
margin: 0;
|
||||||
border: none;
|
width: 100%;
|
||||||
margin-top: 3em;
|
|
||||||
margin-right: 5px;
|
|
||||||
border: solid 3px @body-bg-color;
|
|
||||||
-webkit-transition: border 0.3s;
|
|
||||||
-moz-transition: border 0.3s;
|
|
||||||
-o-transition: border 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:hover, a.button:hover, input[type=submit]:focus {
|
.config-note {
|
||||||
background: @body-bg-color;
|
|
||||||
color: @icinga-blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
color: @text-color-inverted;
|
|
||||||
margin-top: 5em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: @text-color-inverted;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.config-note {
|
|
||||||
width: 50%;
|
|
||||||
padding: 1em;
|
|
||||||
margin: 0 auto 2.5em;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
color: white;
|
||||||
background-color: @color-critical;
|
background-color: @color-critical;
|
||||||
|
margin: 0 auto 2em auto; // Center horizontally w/ bottom margin
|
||||||
|
max-width: 50%;
|
||||||
|
min-width: 24em;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: @text-color-inverted;
|
color: @text-color-inverted;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.info-box {
|
|
||||||
width: 50%;
|
|
||||||
height: 2.2em;
|
|
||||||
margin: 2em auto 2.5em;
|
|
||||||
|
|
||||||
i.icon-info {
|
|
||||||
float: left;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
em {
|
#login-footer {
|
||||||
text-decoration: underline;
|
color: @body-bg-color;
|
||||||
|
font-size: @font-size-small;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: @font-weight-bold;
|
||||||
|
|
||||||
|
// Social links
|
||||||
|
&:hover > i {
|
||||||
|
color: @text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make keyframes that tell the start state and the end state of our object */
|
|
||||||
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
|
||||||
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
|
||||||
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
|
||||||
|
|
||||||
.fade-in {
|
|
||||||
opacity:0; /* make things invisible upon start */
|
|
||||||
-webkit-animation:fadeIn ease-in 1; /* call our keyframe named fadeIn, use animattion ease-in and repeat it only 1 time */
|
|
||||||
-moz-animation:fadeIn ease-in 1;
|
|
||||||
animation:fadeIn ease-in 1;
|
|
||||||
|
|
||||||
-webkit-animation-fill-mode:forwards; /* this makes sure that after animation is done we remain at the last keyframe value (opacity: 1)*/
|
|
||||||
-moz-animation-fill-mode:forwards;
|
|
||||||
animation-fill-mode:forwards;
|
|
||||||
|
|
||||||
-webkit-animation-duration:1s;
|
|
||||||
-moz-animation-duration:1s;
|
|
||||||
animation-duration:1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-in.one {
|
|
||||||
-webkit-animation-delay: 0.7s;
|
|
||||||
-moz-animation-delay: 0.7s;
|
|
||||||
animation-delay: 0.7s;
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
// Width for the name column--th--of name-value-table
|
// Width for the name column--th--of name-value-table
|
||||||
@name-value-table-name-width: 14em;
|
@name-value-table-name-width: 14em;
|
||||||
|
|
||||||
.action-link {
|
.action-link(@color: @icinga-blue) {
|
||||||
color: @icinga-blue;
|
color: @color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-weight: @font-weight-bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.large-icon {
|
.large-icon {
|
||||||
|
@ -58,9 +62,9 @@ a:hover > .icon-cancel {
|
||||||
|
|
||||||
// Link styles
|
// Link styles
|
||||||
|
|
||||||
.button-link {
|
.button-link(@background-color: @body-bg-color, @color: @icinga-blue) {
|
||||||
.action-link();
|
.action-link(@color);
|
||||||
.button();
|
.button(@background-color, @color);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
@ -164,13 +168,16 @@ a:hover > .icon-cancel {
|
||||||
}
|
}
|
||||||
|
|
||||||
tr[href] {
|
tr[href] {
|
||||||
|
.transition(background 0.2s ease);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: @gray-lighter;
|
background-color: @tr-active-color;
|
||||||
border-left-color: @icinga-blue;
|
border-left-color: @icinga-blue;
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: @gray-lightest;
|
background-color: @tr-hover-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,3 +234,35 @@ a:hover > .icon-cancel {
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Styles for centering content of unknown width and height both horizontally and vertically
|
||||||
|
*
|
||||||
|
* Example markup:
|
||||||
|
* <div class="centered-ghost">
|
||||||
|
* <div class="centered-content">
|
||||||
|
* <p>I'm centered.</p>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
|
||||||
|
.centered-content {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-ghost {
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: -1em; // Remove gap between content and ghost
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-ghost > * {
|
||||||
|
letter-spacing: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered-ghost:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
|
@ -12,22 +12,24 @@
|
||||||
box-shadow: @arguments;
|
box-shadow: @arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button() {
|
.button(@background-color: @body-bg-color, @color: @icinga-blue) {
|
||||||
.rounded-corners(3px);
|
.rounded-corners(3px);
|
||||||
background-color: @body-bg-color;
|
|
||||||
border: 2px solid @icinga-blue;
|
background-color: @background-color;
|
||||||
color: @icinga-blue;
|
border: 2px solid @color;
|
||||||
|
color: @color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: @vertical-padding @horizontal-padding;
|
padding: @vertical-padding @horizontal-padding;
|
||||||
|
|
||||||
|
// Transition mixin does not work w/ comma-separated transistions
|
||||||
-webkit-transition: background 0.3s ease, color 0.3s ease;
|
-webkit-transition: background 0.3s ease, color 0.3s ease;
|
||||||
-moz-transition: background 0.3s ease, color 0.3s ease;
|
-moz-transition: background 0.3s ease, color 0.3s ease;
|
||||||
-o-transition: background 0.3s ease, color 0.3s ease;
|
-o-transition: background 0.3s ease, color 0.3s ease;
|
||||||
transition: background 0.3s ease, color 0.3s ease;
|
transition: background 0.3s ease, color 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: @icinga-blue;
|
background-color: @color;
|
||||||
color: @text-color-inverted;
|
color: @background-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,3 +81,49 @@
|
||||||
.visible {
|
.visible {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fadein animation
|
||||||
|
|
||||||
|
/* Chrome, WebKit */
|
||||||
|
@-webkit-keyframes fadein {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FF < 16 */
|
||||||
|
@-moz-keyframes fadein {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IE */
|
||||||
|
@-ms-keyframes fadein {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opera < 12.1 */
|
||||||
|
@-o-keyframes fadein {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadein {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadein() {
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
-webkit-animation: fadein 2s ease-in; /* Chrome, WebKit */
|
||||||
|
-moz-animation: fadein 2s ease-in; /* FF < 16 */
|
||||||
|
-o-animation: fadein 2s ease-in; /* Opera < 12.1 */
|
||||||
|
animation: fadein 2s ease-in;
|
||||||
|
|
||||||
|
// Make sure that after animation is done we remain at the last keyframe value (opacity: 1)
|
||||||
|
-webkit-animation-fill-mode: forwards;
|
||||||
|
-moz-animation-fill-mode: forwards;
|
||||||
|
-o-animation-fill-mode: forwards;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
// Print styles
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
#fileupload-frame-target,
|
||||||
|
#header,
|
||||||
|
#responsive-debug,
|
||||||
|
#sidebar,
|
||||||
|
.controls > .tabs,
|
||||||
|
.controls .filter,
|
||||||
|
.controls .limiter-control,
|
||||||
|
.controls .pagination-control,
|
||||||
|
.controls .selection-info,
|
||||||
|
.controls .sort-control,
|
||||||
|
.dontprint, // Compat only, use dont-print instead
|
||||||
|
.dont-print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layout,
|
||||||
|
#main,
|
||||||
|
.controls {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
float: none !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
.controls form, .controls .pagination, .controls .widgetLimiter, .controls > .tabs, .dontprint {
|
.controls form,
|
||||||
|
.controls .pagination,
|
||||||
|
.controls .widgetLimiter,
|
||||||
|
.controls > .tabs,
|
||||||
|
.dontprint, // Compat only, use dont-print instead
|
||||||
|
.dont-print {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,4 +98,3 @@ h1 form {
|
||||||
body {
|
body {
|
||||||
margin: 1cm 1cm 1.5cm 1cm;
|
margin: 1cm 1cm 1.5cm 1cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad: function (event) {
|
onLoad: function (event) {
|
||||||
|
$('body').removeClass('loading');
|
||||||
//$('.container').trigger('rendered');
|
//$('.container').trigger('rendered');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue