diff options
Diffstat (limited to 'install/ui/details.js')
-rw-r--r-- | install/ui/details.js | 897 |
1 files changed, 897 insertions, 0 deletions
diff --git a/install/ui/details.js b/install/ui/details.js new file mode 100644 index 000000000..aad77a9c4 --- /dev/null +++ b/install/ui/details.js @@ -0,0 +1,897 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Adam Young <ayoung@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* IPA Object Details - populating definiton lists from entry data */ + +/* REQUIRES: ipa.js */ + +IPA.expand_icon = 'ui-icon-minus'; +IPA.collapse_icon = 'ui-icon-plus'; + +IPA.is_field_writable = function(rights){ + if (!rights){ + alert('no right'); + } + return rights.indexOf('w') > -1; +}; + +IPA.details_field = function (spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.load = spec.load || load; + that.save = spec.save || save; + + function load(record) { + that.record = record; + that.values = record[that.name]; + that.reset(); + } + + that.update = function() { + + if (!that.record) return; + + /* remove all <dd> tags i.e. all attribute values */ + $('dd', that.container).remove(); + + var multivalue = false; + var hint_span = null; + var dd; + + var param_info = IPA.get_param_info(that.entity_name, that.name); + if (param_info) { + if (param_info['multivalue'] || param_info['class'] == 'List') + multivalue = true; + var hint = param_info['doc']; + if (hint){ + hint_span = $('<span />',{ + 'class': 'attrhint', + 'html': 'Hint: ' + hint}); + } + } + + var rights = 'rsc'; + + if (that.record.attributelevelrights){ + rights = that.record.attributelevelrights[this.name] || rights ; + } + + if (that.values) { + /* + Too much logic currently assumes an array. + This is true everywhere but ACIs. */ + + if (!(that.values instanceof Array)){ + that.values = [that.values]; + } + + dd = IPA.create_first_dd(that.name); + dd.append(that.create_value(that.values[0], hint_span, rights, 0)); + dd.appendTo(that.container); + + for (var i = 1; i < that.values.length; ++i) { + dd = IPA.create_other_dd(that.name); + dd.append(that.create_value(that.values[i], hint_span, rights, i)); + dd.appendTo(that.container); + } + + if (multivalue && IPA.is_field_writable(rights) ) { + dd = IPA.create_other_dd(that.name); + dd.append(IPA.details_field_create_add_link.call(that, that.name, rights, that.values.length)); + dd.appendTo(that.container); + } + + } else { + if (multivalue && IPA.is_field_writable(rights)) { + dd = IPA.create_first_dd(that.name); + dd.append(IPA.details_field_create_add_link.call(that, that.name, rights, 0)); + dd.appendTo(that.container); + + } else { + dd = IPA.create_first_dd(that.name); + dd.append(that.create_value('', hint_span, rights, 0)); + dd.appendTo(that.container); + } + } + }; + + /* create an HTML element for displaying/editing an attribute + * arguments: + * attr - LDAP attribute name + * value - the attributes value */ + that.create_value = function(value, hint, rights, index) { + + // if field is primary key or non-writable, return a label + + var label = $('<label/>', { html:value.toString() }); + + if (!IPA.is_field_writable(rights)) return label; + + var param_info = IPA.get_param_info(that.entity_name, that.name); + if (param_info) { + if (param_info['primary_key']) return label; + if ('no_update' in param_info['flags']) return label; + } + + // otherwise, create input field + + var input = that.create_input(value, param_info, rights, index); + if (param_info) { + if (param_info['multivalue'] || param_info['class'] == 'List') { + input.append(_ipa_create_remove_link(that.name, param_info)); + } + } + + if (hint) input.after(hint); + + return input; + }; + + /* creates a input box for editing a string attribute */ + that.create_input = function(value, param_info, rights, index) { + + index = index || 0; + + function validate_input(text, param_info, error_link) { + if (param_info && param_info.pattern) { + var regex = new RegExp( param_info.pattern ); + if (!text.match(regex)) { + error_link.style.display = "block"; + if (param_info.pattern_errmsg) { + error_link.innerHTML = param_info.pattern_errmsg; + } + } else { + error_link.style.display = "none"; + } + } + } + + var doc = that.name; + if (param_info && param_info.doc) { + doc = param_info.doc; + } + var span = $("<Span />"); + var input = $("<input/>", { + type: "text", + name: that.name, + value: value.toString(), + title: doc, + keyup: function(){ + var undo_link = this.nextElementSibling; + undo_link.style.display = "inline"; + var error_link = undo_link.nextElementSibling; + + var text = $(this).val(); + validate_input(text, param_info,error_link); + } + }).appendTo(span) ; + + if (!IPA.is_field_writable(rights)) { + input.attr('disabled', 'disabled'); + } + + span.append($("<a/>", { + html:"undo", + "class":"ui-state-highlight ui-corner-all undo", + style:"display:none", + click: function(){ + var previous_value = that.values || ''; + if (index >= previous_value.length){ + previous_value = ''; + }else{ + previous_value= previous_value[index]; + } + + this.previousElementSibling.value = previous_value; + this.style.display = "none"; + var error_link = this.nextElementSibling; + validate_input(previous_value, param_info,error_link); + } + })); + span.append($("<span/>", { + html:"Does not match pattern", + "class":"ui-state-error ui-corner-all", + style:"display:none" + })); + return span; + }; + + function save() { + var values = []; + + $('dd', that.container).each(function () { + + var input = $('input', $(this)); + if (!input.length) return; + + if (input.is('.strikethrough')) return; + + var value = $.trim(input.val()); + if (!value) value = ''; + + values.push(value); + }); + + return values; + } + + return that; +}; + + +IPA.details_section = function (spec){ + + spec = spec || {}; + + var that = {}; + + that.name = spec.name || ''; + that.label = spec.label || ''; + that.template = spec.template; + that._entity_name = spec.entity_name; + + that.fields = []; + that.fields_by_name = {}; + + that.__defineGetter__("entity_name", function(){ + return that._entity_name; + }); + + that.__defineSetter__("entity_name", function(entity_name){ + that._entity_name = entity_name; + + for (var i=0; i<that.fields.length; i++) { + that.fields[i].entity_name = entity_name; + } + }); + + that.get_field = function(name) { + return that.fields_by_name[name]; + }; + + that.add_field = function(field) { + field.entity_name = that.entity_name; + that.fields.push(field); + that.fields_by_name[field.name] = field; + return field; + }; + + that.create_field = function(spec) { + + //TODO: replace IPA.details_field with class-specific implementation + //Valid field classes: Str, IA5Str, Int, Bool and List + var field = IPA.details_field(spec); + that.add_field(field); + return field; + }; + + that.create_text = function(spec) { + var field = IPA.text_widget(spec); + that.add_field(field); + return field; + }; + + that.create_radio = function(spec) { + var field = IPA.radio_widget(spec); + that.add_field(field); + return field; + }; + + that.create_textarea = function(spec) { + var field = IPA.textarea_widget(spec); + that.add_field(field); + return field; + }; + + that.create_button = function(spec) { + var field = IPA.button_widget(spec); + that.add_field(field); + return field; + }; + + that.init = function() { + for (var i=0; i<that.fields.length; i++) { + var field = that.fields[i]; + field.init(); + } + }; + + that.create = function(container) { + + if (that.template) return; + + var fields = that.fields; + for (var i = 0; i < fields.length; ++i) { + var field = fields[i]; + + var span = $('<span/>', { 'name': field.name }).appendTo(container); + field.create(span); + } + }; + + that.setup = function(container) { + + that.container = container; + + if (that.template) return; + + var fields = that.fields; + for (var i = 0; i < fields.length; ++i) { + var field = fields[i]; + + var span = $('span[name='+field.name+']', this.container).first(); + field.setup(span); + } + }; + + that.load = function(record) { + + var fields = that.fields; + + if (that.template) { + var template = IPA.get_template(that.template); + this.container.load( + template, + function(data, text_status, xhr) { + for (var i = 0; i < fields.length; ++i) { + var field = fields[i]; + var span = $('span[name='+field.name+']', this.container).first(); + field.setup(span); + field.load(record); + } + } + ); + return; + } + + for (var j=0; j<fields.length; j++) { + var field = fields[j]; + var span = $('span[name='+field.name+']', this.container).first(); + field.load(record); + } + }; + + that.reset = function() { + for (var i=0; i<that.fields.length; i++) { + var field = that.fields[i]; + var span = $('span[name='+field.name+']', this.container).first(); + field.reset(); + } + }; + + // methods that should be invoked by subclasses + that.section_init = that.init; + that.section_create = that.create; + that.section_setup = that.setup; + that.section_load = that.load; + + return that; +}; + + +/** + * This class creates a details section formatted as a list of + * attributes names and values. The list is defined using <dl> tag. + * The attribute name is defined inside a <dt> tag. The attribute + * value is defined using a <dd> tag inside a <span> tag. If the + * attribute has multiple values the content inside <span> will + * be duplicated to display each value. + * + * Example: + * <dl class="entryattrs"> + * + * <dt title="givenname">First Name:</dt> + * <span name="givenname"> + * <dd><input type="text" size="20"/></dd> + * </span> + * + * <dt title="telephonenumber">Telephone Number:</dt> + * <span name="telephonenumber"> + * <dd><input type="text" size="20"/></dd> + * <dd><input type="text" size="20"/></dd> + * </span> + * + * </dl> + */ +IPA.details_list_section = function (spec){ + + spec = spec || {}; + + var that = IPA.details_section(spec); + + that.create = function(container) { + + // do not call section_create() here + + if (that.template) return; + + var dl = $('<dl/>', { + 'id': that.name, + 'class': 'entryattrs' + }).appendTo(container); + + var fields = that.fields; + for (var i = 0; i < fields.length; ++i) { + var field = fields[i]; + + var label = field.label; + + // no need to get i18n label from metadata + // because it's already done by field.init() + + if (label !== '') { + label += ':'; + } + + $('<dt/>', { + html: label + }).appendTo(dl); + + var span = $('<span/>', { 'name': field.name }).appendTo(dl); + field.create(span); + } + }; + + return that; +}; + + +/* shorthand notation used for declarative definitions of details pages */ +IPA.stanza = function (spec) { + + spec = spec || {}; + + var that = IPA.details_list_section(spec); + + // This is to allow declarative style programming for details + that.input = function(spec) { + that.create_field(spec); + return that; + }; + + that.custom_input = function(input) { + that.add_field(input); + return that; + }; + + return that; +}; + + +IPA.details_facet = function (spec) { + + spec = spec || {}; + + var that = IPA.facet(spec); + + that.label = ( IPA.messages && IPA.messages.facets && IPA.messages.facets.details) || spec.label; + that.is_dirty = spec.is_dirty || is_dirty; + that.create = spec.create || create; + that.setup = spec.setup || setup; + that.load = spec.load || load; + that.update = spec.update || IPA.details_update; + that.reset = spec.reset || reset; + that.refresh = spec.refresh || IPA.details_refresh; + + that.sections = []; + that.sections_by_name = {}; + + that.__defineGetter__("entity_name", function(){ + return that._entity_name; + }); + + that.__defineSetter__("entity_name", function(entity_name){ + that._entity_name = entity_name; + + for (var i=0; i<that.sections.length; i++) { + that.sections[i].entity_name = entity_name; + } + }); + + that.get_section = function(name) { + return that.sections_by_name[name]; + }; + + that.add_section = function(section) { + section.entity_name = that.entity_name; + that.sections.push(section); + that.sections_by_name[section.name] = section; + return section; + }; + + that.create_section = function(spec) { + var section = IPA.details_section(spec); + that.add_section(section); + return section; + }; + + that.init = function() { + for (var i=0; i<that.sections.length; i++) { + var section = that.sections[i]; + section.init(); + } + }; + + that.get_primary_key = function() { + var pkey_name = IPA.metadata[that.entity_name].primary_key; + if (that.record[pkey_name] instanceof Array){ + return that.record[pkey_name][0]; + }else{ + return that.record[pkey_name]; + } + }; + + that.get_section_header_prefix = function(visible) { + if (visible) { + return '<span class="ui-icon '+ + IPA.collapse_icon + + ' section-expand" ></span>'; + } else { + return '<span class="ui-icon '+ + IPA.expand_icon + + ' section-expand" />'; + } + }; + + function create(container) { + + container.attr('title', that.entity_name); + + $('<h1/>',{ + html: "<span id='headerpkey' />"+that.entity_name + ' Settings' + }).append(IPA.create_network_spinner()). + appendTo(container); + + var details = $('<div/>', { + 'class': 'content' + }).appendTo(container); + + var action_panel = that.get_action_panel(); + + var ul = $('ul', action_panel); + var buttons = $('.action-controls',action_panel); + + $('<input/>', { + 'type': 'text', + 'name': 'reset' + }).appendTo(buttons); + + $('<input/>', { + 'type': 'text', + 'name': 'update' + }).appendTo(buttons); + + + for (var i = 0; i < that.sections.length; ++i) { + var section = that.sections[i]; + + $('<h2/>', { + name: section.name, + title: section.label, + html: that.get_section_header_prefix(true) + ' ' + section.label + }).appendTo(details); + + var div = $('<div/>', { + 'id': that.entity_name+'-'+that.name+'-'+section.name, + 'class': 'details-section' + }).appendTo(details); + + section.create(div); + + details.append('<hr/>'); + } + } + + function setup(container) { + + that.facet_setup(container); + + var button = $('input[name=reset]', that.container); + that.reset_button = IPA.action_button({ + 'label': 'Reset', + 'icon': 'ui-icon-refresh', + 'class': 'details-reset', + 'click': function() { + that.reset(); + return false; + } + }); + button.replaceWith(that.reset_button); + + button = $('input[name=update]', that.container); + that.update_button = IPA.action_button({ + 'label': 'Update', + 'icon': 'ui-icon-check', + 'class': 'details-update', + 'click': function() { + that.update(); + return false; + } + }); + button.replaceWith(that.update_button); + + for (var i = 0; i < that.sections.length; ++i) { + var section = that.sections[i]; + + var header = $('h2[name='+section.name+']', that.container); + + var div = $('#'+that.entity_name+'-'+that.name+'-'+section.name, + that.container); + + header.click(function(section, header, div) { + return function() { + var visible = div.is(":visible"); + header.html(that.get_section_header_prefix(!visible) + ' ' + section.label); + div.slideToggle(); + }; + }(section, header, div)); + + section.setup(div); + } + } + + function is_dirty() { + var pkey = $.bbq.getState(that.entity_name + '-pkey', true) || ''; + return pkey != that.pkey; + } + + function load(record) { + that.record = record; + for (var i=0; i<that.sections.length; i++) { + var section = that.sections[i]; + section.load(record); + } + if (that.pkey){ + $('h1 #headerpkey',that.container).html(that.pkey+": "); + } + } + + function reset() { + + for (var i=0; i<that.sections.length; i++) { + var section = that.sections[i]; + section.reset(); + } + } + + that.details_facet_init = that.init; + that.details_facet_create = that.create; + that.details_facet_load = that.load; + + return that; +}; + +IPA.action_button = function(spec) { + var button = IPA.button(spec); + button.removeClass("ui-state-default").addClass("action-button"); + return button; +}; + +IPA.button = function(spec) { + + spec = spec || {}; + + var button = $('<a/>', { + id: spec.id, + html: spec.label, + title: spec.title || spec.label, + 'class': 'ui-state-default ui-corner-all' + }); + + if (spec.click) button.click(spec.click); + if (spec['class']) button.addClass(spec['class']); + + if (spec.icon) { + button.addClass('input_link'); + button.append('<span class="ui-icon '+spec.icon+'" ></span> '); + } else { + button.addClass('button-without-icon'); + } + + return button; +}; + +IPA.details_refresh = function () { + + var that = this; + + that.pkey = $.bbq.getState(that.entity_name + '-pkey', true) || ''; + + function on_success(data, text_status, xhr) { + that.load(data.result.result); + } + + function on_failure(xhr, text_status, error_thrown) { + var details = $('.details', that.container).empty(); + details.append('<p>Error: '+error_thrown.name+'</p>'); + details.append('<p>'+error_thrown.title+'</p>'); + details.append('<p>'+error_thrown.message+'</p>'); + } + + var params = []; + if (that.pkey) params.push(that.pkey); + + IPA.cmd( 'show', params, {all: true, rights: true}, on_success, on_failure, + that.entity_name ); +}; + +IPA.details_update = function (on_win, on_fail) +{ + var that = this; + var entity_name = that.entity_name; + + function update_on_win(data, text_status, xhr) { + if (on_win) + on_win(data, text_status, xhr); + if (data.error) + return; + + var result = data.result.result; + that.load(result); + } + + function update_on_fail(xhr, text_status, error_thrown) { + if (on_fail) + on_fail(xhr, text_status, error_thrown); + } + + var values; + var modlist = {'all': true, 'setattr': [], 'addattr': [], 'rights': true}; + var attrs_wo_option = {}; + + for (var i=0; i<that.sections.length; i++) { + var section = that.sections[i]; + + if (section.save){ + section.save(modlist); + continue; + } + + var div = $('#'+that.entity_name+'-'+that.name+'-'+section.name, that.container); + + for (var j=0; j<section.fields.length; j++) { + var field = section.fields[j]; + + var span = $('span[name='+field.name+']', div).first(); + values = field.save(); + if (!values) continue; + + var param_info = IPA.get_param_info(entity_name, field.name); + if (param_info) { + if (param_info['primary_key']) continue; + if (values.length === 1) { + modlist[field.name] = values[0]; + }else if (values.length > 1){ + modlist[field.name] = values; + } else if (param_info['multivalue']){ + modlist[field.name] = []; + } + } else { + if (values.length) attrs_wo_option[field.name] = values; + } + } + } + + for (var attr in attrs_wo_option) { + values = attrs_wo_option[attr]; + modlist['setattr'].push(attr + '=' + values[0]); + for (var k = 1; k < values.length; ++k){ + modlist['addattr'].push(attr + '=' + values[k]); + } + } + + var pkey = that.get_primary_key() ; + if (pkey){ + pkey = [pkey]; + }else{ + pkey = []; + } + + IPA.cmd('mod', pkey, modlist, update_on_win, null, entity_name); +}; + + +IPA.create_first_dd = function (field_name, content){ + var dd = $('<dd/>', { + 'class': 'first', + 'title': field_name + }); + if (content) dd.append(content); + return dd; +}; + +IPA.create_other_dd = function (field_name, content){ + return $('<dd/>', { + 'class': 'other', + 'title': field_name + }).append(content); +}; + + +/* creates a Remove link for deleting attribute values */ +function _ipa_create_remove_link(attr, param_info) +{ + if (param_info){ + /* check if the param is required or of the Password type + * if it is, then we don't want people to be able to remove it */ + if ((param_info['required']) || (param_info['class'] == 'Password')){ + return (''); + } + } + return $('<a/>',{ + href:"jslink", + click: function (){return (_ipa_remove_on_click(this));}, + title: attr, + text: 'Remove'}); + +} + +IPA.details_field_create_add_link = function (title, rights, index) { + + var that = this; + + var link = $('<a/>', { + 'href': 'jslink', + 'title': title, + 'html': 'Add', + 'click': function () { + + var param_info = IPA.get_param_info(that.entity_name, ''); + var input = that.create_input('', param_info, rights, index); + + link.replaceWith(input); + input.focus(); + + var dd = IPA.create_other_dd(that.name); + dd.append(IPA.details_field_create_add_link.call(that, that.name, rights, index+1)); + dd.appendTo(that.container); + + return false; + } + }); + + return link; +}; + + +function _ipa_remove_on_click(obj) +{ + var jobj = $(obj); + var attr = jobj.attr('title'); + var par = jobj.parent(); + + var input = par.find('input'); + + if (input.is('.strikethrough')){ + input.removeClass('strikethrough'); + jobj.text("Remove"); + }else{ + input.addClass('strikethrough'); + jobj.text("Undo"); + } + return (false); +} |