diff options
Diffstat (limited to 'install/ui/src/freeipa/field.js')
-rw-r--r-- | install/ui/src/freeipa/field.js | 1142 |
1 files changed, 499 insertions, 643 deletions
diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index ab04fcacf..52cd2b18f 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -24,7 +24,9 @@ define([ 'dojo/_base/array', + 'dojo/_base/declare', 'dojo/_base/lang', + 'dojo/Evented', './metadata', './builder', './datetime', @@ -34,25 +36,31 @@ define([ './phases', './reg', './rpc', - './text'], - function(array, lang, metadata_provider, builder, datetime, IPA, $, - navigation, phases, reg, rpc, text) { + './text', + './util', + './FieldBinder'], + function(array, declare, lang, Evented, metadata_provider, builder, datetime, + IPA, $, navigation, phases, reg, rpc, text, util, FieldBinder) { /** * Field module - * @class field + * + * Contains basic fields, adapters and validators. + * + * @class * @singleton */ -var exp = {}; +var field = {}; /** * Field - * @class IPA.field + * @class + * @alternateClassName IPA.field */ -IPA.field = function(spec) { +field.field = IPA.field = function(spec) { spec = spec || {}; - var that = IPA.object(); + var that = new Evented(); /** * Entity @@ -70,7 +78,7 @@ IPA.field = function(spec) { * Container * @property {facet.facet|IPA.dialog} */ - that.container = null; + that.container = spec.container; /** * Name @@ -116,22 +124,48 @@ IPA.field = function(spec) { that.measurement_unit = spec.measurement_unit; /** - * Formatter + * Data parser + * + * - transforms datasource value to field value + * @property {IPA.formatter} + */ + that.data_parser = builder.build('formatter', spec.data_parser); + + /** + * Data formatter + * + * - formats field value to datasource value + * + * @property {IPA.formatter} + */ + that.data_formatter = builder.build('formatter', spec.data_formatter); + + /** + * UI parser + * + * - formats widget value to field value * - * - transforms field value to widget value - * - use corresponding output_formatter if field is not read-only and - * backend can't handle the different format * @property {IPA.formatter} */ - that.formatter = builder.build('formatter', spec.formatter); + that.ui_parser = builder.build('formatter', spec.ui_parser); /** - * Output formatter + * UI formatter + * + * - formats field value to widget value + * - in spec one can use also `formatter` instead of `ui_formatter` * - * - transforms widget value into value for backend * @property {IPA.formatter} */ - that.output_formatter = builder.build('formatter', spec.output_formatter); + that.ui_formatter = builder.build('formatter', spec.ui_formatter || spec.formatter); + + + /** + * Adapter whÃch selected values from record on load. + * + * @property {field.Adapter} + */ + that.adapter = builder.build('adapter', spec.adapter || 'adapter', { context: that }); /** * Widget @@ -205,10 +239,20 @@ IPA.field = function(spec) { that.priority = spec.priority; /** - * Loaded values + * Loaded value + * + * - currently value is supposed to be an Array. This might change in a + * future. + * * @property {Array.<Object>} */ - that.values = []; + that.value = []; + + /** + * Default value + * @property {Mixed} + */ + that.default_value = null; /** * Field is dirty (value is modified) @@ -230,6 +274,20 @@ IPA.field = function(spec) { */ that.dirty_changed = IPA.observer(); + /** + * Last validation result + * @property {Object} + */ + that.validation_result = null; + + /** + * Controls if field should perform validation when it's not supposed to + * be edited by user (`is_editable()`). + * @property {boolean} + */ + that.validate_noneditable = spec.validate_noneditable !== undefined ? + spec.validate_noneditable : false; + var init = function() { if (typeof that.metadata === 'string') { that.metadata = metadata_provider.get(that.metadata); @@ -246,6 +304,7 @@ IPA.field = function(spec) { } } + that.set_value([], true); // default value that.validators.push(IPA.metadata_validator()); }; @@ -263,107 +322,109 @@ IPA.field = function(spec) { /** * Required setter + * + * Note that final required state also depends on `read_only` and + * `writable` states. + * * @param {boolean} required */ that.set_required = function(required) { + var old = that.is_required(); that.required = required; + var current = that.is_required(); - that.update_required(); - }; - - /** - * Update required state in widget to match field's - * @protected - */ - that.update_required = function() { - if(that.widget && that.widget.set_required) { - that.widget.set_required(that.is_required()); + if (current !== old) { + that.emit('require-change', { source: that, required: current }); } }; /** - * Check if value is set when it has to be. Show error if not. - * @return {boolean} + * Check if value is set when it has to be. Report if not. + * @return {boolean} value passes the require check */ that.validate_required = function() { - var values = that.save(); - if (IPA.is_empty(values) && that.is_required() && that.enabled) { - that.valid = false; - var message = text.get('@i18n:widget.validation.required', + var values = that.get_value(); + var result = { valid: true, message: null }; + if ((that.validate_noneditable || that.is_editable()) && + util.is_empty(values) && that.is_required()) { + result.valid = false; + result.message = text.get('@i18n:widget.validation.required', "Required field"); - that.show_error(message); - return false; + that.set_valid(result); } - return true; + return result.valid; }; /** - * Returns true and clears the error message if the field value passes - * the validation pattern. If the field value does not pass validation, - * displays the error message and returns false. - * @return {boolean} + * Validates the field. + * Sets the result by `set_valid` call. + * @return {boolean} field is valid */ that.validate = function() { - that.hide_error(); - that.valid = true; - - if (!that.enabled) return that.valid; - - var values = that.get_widget_values(); - - if (IPA.is_empty(values)) { - return that.valid; - } - - var value = values[0]; - for (var i=0; i<that.validators.length; i++) { - var validation_result = that.validators[i].validate(value, that); - that.valid = validation_result.valid; - if (!that.valid) { - that.show_error(validation_result.message); - break; + var result = { valid: true, message: null, errors: [], results: []}; + var values = that.get_value(); + + if ((that.validate_noneditable || that.is_editable()) && !util.is_empty(values)) { + + // validate all values + for (var i=0, il=values.length; i<il; i++) { + for (var j=0, jl=that.validators.length; j<jl; j++) { + var res = that.validators[j].validate(values[i], that); + result.results[i] = res; + if (!res.valid) { + result.valid = false; + result.errors[i] = res; + // set error message only for first error + if (!result.message) result.message = res.message; + break; // report only one error per value + } + } } } - return that.valid; + that.set_valid(result); + return result.valid; }; /** - * This function stores the entire record and the values - * of the field, then invoke `reset()` to update the UI. + * Set valid state and validation error message + * @param {Object|null} result Validation result + * @fires valid-change */ - that.load = function(record) { - that.record = record; - - that.values = that.get_value(record, that.param); + that.set_valid = function(result) { - that.load_writable(record); + var old_result = that.validation_result; + that.valid = result.valid; + that.validation_result = result; - that.reset(); + if (!util.equals(old_result, result)) { + that.emit('valid-change', { + source: that, + valid: result.valid, + result: result + }); + } }; /** - * Get value of attribute with given name from record (during `load`) - * - * @protected - * @param {Object} record - * @param {string} name - * @return {Array} array of values + * This function calls adapter to get value from record and date_parser to + * process it. The it sets is as `value`. */ - that.get_value = function(record, name) { - - var value = record[name]; + that.load = function(record) { - if (!(value instanceof Array)) { - value = value !== undefined ? [value] : []; + var value = that.adapter.load(record); + var parsed = util.parse(that.data_parser, value, "Parse error:"+that.name); + value = parsed.value; + if (!parsed.ok) { + window.console.warn(parsed.message); } - if (!value.length) { - value = ['']; - } + // this call is quite application specific and should be moved to + // different component + that.load_writable(record); - return value; + that.set_value(value, true); }; /** @@ -386,15 +447,15 @@ IPA.field = function(spec) { */ that.load_writable = function(record) { - that.writable = true; + var writable = true; if (that.metadata) { if (that.metadata.primary_key) { - that.writable = false; + writable = false; } if (that.metadata.flags && array.indexOf(that.metadata.flags, 'no_update') > -1) { - that.writable = false; + writable = false; } } @@ -411,44 +472,62 @@ IPA.field = function(spec) { // For all others, lack of rights means no write. if ((!rights && !(that.flags.indexOf('w_if_no_aci') > -1 && write_oc)) || (rights && rights.indexOf('w') < 0)) { - that.writable = false; + writable = false; } } + + that.set_writable(writable); }; /** - * Reset field and widget to loaded values + * Set writable + * @fires writable-change + * @param {boolean} writable */ - that.reset = function() { - that.set_widget_flags(); - that.update_required(); - that.update(); - that.validate(); - that.set_dirty(false); + that.set_writable = function(writable) { + + var old = !!that.writable; + that.writable = writable; + if (old !== writable) { + that.emit('writable-change', { source: that, writable: writable }); + } + + that.set_required(that.required); // force update of required }; /** - * Update widget with loaded values. + * Set read only + * @fires readonly-change + * @param {boolean} writable */ - that.update = function() { + that.set_read_only = function(read_only) { - if (!that.widget || !that.widget.update) return; + var old = !!that.read_only; + that.read_only = read_only; + if (old !== read_only) { + that.emit('readonly-change', { source: that, readonly: read_only }); + } + that.set_required(that.required); // force update of required + }; - var formatted_values; + /** + * Get if field is intended to be edited + * + * It's a combination of `enabled`, 'writable` and `read_only` state. + * + * @returns {Boolean} + */ + that.is_editable = function() { - // Change loaded value to human readable value - if (that.formatter) { - formatted_values = []; - for (var i=0; that.values && i<that.values.length; i++) { - var value = that.values[i]; - var formatted_value = that.formatter.format(value); - formatted_values.push(formatted_value); - } - } else { - formatted_values = that.values; - } + return that.enabled && that.writable && !that.read_only; + }; - that.widget.update(formatted_values); + /** + * Reset field and widget to loaded values + */ + that.reset = function() { + that.emit('reset', { source: that }); + that.set_value(that.get_pristine_value(), true); }; /** @@ -461,7 +540,7 @@ IPA.field = function(spec) { that.get_update_info = function() { var update_info = IPA.update_info_builder.new_update_info(); - if (that.is_dirty()) { + if (that.dirty) { var values = that.save(); var field_info = IPA.update_info_builder.new_field_info(that, values); update_info.fields.push(field_info); @@ -470,27 +549,95 @@ IPA.field = function(spec) { }; /** - * This function saves the values entered in the UI. - * It returns the values in an array, or null if - * the field should not be saved. + * Prepare value for persistor. + * + * Sets `record[param]` option if `record` is supplied. + * + * Returns `['']` when disabled. Otherwise value formatted by + * `data_formatter` and `adapter`. + * + * @param {Object} [record] * @return {Array} values */ that.save = function(record) { - var values = that.values; + if (!that.enabled) return ['']; // not pretty, maybe leave it for caller - if (!that.enabled) return ['']; + var value = that.get_value(); + var formatted = util.format(that.data_formatter, value); + if (formatted.ok) { + value = formatted.value; + } else { + window.console.warn('Output data format error:\n'+ + JSON.stringify(formatted)); + } - if (that.widget) { - values = that.get_widget_values(); - values = that.format_output(values); + var diff = that.adapter.save(value, record); + value = diff[that.param]; // a hack which should be removed. This + // function should not return any value. But + // current consumers expect it. + return value; + }; + + /** + * Get field's value + * + * Returns pure value; doesn't use any formatter. + * + * @returns {Mixed} field's value + */ + that.get_value = function() { + return that.value; + }; + + /** + * Set value + * + * Always raises value-change when setting pristine value + * + * @param {Mixed} value + * @param {boolean} pristine - value is pristine + * @fires value-change + * @fires dirty-change + */ + that.set_value = function(value, pristine) { + + that.set_previous_value(that.value); + that.value = value; + + if (util.dirty(that.value, that.previous_value, that.get_dirty_check_options()) || + pristine) { + that.emit('value-change', { + source: that, + value: that.value, + previous: that.previous_value + }); } - if (record) { - record[that.param] = values; + var dirty = false; + if (pristine) { + that.set_pristine_value(value); + } else { + dirty = that.test_dirty(); } + that.set_dirty(dirty); + that.validate(); + }; - return values; + that.get_previous_value = function() { + return that.previous_value; + }; + + that.set_previous_value = function(value) { + that.previous_value = value; + }; + + that.get_pristine_value = function() { + return that.pristine_value; + }; + + that.set_pristine_value = function(value) { + that.pristine_value = value; }; /** @@ -509,26 +656,6 @@ IPA.field = function(spec) { }; /** - * Use output formatter to transform value entered into UI to - * value used by backend - * - * @param {Array} values - * @return {Array} formatted values - */ - that.format_output = function(values) { - - if (that.output_formatter) { - var formatted_values = []; - for (var i=0; values && i<values.length; i++) { - var formatted_value = that.output_formatter.format(values[i]); - formatted_values.push(formatted_value); - } - return formatted_values; - } - return values; - }; - - /** * This function compares the original values and the * values entered in the UI. If the values have changed * it will return true. @@ -537,52 +664,25 @@ IPA.field = function(spec) { */ that.test_dirty = function() { + // remove? this check should part of container which cares, the + // field should not care if (that.read_only || !that.writable) return false; - var values = that.save(); - - //check for empty value: null, [''], '', [] - var orig_empty = IPA.is_empty(that.values); - var new_empty= IPA.is_empty(values); - if (orig_empty && new_empty) return false; - if (orig_empty != new_empty) return true; + var pristine = that.get_pristine_value(); + var value = that.get_value(); - //strict equality - checks object's ref equality, numbers, strings - if (values === that.values) return false; - - //compare values in array - if (values.length !== that.values.length) return true; - - return !that.dirty_are_equal(that.values, values); + return util.dirty(value, pristine, that.get_dirty_check_options()); }; /** - * Compares values in two arrays - * @protected - * @param {Array} orig_vals - * @param {Array} new_vals - * @return {boolean} values are equal + * Returns options for dirty check + * @returns {Object} */ - that.dirty_are_equal = function(orig_vals, new_vals) { - - orig_vals.sort(); - new_vals.sort(); - - for (var i=0; i<orig_vals.length; i++) { - if (orig_vals[i] !== new_vals[i]) { - return false; - } - } + that.get_dirty_check_options = function() { - return true; - }; - - /** - * Getter for `dirty` - * @return {boolean} - */ - that.is_dirty = function() { - return that.dirty; + return { + unordered: !that.ordered + }; }; /** @@ -592,42 +692,10 @@ IPA.field = function(spec) { that.set_dirty = function(dirty) { var old = that.dirty; that.dirty = dirty; - if (that.undo) { - that.show_undo(dirty); - } if (old !== dirty) { that.dirty_changed.notify([], that); - } - }; - - - /** - * Display validation error - * @protected - * @param {string} message - */ - that.show_error = function(message) { - if (that.widget && that.widget.show_error) that.widget.show_error(message); - }; - - /** - * Hide validation error - * @protected - */ - that.hide_error = function() { - if (that.widget && that.widget.hide_error) that.widget.hide_error(); - }; - - /** - * Show/hide undo button - * @protected - * @param {boolean} value true:show, false:hide - */ - that.show_undo = function(value) { - if (that.widget && that.widget.show_undo) { - if(value) { that.widget.show_undo(); } - else { that.widget.hide_undo(); } + that.emit('dirty-change', { source: that, dirty: dirty }); } }; @@ -636,32 +704,10 @@ IPA.field = function(spec) { * @param {boolean} value */ that.set_enabled = function(value) { + var old = !!that.enabled; that.enabled = value; - if (that.widget && that.widget.set_enabled) { - that.widget.set_enabled(value); - } - }; - - /** - * Subject to removal - * @deprecated - */ - that.refresh = function() { - }; - - /** - * Reflect `label`, `tooltip`, `measurement_unit`, `undo`, `writable`, - * `read_only` into widget. - */ - that.set_widget_flags = function() { - - if (that.widget) { - if (that.label) that.widget.label = that.label; - if (that.tooltip) that.widget.tooltip = that.tooltip; - if (that.measurement_unit) that.widget.measurement_unit = that.measurement_unit; - that.widget.undo = that.undo; - that.widget.writable = that.writable; - that.widget.read_only = that.read_only; + if (old !== that.enabled) { + that.emit('enable-change', { source: that, enabled: that.enabled }); } }; @@ -671,30 +717,13 @@ IPA.field = function(spec) { that.widgets_created = function() { that.widget = that.container.widgets.get_widget(that.widget_name); - - if(that.widget) { - that.set_widget_flags(); - - that.widget.value_changed.attach(that.widget_value_changed); - that.widget.undo_clicked.attach(that.widget_undo_clicked); + if (that.widget) { + that._binder = new FieldBinder(that, that.widget); + that._binder.bind(); + that._binder.copy_properties(); } }; - /** - * Handler for widget's `value_changed` event - */ - that.widget_value_changed = function() { - that.set_dirty(that.test_dirty()); - that.validate(); - }; - - /** - * Handler for widget's `undo_clicked` event - */ - that.widget_undo_clicked = function() { - that.reset(); - }; - init(); // methods that should be invoked by subclasses @@ -711,13 +740,91 @@ IPA.field = function(spec) { }; /** + * Adapter's task is to select wanted data from record and vice-versa. + * + * This default adapter expects that context will be field and record + * will be FreeIPA JsonRPC result. + * + * @class + */ +field.Adapter = declare(null, { + + /** + * Adapter's context; e.g., field + * + * @property {Object} + */ + context: null, + + /** + * Get single value from record + * @param {Object} record Record + * @param {string} name Attribute name + * @returns {Array} attribute value + * @protected + */ + get_value: function(record, name) { + var value = record[name]; + return util.normalize_value(value); + }, + + /** + * By default just select attribute with name defined by `context.param` + * from a record. Uses default value if value is not in record and context + * defines it. + * @param {Object} record + * @returns {Array} attribute value + */ + load: function(record) { + var value = this.get_value(record, this.context.param); + if (util.is_empty(value) && !util.is_empty(this.context.default_value)) { + value = util.normalize_value(this.context.default_value); + } + return value; + }, + + /** + * Save value into record + * + * Default behavior is to save it as property which name is defined by + * contex's param. + * @param {Object} value Value to save + * @param {Object} record Record to save the value into + * @returns {Object} what was saved + */ + save: function(value, record) { + + var diff = {}; + diff[this.context.param] = value; + if (record) { + lang.mixin(record, diff); + } + return diff; + }, + + constructor: function(spec) { + this.context = spec.context || {}; + } +}); + +/** * Validator * * - base class, always returns positive result * - * @class IPA.validator + * Result format + * + * - validation result is an object with mandatory `valid` property which + * has to be set to a boolean value. True if value is valid, false otherwise. + * - if `valid === false` result should also contain `message` property with + * human readable error text + * - it may contain also other properties; e.g., `errors` which contains an + * array with other validation result objects in case of complex validation. + * + * @class + * @alternateClassName IPA.validator */ -IPA.validator = function(spec) { +field.validator = IPA.validator = function(spec) { spec = spec || {}; @@ -768,10 +875,11 @@ IPA.validator = function(spec) { * * Validates value according to supplied metadata * - * @class IPA.metadata_validator + * @class + * @alternateClassName IPA.metadata_validator * @extends IPA.validator */ -IPA.metadata_validator = function(spec) { +field.metadata_validator = IPA.metadata_validator = function(spec) { var that = IPA.validator(spec); @@ -784,7 +892,7 @@ IPA.metadata_validator = function(spec) { var metadata = context.metadata; var number = false; - if (!metadata || IPA.is_empty(value)) return that.true_result(); + if (!metadata || util.is_empty(value)) return that.true_result(); if (metadata.type === 'int') { number = true; @@ -829,10 +937,11 @@ IPA.metadata_validator = function(spec) { /** * Checks if value is supported * - * @class IPA.unsupported_validator + * @class + * @alternateClassName IPA.unsupported_validator * @extends IPA.validator */ -IPA.unsupported_validator = function(spec) { +field.unsupported_validator = IPA.unsupported_validator = function(spec) { spec.message = spec.message ||'@i18n:widgets.validation.unsupported'; @@ -849,7 +958,7 @@ IPA.unsupported_validator = function(spec) { */ that.validate = function(value, context) { - if (IPA.is_empty(value)) return that.true_result(); + if (util.is_empty(value)) return that.true_result(); if (that.unsupported.indexOf(value) > -1) return that.false_result(); @@ -864,10 +973,11 @@ IPA.unsupported_validator = function(spec) { * * - designed for password confirmation * - * @class IPA.same_password_validator + * @class + * @alternateClassName IPA.same_password_validator * @extends IPA.validator */ -IPA.same_password_validator = function(spec) { +field.same_password_validator = IPA.same_password_validator = function(spec) { spec = spec || {}; @@ -887,7 +997,7 @@ IPA.same_password_validator = function(spec) { */ that.validate = function(value, context) { - var other_field = context.container.fields.get_field(that.other_field); + var other_field = context.container.get_field(that.other_field); var other_value = other_field.save(); var this_value = context.save(); @@ -900,48 +1010,25 @@ IPA.same_password_validator = function(spec) { }; /** - * Check if input value is a valid datetime - * - * @class IPA.datetime_validator - * @extends IPA.validator - */ -IPA.datetime_validator = function(spec) { - - spec = spec || {}; - - var that = IPA.validator(spec); - - that.message = text.get(spec.message || '@i18n:widget.validation.datetime'); - - /** - * @inheritDoc - */ - that.validate = function(value, context) { - - var valid = datetime.parse(value) !== null; - if (!valid) return that.false_result(); - - return that.true_result(); - }; - - return that; -}; - -/** * Used along with checkbox widget * - * @class IPA.checkbox_field + * @class + * @alternateClassName IPA.datetime_field * @extends IPA.field */ -IPA.datetime_field = function(spec) { +field.datetime_field = IPA.datetime_field = function(spec) { spec = spec || {}; - spec.validators = spec.validators || ['datetime']; - spec.output_formatter = spec.output_formatter || { + spec.data_formatter = spec.data_formatter || { $type: 'datetime', template: datetime.templates.generalized }; - spec.formatter = spec.formatter || 'datetime'; + spec.data_parser = spec.formatter || 'datetime'; + spec.ui_formatter = spec.ui_formatter || spec.formatter || { + $type: 'datetime', + template: datetime.templates.human + }; + spec.ui_parser = spec.ui_parser || 'datetime'; var that = IPA.field(spec); return that; @@ -950,56 +1037,18 @@ IPA.datetime_field = function(spec) { /** * Used along with checkbox widget * - * @class IPA.checkbox_field + * @class + * @alternateClassName IPA.checkbox_field * @extends IPA.field */ -IPA.checkbox_field = function(spec) { +field.checkbox_field = IPA.checkbox_field = function(spec) { spec = spec || {}; + spec.data_parser = 'boolean'; var that = IPA.field(spec); /** - * Check value by default - * @property {boolean} - */ - that.checked = spec.checked || false; - - /** - * Boolean formatter for parsing loaded values. - * @property {IPA.boolean_formatter} - */ - that.boolean_formatter = IPA.boolean_formatter(); - - /** - * @inheritDoc - */ - that.load = function(record) { - - that.record = record; - - that.values = that.get_value(record, that.param); - - var value = that.boolean_formatter.parse(that.values); - if (value === '') value = that.widget.checked; //default value - - that.values = [value]; - - that.load_writable(record); - - that.reset(); - }; - - /** - * @inheritDoc - */ - that.widgets_created = function() { - - that.field_widgets_created(); - that.widget.checked = that.checked; - }; - - /** * A checkbox will always have a value, so it's never required. * * @return {boolean} false @@ -1008,33 +1057,17 @@ IPA.checkbox_field = function(spec) { return false; }; - that.checkbox_load = that.load; - - return that; -}; - -/** - * Used along with checkboxes widget - * - * @class IPA.checkboxes_field - * @extends IPA.field - */ -IPA.checkboxes_field = function(spec) { - - spec = spec || {}; - - var that = IPA.field(spec); - return that; }; /** * Used along with radio widget * - * @class IPA.radio_field + * @class + * @alternateClassName IPA.radio_field * @extends IPA.field */ -IPA.radio_field = function(spec) { +field.radio_field = IPA.radio_field = function(spec) { spec = spec || {}; @@ -1048,125 +1081,55 @@ IPA.radio_field = function(spec) { return false; }; - /** - * @inheritDoc - */ - that.widgets_created = function() { - - that.field_widgets_created(); - }; - return that; }; /** - * Used along with multivalued widget + * Used along with ssh key widget + * + * - by default has `w_if_no_aci` to workaround missing object class * - * @class IPA.multivalued_field + * @class + * @alternateClassName IPA.sshkeys_field * @extends IPA.field */ -IPA.multivalued_field = function(spec) { +field.sshkeys_field = IPA.sshkeys_field = function(spec) { spec = spec || {}; + spec.adapter = spec.adapter || field.SshKeysAdapter; + spec.flags = spec.flags || ['w_if_no_aci']; var that = IPA.field(spec); - - /** - * @inheritDoc - */ - that.load = function(record) { - - that.field_load(record); - }; - - /** - * @inheritDoc - */ - that.test_dirty = function() { - var dirty = that.field_test_dirty(); - dirty = dirty || that.widget.test_dirty(); //also checks order - return dirty; - }; - - /** - * @inheritDoc - */ - that.validate = function() { - - var values = that.save(); - - return that.validate_core(values); - }; - - /** - * Validate each value separately. - * @protected - * @param {Array} values - * @return {boolean} valid - */ - that.validate_core = function(values) { - - that.hide_error(); - that.valid = true; - - if (IPA.is_empty(values)) { - return that.valid; - } - - for (var i=0; i<values.length; i++) { - - for (var j=0; j<that.validators.length; j++) { - - var validation_result = that.validators[j].validate(values[i], that); - if (!validation_result.valid) { - that.valid = false; - var row_index = that.widget.get_saved_value_row_index(i); - that.widget.show_child_error(row_index, validation_result.message); - break; - } - } - } - - return that.valid; - }; - return that; }; /** - * Used along with ssh key widget - * - * @class IPA.sshkeys_field - * @extends IPA.multivalued_field + * SSH Keys Adapter + * @class + * @extends field.Adapter */ -IPA.sshkeys_field = function(spec) { - - spec = spec || {}; - - var that = IPA.multivalued_field(spec); +field.SshKeysAdapter = declare([field.Adapter], { /** - * By default has 'w_if_no_aci' flag. + * Transforms record into array of key, fingerprint pairs * - * - fixes upgrade issue. When attr rights are missing due to lack of - * object class. - */ - that.flags = spec.flags || ['w_if_no_aci']; - - /** - * Name of fingerprint param - * @property {string} - */ - that.sshfp_attr = spec.sshfp_attr || 'sshpubkeyfp'; - - /** - * @inheritDoc - */ - that.load = function(record) { - - var keys = that.get_value(record, that.param); - var fingerprints = that.get_value(record, that.sshfp_attr); - + * """ + * // input: + * { + * 'ipasshpubkey': [ 'foo', 'foo1'], + * 'sshpubkeyfp': ['fooFP', 'fooFP2'] + * } + * + * // output: + * [ + * { key: 'foo', fingerprint: 'fooFP'}, + * { key: 'foo1', fingerprint: 'fooFP2'}, + * ] + * """ + */ + load: function(record) { + var keys = this.get_value(record, this.context.param); + var fingerprints = this.get_value(record, 'sshpubkeyfp'); var values = []; if (keys.length === fingerprints.length) { @@ -1181,148 +1144,24 @@ IPA.sshkeys_field = function(spec) { values.push(value); } } - - that.values = values; - - that.load_writable(record); - - that.reset(); - }; + return values; + }, /** - * @inheritDoc + * Transforms array of pairs into array of keys and save it into record. + * @param {Array} values Source values + * @param {Object} record Target record. + * @returns {Array} saved value */ - that.dirty_are_equal = function(orig_vals, new_vals) { + save: function(values, record) { - var i; - var orig_keys = []; - - for (i=0; i<orig_vals.length; i++) { - orig_keys.push(orig_vals[i].key); + var ret = []; + for (var i=0; i<values.length; i++) { + ret.push(values[i].key); } - - return that.field_dirty_are_equal(orig_keys, new_vals); - }; - - return that; -}; - -/** - * Used along with select widget - * - * @class IPA.select_field - * @extends IPA.field - */ -IPA.select_field = function(spec) { - - spec = spec || {}; - - var that = IPA.field(spec); - - /** - * @inheritDoc - */ - that.widgets_created = function() { - - that.field_widgets_created(); - }; - - return that; -}; - -/** - * Used along with link widget - * - * @class IPA.link_field - * @extends IPA.field - */ -IPA.link_field = function(spec) { - - spec = spec || {}; - - var that = IPA.field(spec); - - /** - * Entity a link points to - * @property {entity.entity} - */ - that.other_entity = IPA.get_entity(spec.other_entity); - - function other_pkeys () { - return that.facet.get_pkeys(); + return this.inherited(arguments, [ret, record]); } - - /** - * Function which should return primary keys of link target in case of - * link points to an entity. - * @property {Function} - */ - that.other_pkeys = spec.other_pkeys || other_pkeys; - - /** - * Handler for widget `link_click` event - */ - that.on_link_clicked = function() { - - navigation.show_entity( - that.other_entity.name, - 'default', - that.other_pkeys()); - }; - - /** - * @inheritDoc - */ - that.load = function(record) { - - that.field_load(record); - that.check_entity_link(); - }; - - /** - * Check if entity exists - * - * - only if link points to an entity - * - update widget's `is_link` accordingly - */ - that.check_entity_link = function() { - - //In some cases other entity may not be present. - //For example when DNS is not configured. - if (!that.other_entity) { - that.widget.is_link = false; - that.widget.update(that.values); - return; - } - - rpc.command({ - entity: that.other_entity.name, - method: 'show', - args: that.other_pkeys(), - options: {}, - retry: false, - on_success: function(data) { - that.widget.is_link = data.result && data.result.result; - that.widget.update(that.values); - }, - on_error: function() { - that.widget.is_link = false; - that.widget.update(that.values); - } - }).execute(); - }; - - /** - * @inheritDoc - */ - that.widgets_created = function() { - that.field_widgets_created(); - that.widget.link_clicked.attach(that.on_link_clicked); - }; - - - return that; -}; +}); /** * Field for enabling/disabling entity @@ -1330,10 +1169,11 @@ IPA.link_field = function(spec) { * - expects radio widget * - requires facet to use 'update_info' update method * - * @class IPA.enable_field + * @class + * @alternateClassName IPA.enable_field * @extends IPA.field */ -IPA.enable_field = function(spec) { +field.enable_field = IPA.enable_field = function(spec) { spec = spec || {}; @@ -1390,9 +1230,10 @@ IPA.enable_field = function(spec) { /** * Collection of fields - * @class IPA.field_container + * @class + * @alternateClassName IPA.field_container */ -IPA.field_container = function(spec) { +field.field_container = IPA.field_container = function(spec) { spec = spec || {}; @@ -1456,9 +1297,10 @@ IPA.field_container = function(spec) { /** * Old field builder - * @class IPA.field_builder + * @class + * @alternateClassName IPA.field_builder */ -IPA.field_builder = function(spec) { +field.field_builder = IPA.field_builder = function(spec) { spec = spec || {}; @@ -1507,7 +1349,7 @@ IPA.field_builder = function(spec) { * @member field * @return spec */ -exp.pre_op = function(spec, context) { +field.pre_op = function(spec, context) { if (context.facet) spec.facet = context.facet; if (context.entity) spec.entity = context.entity; @@ -1520,7 +1362,7 @@ exp.pre_op = function(spec, context) { * @member field * @return obj */ -exp.post_op = function(obj, spec, context) { +field.post_op = function(obj, spec, context) { if (context.container) context.container.add_field(obj); return obj; @@ -1530,52 +1372,66 @@ exp.post_op = function(obj, spec, context) { * Field builder with registry * @member field */ -exp.builder = builder.get('field'); -exp.builder.factory = IPA.field; -exp.builder.string_mode = 'property'; -exp.builder.string_property = 'name'; -reg.set('field', exp.builder.registry); -exp.builder.pre_ops.push(exp.pre_op); -exp.builder.post_ops.push(exp.post_op); +field.builder = builder.get('field'); +field.builder.factory = field.field; +field.builder.string_mode = 'property'; +field.builder.string_property = 'name'; +reg.set('field', field.builder.registry); +field.builder.pre_ops.push(field.pre_op); +field.builder.post_ops.push(field.post_op); /** * Validator builder with registry * @member field */ -exp.validator_builder = builder.get('validator'); -reg.set('validator', exp.validator_builder.registry); +field.validator_builder = builder.get('validator'); +reg.set('validator', field.validator_builder.registry); + +/** + * Adapter builder with registry + * @member field + */ +field.adapter_builder = builder.get('adapter'); +field.adapter_builder.post_ops.push(function(obj, spec, context) { + obj.context = context.context; + return obj; + } +); +reg.set('adapter', field.adapter_builder.registry); /** * Register fields and validators to global registry * @member field */ -exp.register = function() { +field.register = function() { var f = reg.field; var v = reg.validator; - - f.register('checkbox', IPA.checkbox_field); - f.register('checkboxes', IPA.checkboxes_field); - f.register('combobox', IPA.field); - f.register('datetime', IPA.datetime_field); - f.register('enable', IPA.enable_field); - f.register('entity_select', IPA.field); - f.register('field', IPA.field); - f.register('link', IPA.link_field); - f.register('multivalued', IPA.multivalued_field); - f.register('password', IPA.field); - f.register('radio', IPA.radio_field); - f.register('select', IPA.select_field); - f.register('sshkeys', IPA.sshkeys_field); - f.register('textarea', IPA.field); - f.register('text', IPA.field); - f.register('value_map', IPA.field); - - v.register('metadata', IPA.metadata_validator); - v.register('unsupported', IPA.unsupported_validator); - v.register('same_password', IPA.same_password_validator); - v.register('datetime', IPA.datetime_validator); + var l = reg.adapter; + + f.register('checkbox', field.checkbox_field); + f.register('checkboxes', field.field); + f.register('combobox', field.field); + f.register('datetime', field.datetime_field); + f.register('enable', field.enable_field); + f.register('entity_select', field.field); + f.register('field', field.field); + f.register('link', field.field); + f.register('multivalued', field.field); + f.register('password', field.field); + f.register('radio', field.radio_field); + f.register('select', field.field); + f.register('sshkeys', field.sshkeys_field); + f.register('textarea', field.field); + f.register('text', field.field); + f.register('value_map', field.field); + + v.register('metadata', field.metadata_validator); + v.register('unsupported', field.unsupported_validator); + v.register('same_password', field.same_password_validator); + + l.register('adapter', field.Adapter); }; -phases.on('registration', exp.register); +phases.on('registration', field.register); -return exp; +return field; }); |