diff options
author | Endi S. Dewata <edewata@redhat.com> | 2011-02-02 16:18:35 -0600 |
---|---|---|
committer | Adam Young <ayoung@redhat.com> | 2011-02-03 20:09:15 -0500 |
commit | bd493d47a736fab4e74efbe9b603dcc8f1986512 (patch) | |
tree | 00f21fb46a747c8ef3219773b2d669c9b1d76f7f | |
parent | ff646ec3a428647cac8fa9304a5695c7f29456a2 (diff) | |
download | freeipa-bd493d47a736fab4e74efbe9b603dcc8f1986512.tar.gz freeipa-bd493d47a736fab4e74efbe9b603dcc8f1986512.tar.xz freeipa-bd493d47a736fab4e74efbe9b603dcc8f1986512.zip |
Added multi-valued text widget.
A multi-valued text widget has been created to replace the old
IPA.details_field. The old code was designed to handle all data
types, and it uses one <dd> tag for each value, so the code is
still incomplete and complex. The new code was designed to handle
only multi-valued text attributes, and it uses one <dd> tag for
all values, so it's easier to maintain. There are already other
widgets that can be used to handle other data types.
The new code supports line-level undo and line-out for removal
like the old code, but there are some changes:
- Undoing a newly added line will remove the entire line.
- Editing the value of a removed line will cancel the removal.
- It provides 'undo all' link to reset the entire attribute.
The old code will be cleaned up in a subsequent patch.
-rw-r--r-- | install/ui/details.js | 15 | ||||
-rw-r--r-- | install/ui/ipa.css | 4 | ||||
-rw-r--r-- | install/ui/test/details_tests.js | 46 | ||||
-rw-r--r-- | install/ui/test/widget_tests.js | 34 | ||||
-rw-r--r-- | install/ui/user.js | 10 | ||||
-rw-r--r-- | install/ui/widget.js | 302 |
6 files changed, 360 insertions, 51 deletions
diff --git a/install/ui/details.js b/install/ui/details.js index 125ef1bc8..5872e81cf 100644 --- a/install/ui/details.js +++ b/install/ui/details.js @@ -333,6 +333,12 @@ IPA.details_section = function (spec){ return that; }; + that.multivalued_text = function(spec) { + var field = IPA.multivalued_text_widget(spec); + that.add_field(field); + return that; + }; + that.create_field = function(spec) { //TODO: replace IPA.details_field with class-specific implementation @@ -510,17 +516,14 @@ IPA.details_list_section = function (spec){ for (var i = 0; i < fields.length; ++i) { var field = fields[i]; - var label = field.label; + 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 + html: label+':', + title: label }).appendTo(dl); var span = $('<span/>', { 'name': field.name }).appendTo(dl); diff --git a/install/ui/ipa.css b/install/ui/ipa.css index 1f34b3ce4..c851a400e 100644 --- a/install/ui/ipa.css +++ b/install/ui/ipa.css @@ -246,7 +246,7 @@ dl.entryattrs dd { dl.entryattrs dd.first { margin-left: 0; margin-top: 0.5em; - font-weight: bold; + font-weight: bold; } dl.entryattrs dd.other { @@ -256,7 +256,7 @@ dl.entryattrs dd.other { dl.entryattrs input { margin-right: 0.5em; - margin-top: -1.2em; + margin-bottom: 1em; } dl.entryattrs input.otp { diff --git a/install/ui/test/details_tests.js b/install/ui/test/details_tests.js index fb33808c7..cf4de0825 100644 --- a/install/ui/test/details_tests.js +++ b/install/ui/test/details_tests.js @@ -21,12 +21,24 @@ module('details', { setup: function() { + IPA.ajax_options.async = false; + + IPA.init( + "data", + true, + function(data, text_status, xhr) { + }, + function(xhr, text_status, error_thrown) { + ok(false, "ipa_init() failed: "+error_thrown); + } + ); + var obj_name = 'user'; IPA.entity_factories.user= function(){ return IPA.entity({name:obj_name}); }; - IPA.start_entities(); + IPA.start_entities(); }, teardown: function() { } @@ -35,24 +47,13 @@ module('details', { test("Testing IPA.details_section.create().", function() { - IPA.ajax_options.async = false; - - IPA.init( - "data", - true, - function(data, text_status, xhr) { - ok(true, "ipa_init() succeeded."); - }, - function(xhr, text_status, error_thrown) { - ok(false, "ipa_init() failed: "+error_thrown); - } - ); - var section = IPA.stanza({name:'IDIDID', label:'NAMENAMENAME'}). input({name:'cn'}). - input({name:'description'}). - input({name:'number'}); + input({name:'uid'}). + input({name:'mail'}); + section.entity_name = 'user'; + section.init(); var fields = section.fields; var container = $("<div/>"); @@ -105,19 +106,6 @@ test("Testing IPA.details_section.create().", function() { test("Testing details lifecycle: create, setup, load.", function(){ - IPA.ajax_options.async = false; - - IPA.init( - "data", - true, - function(data, text_status, xhr) { - ok(true, "ipa_init() succeeded."); - }, - function(xhr, text_status, error_thrown) { - ok(false, "ipa_init() failed: "+error_thrown); - } - ); - var result = {}; IPA.cmd( diff --git a/install/ui/test/widget_tests.js b/install/ui/test/widget_tests.js index 3d827cdb8..f4281e38f 100644 --- a/install/ui/test/widget_tests.js +++ b/install/ui/test/widget_tests.js @@ -67,7 +67,7 @@ function base_widget_test(widget,entity_name, value){ } -function widget_string_test(widget, value){ +function widget_string_test(widget) { var value = 'test_title'; var mock_record = {'title': value}; @@ -117,6 +117,31 @@ function text_tests(widget,input){ } +function multivalued_text_tests(widget) { + + var values = ['val1', 'val2', 'val3']; + + var record = {}; + record[widget.name] = values; + + widget.load(record); + + same(widget.save(), values, "All values loaded"); + same(widget.is_dirty(), false, "Field initially clean"); + + values = ['val1', 'val2', 'val3', 'val4']; + widget.add_row('val4'); + + same(widget.save(), values, "Value added"); + same(widget.is_dirty(), true, "Field is dirty"); + + values = ['val1', 'val3', 'val4']; + widget.remove_row(1); + + same(widget.save(), values, "Value removed"); + same(widget.is_dirty(), true, "Field is dirty"); +} + test("IPA.table_widget" ,function(){ var widget = IPA.table_widget({undo:true,name:'users'}); @@ -191,6 +216,13 @@ test("Testing text widget.", function() { }); +test("Testing multi-valued text widget.", function() { + var widget = IPA.multivalued_text_widget({undo:true,name:'title'}); + base_widget_test(widget,'user','test_value'); + widget_string_test(widget); + multivalued_text_tests(widget); +}); + test("Testing checkbox widget.", function() { var widget = IPA.checkbox_widget({name:'title'}); base_widget_test(widget,'user','test_value'); diff --git a/install/ui/user.js b/install/ui/user.js index 1ce95009f..663aab920 100644 --- a/install/ui/user.js +++ b/install/ui/user.js @@ -64,11 +64,11 @@ IPA.entity_factories.user = function (){ input({name:'homedirectory'})). section( IPA.stanza({name: 'contact', label: IPA.messages.details.contact}). - input({name:'mail'}). - input({name:'telephonenumber'}). - input({name:'pager'}). - input({name:'mobile'}). - input({name:'facsimiletelephonenumber'})). + multivalued_text({name:'mail'}). + multivalued_text({name:'telephonenumber'}). + multivalued_text({name:'pager'}). + multivalued_text({name:'mobile'}). + multivalued_text({name:'facsimiletelephonenumber'})). section( IPA.stanza({name: 'mailing', label: IPA.messages.details.mailing}). input({name:'street'}). diff --git a/install/ui/widget.js b/install/ui/widget.js index 8f3eeb62f..db9d1744e 100644 --- a/install/ui/widget.js +++ b/install/ui/widget.js @@ -2,6 +2,7 @@ /* Authors: * Endi Sukma Dewata <edewata@redhat.com> * Adam Young <ayoung@redhat.com> + * Pavel Zuna <pzuna@redhat.com> * * Copyright (C) 2010 Red Hat * see file 'COPYING' for use and warranty information @@ -34,8 +35,13 @@ IPA.widget = function(spec) { that.tooltip = spec.tooltip; that.disabled = spec.disabled; + + // read_only is set during initialization that.read_only = spec.read_only; + // writable is set during load + that.writable = true; + that._entity_name = spec.entity_name; that.width = spec.width; @@ -80,7 +86,7 @@ IPA.widget = function(spec) { if ( !text || text.match(regex) ) { error_link.css('display', 'none'); that.valid = true; - }else{ + } else { error_link.css('display', 'block'); if (that.param_info.pattern_errmsg) { error_link.html(that.param_info.pattern_errmsg); @@ -127,6 +133,23 @@ IPA.widget = function(spec) { that.values = value ? [value] : []; } + that.writable = true; + + if (that.param_info.primary_key) { + that.writable = false; + } + + if ('no_update' in that.param_info.flags) { + that.writable = false; + } + + if (that.record.attributelevelrights) { + var rights = that.record.attributelevelrights[that.name]; + if (!rights || rights.indexOf('w') < 0) { + that.writable = false; + } + } + that.reset(); } @@ -272,21 +295,21 @@ IPA.text_widget = function(spec) { container.append(' '); - $("<span/>",{ - name:'error_link', - html:"Text does not match field pattern", - "class":"ui-state-error ui-corner-all", - style:"display:none" + $('<span/>', { + name: 'error_link', + html: 'Text does not match field pattern', + 'class': 'ui-state-error ui-corner-all', + style: 'display:none' }).appendTo(container); }; that.setup = function(container) { - this.widget_setup(container); + that.widget_setup(container); var input = $('input[name="'+that.name+'"]', that.container); input.keyup(function() { - if(that.undo){ + if (that.undo) { that.show_undo(); } var value = $(this).val(); @@ -343,6 +366,269 @@ IPA.text_widget = function(spec) { return that; }; +IPA.multivalued_text_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.size = spec.size || 30; + + that.get_undo = function(index) { + if (index === undefined) { + return $('span[name="undo_all"]', that.container); + + } else { + var row = that.get_row(index); + return $('span[name="undo"]', row); + } + }; + + that.show_undo = function(index) { + var undo = that.get_undo(index); + undo.css('display', 'inline'); + }; + + that.hide_undo = function(index) { + var undo = that.get_undo(index); + undo.css('display', 'none'); + }; + + that.create = function(container) { + + var dd = $('<dd/>', { + 'class': 'first' + }).appendTo(container); + + var div = $('<div/>', { + name: 'value' + }).appendTo(dd); + + $('<input/>', { + type: 'text', + name: that.name, + disabled: that.disabled, + size: that.size, + title: that.tooltip + }).appendTo(div); + + div.append(' '); + + $('<a/>', { + name: 'remove', + href: 'jslink', + title: 'Remove', + html: 'Remove' + }).appendTo(div); + + if (that.undo) { + div.append(' '); + that.create_undo(div); + } + + div.append(' '); + + $('<span/>', { + name: 'error_link', + html: 'Text does not match field pattern', + 'class': 'ui-state-error ui-corner-all', + style: 'display:none' + }).appendTo(div); + + $('<a/>', { + name: 'add', + href: 'jslink', + title: 'Add', + html: 'Add' + }).appendTo(dd); + + dd.append(' '); + + $('<span/>', { + name: 'undo_all', + style: 'display: none;', + 'class': 'ui-state-highlight ui-corner-all undo', + html: 'undo all' + }).appendTo(dd); + }; + + that.setup = function(container) { + + that.widget_setup(container); + + that.template = $('div[name=value]', that.container); + that.template.detach(); + + var undo = that.get_undo(); + undo.click(function() { + that.reset(); + }); + + var add_link = $('a[name=add]', that.container); + add_link.click(function() { + that.add_row(''); + var input = $('input[name="'+that.name+'"]:last', that.container); + input.focus(); + return false; + }); + }; + + that.save = function() { + var values = []; + if (that.read_only || !that.writable) { + $('label[name="'+that.name+'"]', that.container).each(function() { + var input = $(this); + var value = $.trim(input.html()); + values.push(value); + }); + + } else { + $('input[name="'+that.name+'"]', that.container).each(function() { + var input = $(this); + if (input.is('.strikethrough')) return; + + var value = $.trim(input.val()); + values.push(value); + }); + } + return values; + }; + + that.add_row = function(value) { + + var add_link = $('a[name=add]', that.container); + + var row = that.template.clone(); + row.insertBefore(add_link); + + var input = $('input[name="'+that.name+'"]', row); + var remove_link = $('a[name=remove]', row); + var undo_link = $('span[name=undo]', row); + + if (that.read_only || !that.writable) { + var label = $('<label/>', { + 'name': that.name, + 'html': value + }); + input.replaceWith(label); + + remove_link.css('display', 'none'); + + } else { + input.val(value); + + var index = that.row_index(row); + if (index >= that.values.length) { + // show undo/remove link for new value + if (that.undo) { + that.show_undo(index); + that.show_undo(); + remove_link.css('display', 'none'); + } else { + remove_link.css('display', 'inline'); + } + } + + input.keyup(function() { + var index = that.row_index(row); + // uncross removed value + input.removeClass('strikethrough'); + if (that.undo) { + that.show_undo(index); + that.show_undo(); + if (index < that.values.length) { + remove_link.css('display', 'inline'); + } + } + var value = $(this).val(); + that.validate_input(value); + }); + + remove_link.click(function() { + var index = that.row_index(row); + if (index < that.values.length) { + // restore old value then cross it out + that.update(index); + input.addClass('strikethrough'); + if (that.undo) { + that.show_undo(index); + that.show_undo(); + } + remove_link.css('display', 'none'); + } else { + // remove new value + row.remove(); + } + return false; + }); + + undo_link.click(function() { + var index = that.row_index(row); + if (index < that.values.length) { + // restore old value + that.reset(index); + input.removeClass('strikethrough'); + remove_link.css('display', 'inline'); + } else { + // remove new value + row.remove(); + } + }); + } + }; + + that.remove_row = function(index) { + that.get_row(index).remove(); + }; + + that.get_row = function(index) { + return $('div[name=value]:eq('+index+')', that.container); + }; + + that.get_rows = function() { + return $('div[name=value]', that.container); + }; + + that.row_index = function(row) { + return that.get_rows().index(row); + }; + + that.reset = function(index) { + that.hide_undo(index); + that.update(index); + }; + + that.update = function(index) { + + var value; + + if (index === undefined) { + $('div[name=value]', that.container).remove(); + + for (var i=0; i<that.values.length; i++) { + value = that.values[i]; + that.add_row(value); + } + + var add_link = $('a[name=add]', that.container); + + if (that.read_only || !that.writable) { + add_link.css('display', 'none'); + } else { + add_link.css('display', 'inline'); + } + + } else { + value = that.values[index]; + var row = that.get_row(index); + var input = $('input[name="'+that.name+'"]', row); + input.val(value); + } + }; + + return that; +}; + IPA.checkbox_widget = function (spec) { spec = spec || {}; |