Anurag Awasthi 3bf4e5c498 ui: configurable branding, keyboard list and hide-able columns through a new config.js file (#3258)
We want to support hiding table columns, specifically in metrics table, through config file so that users can make the relevant bits hidden as per their organization. Current work will support the metrics table but can be extended to any table with minimal work in future.

Config file will take the key of the metrics column from metrics.js file for the sake of minimal changes and simplicity of development.

Problem: The keyboard list in the UI is not consistent across views such as in the instance wizard and in the register template form. There is also no way to custom about url/text and doc title and help URL in the UI.
Root Cause: The list is hardcoded in the UI allowing no centralised configuration.
Solution: Introduce a new config.js file installed at the /usr/share/cloudstackmanagement/webapp/config.js location. The config.js allows configurable keyboard list, about url/text, doc title, and help URL.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2019-05-30 15:32:37 +05:30

489 lines
17 KiB
JavaScript

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
$.extend(cloudStack, {
ui: {
widgets: {} // Defines API methods for UI widgets
},
uiCustom: {}
});
/**
* Generate navigation <li>s
*
* @param args cloudStack data args
*/
var makeNavigation = function(args) {
var $navList = $('<ul>');
var preFilter = cloudStack.sectionPreFilter ?
cloudStack.sectionPreFilter({
context: $.extend(true, {}, args.context, {
sections: $.map(cloudStack.sections, function(value, key) {
return key;
})
})
}) : null;
$.each(args.sections, function(sectionID, args) {
if (preFilter && $.inArray(sectionID, preFilter) == -1) {
if (!(args.preFilter && args.preFilter())) {
return true;
}
}
var $li = $('<li>')
.addClass('navigation-item')
.addClass(sectionID)
.append($('<span>').addClass('icon').html('&nbsp;'))
.append($('<span>').text(_l(args.title)))
.data('cloudStack-section-id', sectionID);
if (args.customIcon) {
$li.addClass('custom-icon').find('span.icon').html('').append(
$('<img>').attr({
src: args.customIcon
})
);
}
if (args.isPlugin && !args.showOnNavigation) {
$li.hide();
}
$li.appendTo($navList);
return true;
});
// Special classes for first and last items
$navList.find('li:first').addClass('first');
$navList.find('li:last').addClass('last');
return $navList;
};
/**
* Create section contents
*
* @param sectionID Section's ID to show
* @param args CloudStack3 configuration
*/
var showSection = function(sectionID, args, $browser) {
var $navItem = $('#navigation').find('li').filter(function() {
return $(this).hasClass(sectionID);
});
var data = args.sections[sectionID];
var isPlugin = data.isPlugin && !data.showOnNavigation;
data.$browser = $browser;
// Reset browser panels
if (!isPlugin) {
$navItem.siblings().removeClass('active');
$navItem.addClass('active');
$browser.cloudBrowser('removeAllPanels');
}
$browser.cloudBrowser('addPanel', {
title: '<span class="section">' + _l(data.title) + '</span>' + '<span class="subsection"></span>',
data: '',
complete: function($panel, $breadcrumb) {
if(!isPlugin) {
$breadcrumb.attr('title', _l(data.title));
}
data.$breadcrumb = $breadcrumb;
// Hide breadcrumb if this is the home section
if (args.home === sectionID) {
$('#breadcrumbs').find('li:first, div.end:last').hide();
}
// Append specified widget to view
if (data.show)
$panel.append(data.show(data));
else if (data.treeView)
$panel.treeView(data, {
context: args.context
});
else
$panel.listView(data, {
context: args.context
});
}
});
return $navItem;
};
// Define page element generation fns
var pageElems = {
header: function(args) {
// Make notification area
var $notificationArea = $('<div>').addClass('button notifications')
.append(
$('<div>').addClass('total')
// Total notifications
.append($('<span>').html(0))
)
.append($('<span>').html(_l('label.notifications')))
.notifications();
// Project switcher
var $viewSwitcher = $('<div>').addClass('button view-switcher')
.append(
// Default View
$('<div>').addClass('select default-view active')
.html(_l('label.default.view'))
.prepend(
$('<span>').addClass('icon').html('&nbsp;')
)
)
.append(
// Project View
$('<div>').addClass('select project-view')
.html(_l('label.project.view'))
.prepend(
$('<span>').addClass('icon').html('&nbsp;')
)
)
.click(function(event) {
var $target = $(event.target);
var $projectSwitcher = $(this);
var $container = $('html body');
var $navDisabled = $(
$.map([
'projects',
'accounts',
'domains',
'system',
'global-settings',
'configuration'
], function(id) {
return '#navigation li.' + id;
}).join(',')
);
if ($target.closest('.select.project-view').length) {
$('#cloudStack3-container').addClass('project-view');
$projectSwitcher.addClass('alt');
$projectSwitcher.find('.select.project-view').addClass('active')
.siblings().removeClass('active');
// Activate project view
$navDisabled.hide();
cloudStack.uiCustom.projects({
$projectSelect: $projectSelect.hide().find('select')
});
} else {
$navDisabled.show();
$('#cloudStack3-container').removeClass('project-view');
$projectSwitcher.removeClass('alt');
$projectSelect.hide();
$projectSwitcher.find('.select.default-view').addClass('active')
.siblings().removeClass('active');
// Put project name in header
$('.select.project-view').html(
'<span class="icon">&nbsp;</span>' + _l('label.project.view')
).attr('title', '');
// Clear out project
cloudStack.context.projects = null;
}
$('#navigation li.dashboard').click();
return false;
});
var $projectSelect = $('<div>').addClass('view-switcher').hide()
.append($('<select>'));
// User status area
var userLabel = args.context.users[0].name ?
args.context.users[0].name : args.context.users[0].login;
var $userInfo = $('<div>').attr({
id: 'user'
}).addClass('button')
.append(
$('<div>').addClass('name').text(
args.context && args.context.users ?
cloudStack.concat(userLabel, 21) : 'Invalid User'
)
)
.append(
$('<div>').addClass('icon options')
.append(
$('<div>').addClass('icon arrow')
)
);
$userInfo.attr('title', userLabel);
return [
$('<div>').addClass('logo'),
$('<div>').addClass('controls')
.append($notificationArea)
.append($viewSwitcher)
.append($projectSelect)
.append($userInfo)
];
},
'main-area': function(args) {
var $navigation = $('<div>').attr({
id: 'navigation'
});
var $browser = $('<div>').attr({
id: 'browser'
})
.append(
// Home breadcrumb
$('<div>').attr({
id: 'breadcrumbs'
})
.append($('<div>').addClass('home').text(_l('label.home')))
.append($('<div>').addClass('end'))
)
.append(
// Panel container
$('<div>').addClass('container')
);
makeNavigation(args).appendTo($navigation);
return [
$navigation, $browser
];
}
};
$.fn.cloudStack = function(args) {
var $container = $('<div>')
.attr({
id: 'container',
'cloudStack-container': true
})
.data('cloudStack-args', args)
.appendTo(this);
var context = args.context;
// Cleanup login
$('.login').remove();
// Create pageElems
$.each(pageElems, function(id, fn) {
var $elem = $('<div>').attr({
id: id
});
$(fn(args)).each(function() {
$elem.append($(this));
});
$elem.appendTo($container);
});
// User options
var $options = $('<div>').attr({
id: 'user-options'
})
.appendTo($('#user'));
$(['label.logout', 'label.help', 'label.about']).each(function() {
var $link = $('<a>')
.attr({
href: '#'
})
.text(_l(this.toString()))
.appendTo($options);
if (this == 'label.help') {
$link.addClass('help').click(function() {
window.open(cloudStackOptions.helpURL, '_blank');
return false;
});
}
if (this == 'label.about') {
$link.addClass('about').click(function() {
var $logo = $('<div>').addClass('logo').text(_l(cloudStackOptions.aboutText)),
$version = $('<div>').addClass('version').text(_l(g_cloudstackversion)),
$about = $('<div>').addClass('about').append($logo).append($version);
var $aboutDialog = $about.dialog({
modal: true,
width: 300,
title: _l(cloudStackOptions.aboutTitle),
closeOnEscape: false,
dialogClass: 'dialog-about',
buttons: {
'Close': function() {
$(this).dialog("close");
$(':ui-dialog, .overlay').remove();
}
}
});
cloudStack.applyDefaultZindexAndOverlayOnJqueryDialogAndRemoveCloseButton($aboutDialog);
return false;
});
}
});
// Initialize browser
$container.find('#browser div.container').cloudBrowser();
$container.find('#navigation li')
.filter(function() {
return $(this).hasClass(args.home);
})
.click();
// Validation
$.extend($.validator.messages, {
required: _l('label.required')
});
$.validator.addMethod(
"disallowSpecialCharacters",
function(value, element) {
return (value.indexOf("<") == -1 && value.indexOf(">") == -1);
},
jQuery.validator.format('message.disallowed.characters')
);
// Check for pending project invitations
if (args.projects) {
args.projects.invitationCheck({
context: cloudStack.context,
response: {
success: function(args) {
if (!args.data.length) return;
var projectList = $.map(args.data, function(invitation) {
return '<li>' + invitation.project + '</li>';
}).join('');
cloudStack.dialog.notice({
message: _l('message.pending.projects.1') + '<ul>' + projectList + '</ul>' + '<p>' + _l('message.pending.projects.2') + '</p>'
});
}
}
});
}
// Hide logo conditionally
if (!args.hasLogo) $('#header, #header .controls').addClass('nologo');
$(window).trigger('cloudStack.ready');
return this;
};
// Events
$(function() {
// Check if target should be hovered
function checkHoveredLabel($target) {
var $multiWizard = $('div.ui-dialog div.multi-wizard');
if (($target.is('label[for]') && !$target.parents('body.login')) ||
($multiWizard.length &&
($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').length) ||
($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').length)
))
return true;
return false;
}
// Rollover behavior for user options
$(document).bind('mouseover', function(event) {
var $target = $(event.target);
if (checkHoveredLabel($target)) {
$target.addClass('label-hovered');
}
if ($target.closest('#user, #user-options').length) {
return false;
} else $('#user-options').hide();
return false;
});
$(document).bind('mouseout', function(event) {
var $target = $(event.target);
if (checkHoveredLabel($target)) {
$target.removeClass('label-hovered');
}
});
$(document).bind('click', function(event) {
var $target = $(event.target);
var $container = $target.closest('[cloudStack-container]');
var args = $container.data('cloudStack-args');
var $browser = $container.find('#browser .container');
var $multiWizard = $('div.ui-dialog div.multi-wizard');
// Wizard: trigger click event for input when click it label
if ($multiWizard.length) {
if ($target.is('.multi-wizard label') && $target.prev('input[type="radio"],input[type="checkbox"]').length) {
$target.prev('input').trigger('click');
}
if ($target.is('.multi-wizard .select-desc div.name') && $target.parent('div.select-desc').prev('input[type="radio"],input[type="checkbox"]').length) {
$target.parent('div.select-desc').prev('input').trigger('click');
}
}
if (!$container.length) return true;
// Navigation items
if ($target.closest('li.navigation-item').length && $target.closest('#navigation').length) {
var $navItem = $target.closest('li.navigation-item');
if ($navItem.is('.disabled')) return false;
showSection($navItem.data('cloudStack-section-id'), args, $browser);
return false;
}
// Browser expand
if ($target.hasClass('control expand') && $target.closest('div.panel div.toolbar').length) {
$browser.cloudBrowser('toggleMaximizePanel', {
panel: $target.closest('div.panel')
});
return false;
}
// Home breadcrumb
if ($target.is('#breadcrumbs div.home')) {
showSection(args.home, args, $browser);
return false;
}
// User options
if ($target.closest('#user div.icon.options').length) {
$('#user-options').toggle();
return false;
}
return true;
});
});
})(window.jQuery,
window.cloudStack ? window.cloudStack : window.cloudStack = {});