Re-design configuration interface

Conflicts:
	public/js/icinga/events.js
This commit is contained in:
Alexander Klimov 2014-03-05 13:38:38 +01:00
parent 63c73a8215
commit d2ec3c9509
6 changed files with 327 additions and 347 deletions

View File

@ -134,6 +134,8 @@ class DbBackendForm extends BaseBackendForm
*/ */
public function isValidAuthenticationBackend() public function isValidAuthenticationBackend()
{ {
// @TODO fix validation of authentication backends (AK #5712)
//return true;
try { try {
$name = $this->getBackendName(); $name = $this->getBackendName();
$dbBackend = new DbUserBackend( $dbBackend = new DbUserBackend(

View File

@ -114,10 +114,9 @@ class ReorderForm extends Form
array( array(
'type' => 'submit', 'type' => 'submit',
'escape' => false, 'escape' => false,
'class' => 'btn btn-cta btn-wide',
'value' => 'btn_' . $this->getBackendName() . '_reorder_up', 'value' => 'btn_' . $this->getBackendName() . '_reorder_up',
'name' => 'btn_' . $this->getBackendName() . '_reorder_up', 'name' => 'btn_' . $this->getBackendName() . '_reorder_up',
'label' => '<i class="icinga-icon-up"></i> Move up in authentication order', 'label' => '<img src="/icingaweb/img/icons/up.png" title="Move up in authentication order" />',
) )
); );
} }
@ -137,10 +136,9 @@ class ReorderForm extends Form
array( array(
'type' => 'submit', 'type' => 'submit',
'escape' => false, 'escape' => false,
'class' => 'btn btn-cta btn-wide',
'value' => 'btn_' . $this->getBackendName() . '_reorder_down', 'value' => 'btn_' . $this->getBackendName() . '_reorder_down',
'name' => 'btn_' . $this->getBackendName() . '_reorder_down', 'name' => 'btn_' . $this->getBackendName() . '_reorder_down',
'label' => '<i class="icinga-icon-down"></i> Move down in authentication order', 'label' => '<img src="/icingaweb/img/icons/down.png" title="Move down in authentication order" />',
) )
); );

View File

@ -4,43 +4,41 @@ $createLdapBackend = $this->href('/config/createAuthenticationBackend', array('t
$createDbBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'db')); $createDbBackend = $this->href('/config/createAuthenticationBackend', array('type' => 'db'));
?> ?>
<div class="controls"> <div class="controls">
<?= $this->tabs->render($this); ?> <?= $this->tabs->render($this); ?>
</div> </div>
<div class="content"> <div class="content">
<?php if (isset($this->messageBox)): ?> <?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?> <?= $this->messageBox->render() ?>
<?php endif ?> <?php endif ?>
<div> <h1>Create Authentication Provider</h1>
<div> <p>
Create Authentication Provider <a href="<?= $createLdapBackend ?>"><?= $this->img('img/icons/create.png', array('title' => 'Create A New LDAP Authentication Backend'));
</div> ?> Create A New LDAP Authentication Backend</a>
<div> <br />
<a href="<?= $createLdapBackend ?>"><i class="icinga-icon-create"></i> Create A New LDAP Authentication Backend</a><br/> <a href="<?= $createDbBackend ?>"><?= $this->img('img/icons/create.png', array('title' => 'Create A New DB Authentication Backend'));
<a href="<?= $createDbBackend ?>"><i class="icinga-icon-create"></i> Create A New DB Authentication Backend</a> ?> Create A New DB Authentication Backend</a>
</div> </p>
</div>
<?php foreach ($this->backends as $backend): ?> <table class="configTable">
<div> <?php foreach ($this->backends as $backend): ?>
<div> <tr>
Backend <?= $this->escape($backend->name); ?> <td class="configTable">
<br/> <div>
<?= $backend->reorderForm; ?> <b>Backend:</b>&ensp;<?= $this->escape($backend->name); ?>
</div> </div>
<div> <div>
<a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backend->name));?>"> <?= $backend->reorderForm; ?>
<i class="icinga-icon-edit"></i> Edit This Authentication Provider <a href="<?= $this->href('config/editAuthenticationBackend', array('auth_backend' => $backend->name));?>">
</a> <?= $this->img('img/icons/edit.png', array('title' => 'Edit')); ?> Edit</a>&emsp;
<br/> <?php if (count($this->backends) > 1): ?>
<?php if (count($this->backends) > 1): ?> <a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backend->name));?>">
<a href="<?= $this->href('config/removeAuthenticationBackend', array('auth_backend' => $backend->name));?>"> <?= $this->img('img/icons/remove.png', array('title' => 'Remove')); ?> Remove</a>&emsp;
<i class="icinga-icon-remove"></i> Remove This Authentication Provider <?php endif; ?>
</a> </div>
<br/> </td>
<?php endif; ?> </tr>
</div> <?php endforeach; ?>
</div> </table>
<?php endforeach; ?>
</div> </div>

View File

@ -3,47 +3,36 @@ use Icinga\Web\Url;
$createResource = $this->href('/config/createresource'); $createResource = $this->href('/config/createresource');
?> ?>
<div class="controls"> <div class="controls">
<?= $this->tabs->render($this); ?> <?= $this->tabs->render($this); ?>
</div> </div>
<div class="content"> <div class="content">
<?php if (isset($this->messageBox)): ?> <?php if (isset($this->messageBox)): ?>
<?= $this->messageBox->render() ?> <?= $this->messageBox->render() ?>
<?php endif ?> <?php endif ?>
<div> <h1>Create Resource</h1>
<div> <p><a href="<?= $createResource ?>"><?= $this->img('img/icons/create.png', array('title' => 'Create A New Resource'));
Create Resource ?> Create A New Resource</a></p>
</div>
<div> <table class="configTable">
<a href="<?= $createResource ?>"><i class="icinga-icon-create"></i> Create A New Resource</a><br/> <?php foreach ($this->resources as $name => $resource): ?>
</div> <tr>
</div> <td class="configTable">
<div>
<?php foreach ($this->resources as $name => $resource): ?> <b>Resource:</b>&ensp;<?= $this->escape($name); ?>
<div> </div>
<div>
<div> <a href="<?= $this->href('config/editresource', array('resource' => $name));?>">
<b>Resource: </b> <?= $this->escape($name); ?> <?= $this->img('img/icons/edit.png', array('title' => 'Edit')); ?> Edit</a>&emsp;
<br/> <?php if (count($this->resources) > 1): ?>
</div> <a href="<?= $this->href('config/removeresource', array('resource' => $name));?>">
<div> <?= $this->img('img/icons/remove.png', array('title' => 'Remove')); ?> Remove</a>&emsp;
<?php endif; ?>
<a href="<?= $this->href('config/editresource', array('resource' => $name));?>"> </div>
<i class="icinga-icon-edit"></i> Edit This Resource </td>
</a> </tr>
<br/> <?php endforeach; ?>
</table>
<?php if (count($this->resources) > 1): ?>
<a href="<?= $this->href('config/removeresource', array('resource' => $name));?>">
<i class="icinga-icon-remove"></i> Remove This Resource
</a>
<br/>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div> </div>

View File

@ -1,4 +1,19 @@
table.configTable {
border-spacing: 10px;
border-collapse: separate;
}
td.configTable {
border: solid;
border-width: thin;
padding: 10px;
}
div.form-group {
float: right;
}
.form-group { .form-group {
margin-bottom: 1em; margin-bottom: 1em;
} }

View File

@ -1,288 +1,266 @@
/** (function(Icinga) {
* Icinga.Events
*
* Event handlers
*/
(function (Icinga, $) {
'use strict'; Icinga.Events = function(icinga) {
this.icinga = icinga;
};
Icinga.Events = function (icinga) { Icinga.Events.prototype = {
this.icinga = icinga;
};
Icinga.Events.prototype = { /**
* Icinga will call our initialize() function once it's ready
*/
initialize: function()
{
this.applyGlobalDefaults();
this.icinga.ui.prepareContainers();
},
/** // TODO: What's this?
* Icinga will call our initialize() function once it's ready applyHandlers: function(el)
*/ {
initialize: function () { var icinga = this.icinga;
this.applyGlobalDefaults(); $('.dashboard > div', el).each(function(idx, el) {
this.applyHandlers($('#layout')); var url = $(el).attr('data-icinga-url');
this.icinga.ui.prepareContainers(); if (typeof url === 'undefined') return;
}, icinga.loader.loadUrl(url, $(el)).autorefresh = true;
});
// TODO: What's this? // Set first links href in a action table tr as row href:
applyHandlers: function (el) { $('table.action tr', el).each(function(idx, el) {
var $a = $('a[href]', el).first();
var icinga = this.icinga; if ($a.length) {
$(el).attr('href', $a.attr('href'));
$('.dashboard > div', el).each(function(idx, el) { }
var url = $(el).attr('data-icinga-url'); });
if (typeof url === 'undefined') return; $('.icinga-module', el).each(function(idx, mod) {
icinga.loader.loadUrl(url, $(el)).autorefresh = true; $mod = $(mod);
}); var moduleName = $mod.data('icinga-module');
if (icinga.hasModule(moduleName)) {
// Set first links href in a action table tr as row href: var module = icinga.module(moduleName);
$('table.action tr', el).each(function(idx, el) { // NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
var $a = $('a[href]', el).first();
if ($a.length) {
$(el).attr('href', $a.attr('href'));
}
});
$('.icinga-module', el).each(function(idx, mod) {
var $mod = $(mod);
var moduleName = $mod.data('icinga-module');
if (icinga.hasModule(moduleName)) {
var module = icinga.module(moduleName);
// NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
}
});
$('input.autofocus', el).focus();
$('.inlinepie', el).sparkline('html', {
type: 'pie',
sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
width: '2em',
height: '2em',
});
},
/**
* Global default event handlers
*/
applyGlobalDefaults: function () {
// We catch resize events
$(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize);
// Destroy Icinga, clean up and interrupt pending requests on unload
$( window ).on('unload', { self: this }, this.onUnload);
$( window ).on('beforeunload', { self: this }, this.onUnload);
// We catch scroll events in our containers
$('.container').on('scroll', icinga.events.onContainerScroll);
// We want to catch each link click
$(document).on('click', 'a', { self: this }, this.linkClicked);
// We treat tr's with a href attribute like links
$(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
// We catch all form submit events
$(document).on('submit', 'form', { self: this }, this.submitForm);
// We support an 'autosubmit' class on dropdown form elements
$(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm);
$(document).on('keyup', '#menu input.search', {self: this}, this.submitForm);
$(document).on('mouseenter', '.historycolorgrid td', this.historycolorgridHover);
$(document).on('mouseleave', '.historycolorgrid td', this.historycolorgidUnhover);
// TBD: a global autocompletion handler
// $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
// $(document).on('change', 'form.auto input', this.formChanged);
// $(document).on('change', 'form.auto select', this.submitForm);
},
onUnload: function (event) {
var icinga = event.data.self.icinga;
icinga.logger.info('Unloading Icinga');
icinga.destroy();
},
/**
* A scroll event happened in one of our containers
*/
onContainerScroll: function (event) {
// Ugly. And PLEASE, not so often
icinga.ui.fixControls();
},
historycolorgridHover: function () {
$(this).addClass('hover');
},
historycolorgidUnhover: function() {
$(this).removeClass('hover');
},
/**
*
*/
submitForm: function (event) {
var icinga = event.data.self.icinga;
event.stopPropagation();
event.preventDefault();
// .closest is not required unless subelements to trigger this
var $form = $(event.currentTarget).closest('form');
var url = $form.attr('action');
var method = $form.attr('method');
var data = $form.serializeArray();
// TODO: Check button
data.push({ name: 'btn_submit', value: 'yesss' });
icinga.logger.debug('Submitting form: ' + method + ' ' + url);
// We should move this to a generic target-finder:
var $target = null;
if ($form.closest('[data-base-target]').length) {
$target = $(
'#' + $form.closest('[data-base-target]').data('baseTarget')
);
} else if ($form.closest('.container').length) {
$target = $form.closest('.container');
} else {
icinga.logger.error('No form target found, stopping here');
return false;
} }
});
icinga.loader.loadUrl(url, $target, data, method); $('.inlinepie', el).sparkline('html', {
// TODO: Do we really need to return false with stop/preventDefault? type: 'pie',
return false; sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
}, width: '2em',
height: '2em',
});
layout1col: function () { },
if (! $('#layout').hasClass('twocols')) { return; } /**
var $col2 = $('#col2'); * Global default event handlers
icinga.logger.debug('Switching to single col'); */
$('#layout').removeClass('twocols'); applyGlobalDefaults: function()
$col2.removeAttr('data-icinga-url'); {
$col2.removeAttr('data-icinga-refresh'); // We catch resize events
$col2.removeData('icingaUrl'); $(window).on('resize', { self: this }, this.onWindowResize);
$col2.removeData('icingaRefresh');
this.icinga.loader.stopPendingRequestsFor($col2);
$col2.html('');
this.icinga.ui.fixControls();
},
layout2col: function () { // Destroy Icinga, clean up and interrupt pending requests on unload
if ($('#layout').hasClass('twocols')) { return; } $( window ).on('unload', { self: this }, this.onUnload);
icinga.logger.debug('Switching to double col'); $( window ).on('beforeunload', { self: this }, this.onUnload);
$('#layout').addClass('twocols');
this.icinga.ui.fixControls();
},
/** // We catch scroll events in our containers
* Someone clicked a link or tr[href] $('.container').on('scroll', icinga.events.onContainerScroll);
*/
linkClicked: function (event) {
var icinga = event.data.self.icinga;
var $a = $(this); // We want to catch each link click
var href = $a.attr('href'); $(document).on('click', 'a', { self: this }, this.linkClicked);
var $li;
var targetId;
if ($a.attr('target') === '_blank') {
return true;
}
event.stopPropagation();
event.preventDefault();
// If link is hash tag... // We treat tr's with a href attribute like links
if (href === '#') { $(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
if ($a.closest('#menu')) {
$li = $a.closest('li');
$('#menu .active').removeClass('active');
$li.addClass('active');
}
return;
}
var $target = $('#col1');
var $container = $a.closest('.container');
if ($container.length) {
$target = $container;
}
if ($a.closest('table.action').length) { // We catch all form submit events
$target = $('#col2'); $(document).on('submit', 'form', { self: this }, this.submitForm);
icinga.events.layout2col();
}
if ($a.closest('[data-base-target]').length) {
targetId = $a.closest('[data-base-target]').data('baseTarget');
$target = $('#' + targetId);
if (targetId === 'col2') {
icinga.events.layout2col();
}
}
if ($a.closest('.tree').length) {
$li = $a.closest('li');
if ($li.find('li').length) {
if ($li.hasClass('collapsed')) {
$li.removeClass('collapsed');
} else {
$li.addClass('collapsed');
$li.find('li').addClass('collapsed');
}
return false;
} else {
$target = $('#col2');
icinga.events.layout2col();
}
}
icinga.loader.loadUrl(href, $target); // We support an 'autosubmit' class on dropdown form elements
event.stopPropagation(); $(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm);
event.preventDefault();
if ($a.closest('#menu').length) { $(window).on('popstate', { self: this }, this.historyChanged);
icinga.events.layout1col();
return false;
}
if ($a.closest('table.action').length) { // TBD: a global autocompletion handler
if ($('#layout').hasClass('twocols')) { // $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
if ($target.attr('id') === 'col2') { // $(document).on('change', 'form.auto input', this.formChanged);
return; // $(document).on('change', 'form.auto select', this.submitForm);
} },
icinga.events.layout1col();
} else {
icinga.events.layout2col();
}
return false;
}
},
/* onUnload: function(event)
hrefIsHashtag: function(href) { {
// WARNING: IE gives full URL :( var icinga = event.data.self.icinga;
// Also it doesn't support negativ indexes in substr icinga.logger.info('Unloading Icinga');
return href.substr(href.length - 1, 1) == '#'; icinga.destroy();
}, },
*/
unbindGlobalHandlers: function () { historyChanged: function(event)
$(window).off('resize', this.onWindowResize); {
$(window).off('unload', this.onUnload); var icinga = event.data.self.icinga;
$(window).off('beforeunload', this.onUnload); if (event.originalEvent.state === null) {
$(document).off('scroll', '.container', this.onContainerScroll); icinga.logger.debug('No more history steps available');
$(document).off('click', 'a', this.linkClicked); } else {
$(document).off('click', 'tr[href]', this.linkClicked); icinga.logger.debug(event.originalEvent.state);
$(document).off('submit', 'form', this.submitForm); }
$(document).off('change', 'form select.autosubmit', this.submitForm); icinga.loader.loadUrl(
$(document).off('mouseenter', '.historycolorgrid td', this.historycolorgridHover); document.location.pathname + document.location.search,
$(document).off('mouseenter', '.historycolorgrid td', this.historycolorgidUnhover); $('#col1')
}, ).historyTriggered = true;
},
destroy: function() { /**
// This is gonna be hard, clean up the mess * Our window got resized, let's fix our UI
this.unbindGlobalHandlers(); */
this.icinga = null; onWindowResize: function(event)
{
var icinga = event.data.self.icinga;
icinga.ui.fixControls();
},
/**
* A scroll event happened in one of our containers
*/
onContainerScroll: function(event)
{
// Yet ugly. And PLEASE, not so often
icinga.ui.fixControls();
},
/**
*
*/
submitForm: function (event)
{
var icinga = event.data.self.icinga;
event.stopPropagation();
event.preventDefault();
// .closest is not required unless subelements to trigger this
var $form = $(event.currentTarget).closest('form');
var url = $form.attr('action');
var method = $form.attr('method');
var data = $form.serializeArray();
// TODO: Check button
data.push({ name: 'btn_submit', value: 'yesss' });
icinga.logger.debug('Submitting form: ' + method + ' ' + url);
// We should move this to a generic target-finder:
var $target = $form.closest('.container');
if ($target.length == 0) {
$target = $('#body');
} }
};
}(Icinga, jQuery)); icinga.loader.loadUrl(url, $target, data, method);
// TODO: Do we really need to return false with stop/preventDefault?
return false;
},
/**
* Someone clicked a link or tr[href]
*/
linkClicked: function(event)
{
var icinga = event.data.self.icinga;
var $a = $(this);
var href = $a.attr('href');
if ($a.attr('target') === '_blank') {
return true;
}
event.stopPropagation();
event.preventDefault();
if (href === '#') {
if ($a.closest('#menu')) {
var $li = $a.closest('li');
$li.siblings('li.active').removeClass('active');
$li.addClass('active');
}
return;
}
var $target = $('#col1');
var $container = $a.closest('.container');
if ($container.length) {
$target = $container;
}
// If link is hash tag...
if ($a.closest('table.action').length) {
$target = $('#col2');
$('#layout').addClass('twocols');
icinga.ui.fixControls();
}
if ($a.closest('[data-base-target]').length) {
$target = $('#' + $a.closest('[data-base-target]').data('baseTarget'));
$('#layout').addClass('twocols');
icinga.ui.fixControls();
}
if ($a.closest('.tree').length) {
var $li = $a.closest('li');
if ($li.find('li').length) {
if ($li.hasClass('collapsed')) {
$li.removeClass('collapsed');
} else {
$li.addClass('collapsed');
$li.find('li').addClass('collapsed');
}
return false;
} else {
$target = $('#col2');
$('#layout').addClass('twocols');
icinga.ui.fixControls();
}
}
icinga.loader.loadUrl(href, $target);
event.stopPropagation();
event.preventDefault();
if ($a.closest('#menu').length) {
$('#layout').removeClass('twocols');
$('#col2').html('<ul class="tabs"></ul>');
icinga.ui.fixControls();
return false;
}
if ($a.closest('table').length) {
if ($('#layout').hasClass('twocols')) {
if ($target.attr('id') === 'col2') return;
icinga.logger.debug('Switching to single col');
$('#layout').removeClass('twocols');
icinga.ui.fixControls();
} else {
icinga.logger.debug('Switching to double col');
$('#layout').addClass('twocols');
icinga.ui.fixControls();
}
return false;
}
},
/*
hrefIsHashtag: function(href)
{
// WARNING: IE gives full URL :(
// Also it doesn't support negativ indexes in substr
return href.substr(href.length - 1, 1) == '#';
},
*/
unbindGlobalHandlers: function()
{
$(window).off('popstate', this.historyChanged);
$(window).off('resize', this.onWindowResize);
$(window).off('unload', this.onUnload);
$(window).off('beforeunload', this.onUnload);
$(document).off('scroll', '.container', this.onContainerScroll);
$(document).off('click', 'a', this.linkClicked);
$(document).off('click', 'tr[href]', this.linkClicked);
$(document).off('submit', 'form', this.submitForm);
$(document).off('change', 'form select.autosubmit', this.submitForm);
},
destroy: function() {
// This is gonna be hard, clean up the mess
this.unbindGlobalHandlers();
this.icinga = null;
}
};
}(Icinga));