As already mentioned in my other blog posts, NetEye 4 is based on Icinga Web 2, a powerful PHP framework.
During the development of some of our custom NetEye modules for Icinga Web 2, we needed to include some new, custom jQuery plugins to create a neat new effect in the GUI. For example, with the 4.2 release we introduced a multiselect component, switching from the basic HTML version to our new, easier to use and better looking one.

Our custom multiselect box is based on a jQuery plugin called Multiselect (you can download it from https://crlcu.github.io/multiselect/). We then added some custom jQuery code and a customized theme to better integrate this component with our NetEye 4 look-and-feel.
Our goal was to reuse the structure and style for each multiselect across the entire NetEye web interface. In other words, we also wanted to overwrite some Icinga Web 2 multiselects outside our custom modules. The jQuery libraries are automatically loaded in each section of Icinga Web 2, so the key point here was including a global CSS customization that would apply the desired style everywhere.
A short guide that describes how to reproduce this style follows.
vendor directory inside your module’s public/js folderconfiguration.php file of your module and insert this line:
$this->provideJsFile('vendor/multiselect.js');
function attachMultiselect() {
jQuery('div.control-group select[multiple="multiple"]').each(function (i, select) {
var controlGroup = jQuery(select).parent();
var row = controlGroup.parent();
// Check if the element has already been processed (it should have class .row)
if (!row.hasClass('row')) {
jQuery(select).hide(); // Hide the default select in order to show the better looking one
// get some information by the defaul zend-form element
var selectOrigId = jQuery(select).attr('id');
var selectOrigSortable = jQuery(select).data('sortable'); // sortable is the attribute we choose for adding the sort buttons in the plugin
var newSelectFromId = selectOrigId + '_from';
var newSelectToId = selectOrigId + '_to';
var sortableBtns = '';
if(selectOrigSortable == '1'){
sortableBtns = '<button type="button" id="' + newSelectFromId + '_move_up" class="btn btn-block"><i class="icon-up-dir"></i></button>'
+ '<button type="button" id="' + newSelectFromId + '_move_down" class="btn btn-block"><i class="icon-down-dir"></i></button>';
}
// Create the new HTML structure for the element
var fromSelect = '<select id="' + newSelectFromId + '" class="form-control" size="8" multiple="multiple">'
+ notSelectedOptions
+ '</select>';
var customHtml = '<div class="col-xs-2">'
+ '<button type="button" id="' + newSelectFromId + '_rightSelected" class="btn btn-block"><i class="icon-right-dir"></i></button>'
+ '<button type="button" id="' + newSelectFromId + '_leftSelected" class="btn btn-block"><i class="icon-left-dir"></i></button>'
+ sortableBtns
+ '</div>'
+ '<div class="without-label col-xs-5">'
+ '<select id="' + newSelectToId + '" data-orig-select-id="' + selectOrigId + '" class="form-control" size="8" multiple="multiple">'
+ selectedOptions
+ '</select>'
+ '</div>';
jQuery(select).addClass('form-control');
controlGroup.wrap('<div class=\'row\'></div>');
controlGroup.addClass('col-xs-5');
controlGroup.append(fromSelect);
controlGroup.after(customHtml);
updateValue(jQuery('#' + newSelectToId));
// trigger the value update on button click
jQuery('#' + newSelectFromId).multiselect({
right: '#' + newSelectToId,
sort: false,
afterMoveToRight: function () {
updateValue(jQuery('#' + newSelectToId));
triggerChange(select);
},
afterMoveToLeft: function () {
updateValue(jQuery('#' + newSelectToId));
triggerChange(select);
},
afterMoveUp: function () {
updateValue(jQuery('#' + newSelectToId));
triggerChange(select);
},
afterMoveDown: function () {
updateValue(jQuery('#' + newSelectToId));
triggerChange(select);
}
});
}
});
}
configuration.php file:
$this->provideJsFile('custom-multiselect.js');
#layout .row {
margin-right: -15px;
margin-left: -15px;
.col-xs-5 {
float: left;
text-align: center;
width: 41.66666667%;
&.without-label {
margin-top: 30px;
}
.control-label-group {
padding-left: 2em;
text-align: left;
}
select[multiple] {
height: 150px !important;
border: 1px solid #DDDDDD;
width: 90%;
}
}
.col-xs-2 {
float: left;
margin-top: 30px;
text-align: center;
width: 16.66666667%;
.btn-block {
background-color: #bfe0e0;
border: 2px solid #fff;
color: #4a9e9a;
cursor: pointer;
display: block;
width: 50%;
padding: 6px 12px;
margin: 0 auto;
margin-top: 4px;
line-height: normal;
text-align: center;
}
}
}
configuration.php by using the function:
$this->provideGlobalCssFile('multiselect.less');In this way your theme will be used as the default for all modules and even for Icinga Web 2 itself.