diff options
Diffstat (limited to 'install/ui/src/freeipa/field.js')
-rw-r--r-- | install/ui/src/freeipa/field.js | 568 |
1 files changed, 545 insertions, 23 deletions
diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index 087f6e289..f054ecc0e 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -35,55 +35,187 @@ define([ './text'], function(array, lang, metadata_provider, builder, IPA, $, navigation, phases, reg, text) { +/** + * Field module + * @class field + * @singleton + */ var exp = {}; +/** + * Field + * @class IPA.field + */ IPA.field = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Entity + * @property {entity.entity} + */ that.entity = IPA.get_entity(spec.entity); + + /** + * Facet + * @property {facet.facet} + */ that.facet = spec.facet; + + /** + * Container + * @property {facet.facet|IPA.dialog} + */ that.container = null; + + /** + * Name + * @property {string} + */ that.name = spec.name; + + /** + * Entity param name + * + * - defaults to `name` + * - can be change if multiple fields touches the same param + * @property {string} + */ that.param = spec.param || spec.name; /** - * For most options param == acl_param. But some params might be virtual and - * actual rights might be defined by other param. + * Entity param which provides access control rights + * + * - defaults to `param` + * - some params might be virtual and thus actual rights might be + * defined by other param. + * @property {string} */ that.acl_param = spec.acl_param || that.param; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + + /** + * Tooltip + * @property {string} + */ that.tooltip = text.get(spec.tooltip); + + /** + * Measurement unit + * @property {string} + */ that.measurement_unit = spec.measurement_unit; + + /** + * Formatter + * + * - transforms field value to widget value + * - only for read-only fields + * @property {IPA.formatter} + */ that.formatter = builder.build('formatter', spec.formatter); + /** + * Widget + * @property {IPA.input_widget} + */ that.widget = null; + + /** + * Widget name within `container` + * @property {string} + */ that.widget_name = spec.widget; - // override the required flag in metadata + /** + * Override the required flag in metadata + * @property {boolean} + */ that.required = spec.required; - // read_only is set when widget is created + /** + * read_only is set when widget is created + * @readonly + * @property {boolean} + */ that.read_only = spec.read_only; - // writable is set during load + /** + * Writable is set during load + * @readonly + * @property {boolean} + */ that.writable = true; + /** + * Enabled + * @readonly + * @property {boolean} + */ that.enabled = spec.enabled === undefined ? true : spec.enabled; + + /** + * Flags + * @property {Array.<string>} + */ that.flags = spec.flags || []; + /** + * Undo is displayable + * + * - when false, undo button is not displayed even when the field is dirty + * @property {boolean} + */ that.undo = spec.undo === undefined ? true : spec.undo; + /** + * Metadata + * @property {Object} + */ that.metadata = spec.metadata; + + /** + * Validators + * @property {Array.<IPA.validator>} + */ that.validators = builder.build('validator', spec.validators) || []; + /** + * Field priority + * @property {number} + */ that.priority = spec.priority; + /** + * Loaded values + * @property {Array.<Object>} + */ that.values = []; + + /** + * Field is dirty (value is modified) + * @readonly + * @property {boolean} + */ that.dirty = false; + + /** + * Current value is valid - passes validators + * @property {boolean} + */ that.valid = true; + /** + * Dirty has changed + * @event + * @property {IPA.observer} + */ that.dirty_changed = IPA.observer(); var init = function() { @@ -105,6 +237,10 @@ IPA.field = function(spec) { that.validators.push(IPA.metadata_validator()); }; + /** + * Evaluate if field has to have some value + * @return {boolean} + */ that.is_required = function() { if (that.read_only) return false; if (!that.writable) return false; @@ -113,18 +249,30 @@ IPA.field = function(spec) { return that.metadata && that.metadata.required; }; + /** + * Required setter + * @param {boolean} required + */ that.set_required = function(required) { that.required = 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()); } }; + /** + * Check if value is set when it has to be. Show error if not. + * @return {boolean} + */ that.validate_required = function() { var values = that.save(); if (IPA.is_empty(values) && that.is_required() && that.enabled) { @@ -138,9 +286,10 @@ IPA.field = function(spec) { }; /** - * 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. + * 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} */ that.validate = function() { that.hide_error(); @@ -170,7 +319,7 @@ IPA.field = function(spec) { /** * This function stores the entire record and the values - * of the field, then invoke reset() to update the UI. + * of the field, then invoke `reset()` to update the UI. */ that.load = function(record) { that.record = record; @@ -182,6 +331,14 @@ IPA.field = function(spec) { that.reset(); }; + /** + * Get value of attribute with given name from record (during `load`) + * + * @protected + * @param {Object} record + * @param {string} name + * @return {Array} array of values + */ that.get_value = function(record, name) { var value = record[name]; @@ -197,6 +354,24 @@ IPA.field = function(spec) { return value; }; + /** + * Evaluate if field is writable according to ACL in record and field + * configuration. Updates `writable` property. + * + * Not writable: + * + * - primary keys + * - with 'no_update' metadata flag + * + * Writable: + * + * - attribute level rights for acl param contains 'w' + * - with 'w_if_no_aci' flag and no attribute level rights and user has + * rights to modify objectclass + * + * @protected + * @param {Object} record + */ that.load_writable = function(record) { that.writable = true; @@ -229,6 +404,9 @@ IPA.field = function(spec) { } }; + /** + * Reset field and widget to loaded values + */ that.reset = function() { that.set_widget_flags(); that.update_required(); @@ -237,6 +415,9 @@ IPA.field = function(spec) { that.set_dirty(false); }; + /** + * Update widget with loaded values. + */ that.update = function() { if (!that.widget || !that.widget.update) return; @@ -259,6 +440,13 @@ IPA.field = function(spec) { that.widget.update(formatted_values); }; + /** + * Create and return update info. + * + * Update info is a record which contains information about modifications + * since load. + * @return {Object} update info + */ that.get_update_info = function() { var update_info = IPA.update_info_builder.new_update_info(); @@ -274,6 +462,7 @@ 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. + * @return {Array} values */ that.save = function(record) { @@ -296,6 +485,8 @@ IPA.field = function(spec) { * This function compares the original values and the * values entered in the UI. If the values have changed * it will return true. + * @protected + * @return {boolean} dirty */ that.test_dirty = function() { @@ -318,6 +509,13 @@ IPA.field = function(spec) { return !that.dirty_are_equal(that.values, values); }; + /** + * Compares values in two arrays + * @protected + * @param {Array} orig_vals + * @param {Array} new_vals + * @return {boolean} values are equal + */ that.dirty_are_equal = function(orig_vals, new_vals) { orig_vals.sort(); @@ -333,14 +531,17 @@ IPA.field = function(spec) { }; /** - * This function compares the original values and the - * values entered in the UI. If the values have changed - * it will return true. + * Getter for `dirty` + * @return {boolean} */ that.is_dirty = function() { return that.dirty; }; + /** + * Setter for `dirty` + * @param {boolean} dirty + */ that.set_dirty = function(dirty) { var old = that.dirty; that.dirty = dirty; @@ -354,14 +555,28 @@ IPA.field = function(spec) { }; + /** + * 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(); } @@ -369,6 +584,10 @@ IPA.field = function(spec) { } }; + /** + * `enabled` setter + * @param {boolean} value + */ that.set_enabled = function(value) { that.enabled = value; if (that.widget && that.widget.set_enabled) { @@ -376,9 +595,17 @@ IPA.field = function(spec) { } }; + /** + * 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) { @@ -391,6 +618,9 @@ IPA.field = function(spec) { } }; + /** + * Bind field to a widget defined by `widget_name` + */ that.widgets_created = function() { that.widget = that.container.widgets.get_widget(that.widget_name); @@ -403,11 +633,17 @@ IPA.field = function(spec) { } }; + /** + * 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(); }; @@ -427,14 +663,29 @@ IPA.field = function(spec) { return that; }; +/** + * Validator + * + * - base class, always returns positive result + * + * @class IPA.validator + */ IPA.validator = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Error message + * @property {string} + */ that.message = text.get(spec.message || '@i18n:widget.validation.error'); + /** + * Create negative validation result + * @return {Object} result + */ that.false_result = function(message) { return { valid: false, @@ -442,12 +693,22 @@ IPA.validator = function(spec) { }; }; + /** + * Create positive validation result + * @return {Object} result + */ that.true_result = function() { return { valid: true }; }; + /** + * Perform validation logic + * @param {Mixed} value + * @param {Object} context expected context is field which value is validated + * @return {Object} validation result + */ that.validate = function() { return that.true_result(); }; @@ -455,10 +716,21 @@ IPA.validator = function(spec) { return that; }; +/** + * Metadata validator + * + * Validates value according to supplied metadata + * + * @class IPA.metadata_validator + * @extends IPA.validator + */ IPA.metadata_validator = function(spec) { var that = IPA.validator(spec); + /** + * @inheritDoc + */ that.validate = function(value, context) { var message; @@ -507,14 +779,27 @@ IPA.metadata_validator = function(spec) { return that; }; +/** + * Checks if value is supported + * + * @class IPA.unsupported_validator + * @extends IPA.validator + */ IPA.unsupported_validator = function(spec) { spec.message = spec.message ||'@i18n:widgets.validation.unsupported'; var that = IPA.validator(spec); + /** + * Unsupported values + * @property {Array.<string>} + */ that.unsupported = spec.unsupported || []; + /** + * @inheritDoc + */ that.validate = function(value, context) { if (IPA.is_empty(value)) return that.true_result(); @@ -527,15 +812,32 @@ IPA.unsupported_validator = function(spec) { return that; }; +/** + * Check if value is the same as in other field. + * + * - designed for password confirmation + * + * @class IPA.same_password_validator + * @extends IPA.validator + */ IPA.same_password_validator = function(spec) { spec = spec || {}; var that = IPA.validator(spec); + + /** + * Other field name + * @property {string} + */ that.other_field = spec.other_field; that.message = text.get(spec.message || '@i18n:password.password_must_match', "Passwords must match"); + + /** + * @inheritDoc + */ that.validate = function(value, context) { var other_field = context.container.fields.get_field(that.other_field); @@ -550,15 +852,33 @@ IPA.same_password_validator = function(spec) { return that; }; +/** + * Used along with checkbox widget + * + * @class IPA.checkbox_field + * @extends IPA.field + */ IPA.checkbox_field = function(spec) { spec = spec || {}; 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; @@ -575,13 +895,20 @@ IPA.checkbox_field = function(spec) { 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 + /** + * A checkbox will always have a value, so it's never required. + * + * @return {boolean} false + */ that.is_required = function() { return false; }; @@ -591,6 +918,12 @@ IPA.checkbox_field = function(spec) { return that; }; +/** + * Used along with checkboxes widget + * + * @class IPA.checkboxes_field + * @extends IPA.field + */ IPA.checkboxes_field = function(spec) { spec = spec || {}; @@ -600,17 +933,29 @@ IPA.checkboxes_field = function(spec) { return that; }; +/** + * Used along with radio widget + * + * @class IPA.radio_field + * @extends IPA.field + */ IPA.radio_field = function(spec) { spec = spec || {}; var that = IPA.field(spec); - // a radio will always have a value, so it's never required + /** + * A radio will always have a value, so it's never required + * @return {boolean} false + */ that.is_required = function() { return false; }; + /** + * @inheritDoc + */ that.widgets_created = function() { that.field_widgets_created(); @@ -619,23 +964,38 @@ IPA.radio_field = function(spec) { return that; }; +/** + * Used along with multivalued widget + * + * @class IPA.multivalued_field + * @extends IPA.field + */ IPA.multivalued_field = function(spec) { spec = spec || {}; 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(); @@ -643,6 +1003,12 @@ IPA.multivalued_field = function(spec) { return that.validate_core(values); }; + /** + * Validate each value separately. + * @protected + * @param {Array} values + * @return {boolean} valid + */ that.validate_core = function(values) { that.hide_error(); @@ -672,17 +1038,35 @@ IPA.multivalued_field = function(spec) { return that; }; +/** + * Used along with ssh key widget + * + * @class IPA.sshkeys_field + * @extends IPA.multivalued_field + */ IPA.sshkeys_field = function(spec) { spec = spec || {}; var that = IPA.multivalued_field(spec); - // Fixes upgrade issue. When attr rights are missing due to lack of object class. + /** + * By default has 'w_if_no_aci' flag. + * + * - 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); @@ -710,6 +1094,9 @@ IPA.sshkeys_field = function(spec) { that.reset(); }; + /** + * @inheritDoc + */ that.dirty_are_equal = function(orig_vals, new_vals) { var i; @@ -725,12 +1112,21 @@ IPA.sshkeys_field = function(spec) { 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(); @@ -739,19 +1135,38 @@ IPA.select_field = function(spec) { 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(); } + + /** + * 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( @@ -760,12 +1175,21 @@ IPA.link_field = function(spec) { 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. @@ -793,6 +1217,9 @@ IPA.link_field = function(spec) { }).execute(); }; + /** + * @inheritDoc + */ that.widgets_created = function() { that.field_widgets_created(); that.widget.link_clicked.attach(that.on_link_clicked); @@ -802,16 +1229,42 @@ IPA.link_field = function(spec) { return that; }; +/** + * Field for enabling/disabling entity + * + * - expects radio widget + * - requires facet to use 'update_info' update method + * + * @class IPA.enable_field + * @extends IPA.field + */ IPA.enable_field = function(spec) { spec = spec || {}; var that = IPA.radio_field(spec); + /** + * Name of entity's enable method + * @property {string} + */ that.enable_method = spec.enable_method || 'enable'; + + /** + * Name of entity's disable method + * @property {string} + */ that.disable_method = spec.enable_method || 'disable'; + + /** + * Value of radio's enable option + * @property {string} + */ that.enable_option = spec.enable_option || 'TRUE'; + /** + * @inheritDoc + */ that.get_update_info = function() { var info = IPA.update_info_builder.new_update_info(); @@ -840,30 +1293,59 @@ IPA.enable_field = function(spec) { return that; }; -// TODO: Add support for nested fields +/** + * Collection of fields + * @class IPA.field_container + */ IPA.field_container = function(spec) { spec = spec || {}; var that = IPA.object(); - that.container = spec.container; //usually facet or dialog + /** + * Parent container + * + * - usually facet or dialog + */ + that.container = spec.container; + /** + * Collection of fields + * @property {ordered_map} + * @protected + */ that.fields = $.ordered_map(); + /** + * Get field with given name + * @param {string} name + * @return {IPA.field} + */ that.get_field = function(name) { return that.fields.get(name); }; - that.get_fields = function(name) { + /** + * Get all fields + * @return {Array.<IPA.field>} + */ + that.get_fields = function() { return that.fields.values; }; + /** + * Add field + * @param {IPA.field} field + */ that.add_field = function(field) { field.container = that.container; that.fields.put(field.name, field); }; + /** + * Call each field's `widgets_created` method. + */ that.widgets_created = function() { var fields = that.fields.values; @@ -877,16 +1359,33 @@ IPA.field_container = function(spec) { return that; }; +/** + * Old field builder + * @class IPA.field_builder + */ IPA.field_builder = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Field context property: container + * @property {facet.facet|IPA.dialog} + */ that.container = spec.container; - that.field_options = spec.field_options || {}; + /** + * Map of additional field context properties + * @property {Object} + */ + that.field_options = spec.field_options || {}; + /** + * Build one field + * @param {Object} spec + * @param {facet.facet|IPA.dialog} container + */ that.build_field = function(spec, container) { var context = lang.mixin({}, that.field_options); @@ -895,6 +1394,11 @@ IPA.field_builder = function(spec) { return field; }; + /** + * Build multiple fields + * @param {Array.<Object>} spec + * @param {facet.facet|IPA.dialog} container + */ that.build_fields = function(specs, container) { return that.build_field(specs, container); @@ -903,7 +1407,11 @@ IPA.field_builder = function(spec) { return that; }; - +/** + * Field pre_op build operation + * @member field + * @return spec + */ exp.pre_op = function(spec, context) { if (context.facet) spec.facet = context.facet; @@ -912,13 +1420,21 @@ exp.pre_op = function(spec, context) { return spec; }; +/** + * Field post_op build operation + * @member field + * @return obj + */ exp.post_op = function(obj, spec, context) { if (context.container) context.container.add_field(obj); return obj; }; -// Field builder and registry +/** + * Field builder with registry + * @member field + */ exp.builder = builder.get('field'); exp.builder.factory = IPA.field; exp.builder.string_mode = 'property'; @@ -927,11 +1443,17 @@ reg.set('field', exp.builder.registry); exp.builder.pre_ops.push(exp.pre_op); exp.builder.post_ops.push(exp.post_op); -// Validator builder and registry +/** + * Validator builder with registry + * @member field + */ exp.validator_builder = builder.get('validator'); -//exp.validator_builder.factory = IPA.formatter; reg.set('validator', exp.validator_builder.registry); +/** + * Register fields and validators to global registry + * @member field + */ exp.register = function() { var f = reg.field; var v = reg.validator; |