diff options
43 files changed, 5571 insertions, 479 deletions
diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js index 4c1007b7b..4bde1238c 100644 --- a/install/ui/src/freeipa/Application_controller.js +++ b/install/ui/src/freeipa/Application_controller.js @@ -18,12 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Application controller - * - * Controls interaction between navigation, menu and facets. - */ - define([ 'dojo/_base/declare', 'dojo/_base/lang', @@ -44,10 +38,11 @@ define([ JSON, App_widget, IPA, Menu, Router, menu_spec) { /** - * Main application + * Application controller + * + * Controls interaction between navigation, menu and facets. * - * This class serves as top level widget. It creates basic UI: controls - * rendering of header, footer and placeholder for facets. + * @class Application_controller */ var App = declare(null, { diff --git a/install/ui/src/freeipa/_base/Builder.js b/install/ui/src/freeipa/_base/Builder.js index e4669ed9e..e487aa542 100644 --- a/install/ui/src/freeipa/_base/Builder.js +++ b/install/ui/src/freeipa/_base/Builder.js @@ -28,35 +28,51 @@ define(['dojo/_base/declare', var undefined; + /** + * Builder + * + * Builds objects based on their specification. + * @class _base.Builder + */ var Builder = declare(null, { /** - * Builds objects based on specication. - * - * @class - * @name Builder - */ - - /** * Construct registry - * @property ./Construct_registry + * @property {_base.Construct_registry} */ registry: null, /** * Specification modifier + * @property {_base.Spec_mod} */ spec_mod: null, + /** + * Default factory + * @property {Function|null} + */ factory: null, + /** + * Default constructor + * @property {Function|null} + */ ctor: null, /** * Array of spec modifiers. * - * Spec modifier is a function which is called before build. - * takes params: spec, context - * returns spec + * Are applied before build on spec object. + * + * Spec modifier can be: + * + * - a function which is called before build + * - takes params: spec, context + * - returns spec + * - an object which is mixed in into spec + * - an object with properties for Spec_mod + * + * @property {Array|null} */ pre_ops: null, @@ -64,55 +80,59 @@ define(['dojo/_base/declare', * Array of object modifiers. * * Object modifier is a function which is after build. - * takes params: built object, spec, context - * returns object + * + * - takes params: built object, spec, context + * - returns object + * @property {Array|null} */ post_ops: null, /** * Controls what builder do when spec is a string. Possible values: - * * 'type' - * * 'property' - * Type: - * Spec is type. Queries registry for obtaining construction spec. * - * Property: - * Spec is a property of spec, name of property is set in - * `string_property`. This mode should be combined with default - * factory or ctor otherwise the build will fail. + * - 'type' + * - 'property' + * + * ##Type + * Spec is type. Queries registry for obtaining construction spec. + * + * ##Property + * Spec is a property of spec, name of property is set in + * `string_property`. This mode should be combined with default + * factory or ctor otherwise the build will fail. * - * @type {String} + * @property {string} */ string_mode: 'type', /** * Property name for `string_mode` == `property` - * @type {String} + * @property {string} */ string_property: '', /** * Build object based on spec. * - * @param {String|Function|Object|Array} Build spec - * @param {Object} build context - * @param {Object} overrides + * @param {string|Function|Object|Array} spec Build spec * - * String: type name, queries registry - * Function: factory or ctor - * Object: spec object - * Array: array of spec objects + * - **String**: type name, queries registry + * - **Function**: factory or ctor + * - **Object**: spec object + * - **Array**: array of spec objects * - * Build control properies of spec object: - * $ctor: Function - * $factory: Function - * $mixim_spec: Boolean - * $type: String - * $pre_ops: [] - * $post_ops: [] + * Build control properties of spec object: * - * All other properties will be passed to object construction method. + * - $ctor: Function + * - $factory: Function + * - $mixim_spec: boolean + * - $type: string + * - $pre_ops: [] + * - $post_ops: [] * + * All other properties will be passed to object construction method. + * @param {Object} context build context + * @param {Object} overrides * Builder default factory and ctor is overridden by those specified * in overrides when overrides are set. */ @@ -167,12 +187,20 @@ define(['dojo/_base/declare', return objects; }, + /** + * Build single object + * @protected + */ _build: function(spec, context) { var cs = this._get_construction_spec(spec); var obj = this._build_core(cs, context); return obj; }, + /** + * Normalizes construction specification + * @protected + */ _get_construction_spec: function(spec) { var cs = {}; @@ -235,6 +263,7 @@ define(['dojo/_base/declare', /** * Queries registry and returns copy of construction specification + * @protected */ _query_registry: function(type) { @@ -253,6 +282,7 @@ define(['dojo/_base/declare', /** * Get cs from string according to string mode + * @protected */ _get_cs_string: function(spec) { @@ -267,6 +297,10 @@ define(['dojo/_base/declare', return cs; }, + /** + * Core build method + * @protected + */ _build_core: function(construction_spec, context) { var cs = construction_spec, @@ -318,6 +352,10 @@ define(['dojo/_base/declare', return obj; }, + /** + * Apply pre_ops + * @protected + */ _run_preops: function(pre_ops, spec, context) { for (var i=0; i<pre_ops.length; i++) { var preop = pre_ops[i]; @@ -334,6 +372,10 @@ define(['dojo/_base/declare', return spec; }, + /** + * Apply post_ops + * @protected + */ _run_post_ops: function(post_ops, obj, spec, context) { for (var i=0; i<post_ops.length; i++) { var postop = post_ops[i]; diff --git a/install/ui/src/freeipa/_base/Construct_registry.js b/install/ui/src/freeipa/_base/Construct_registry.js index d9489add3..934af6ef1 100644 --- a/install/ui/src/freeipa/_base/Construct_registry.js +++ b/install/ui/src/freeipa/_base/Construct_registry.js @@ -24,12 +24,12 @@ define(['dojo/_base/declare', './construct' ], function(declare, array, lang, construct) { + /** + * Registry for storing construction specification. + * @class _base.Construct_registry + */ var Construct_registry = declare(null, { - /** - * Registry for storing construction specification. - * @class - * @name Construct_registry - */ + /** * Internal map for construction specifications. @@ -40,27 +40,25 @@ define(['dojo/_base/declare', /** * Registers construction specification * - * @param type {String|Object} type or construction spec - * @param func {Function} ctor or factory function - * @param [default_spec] {Object} default spec object for given type - * - * @returns Object + * // May be defined by single construction spec object: + * var construction_spec = { + * type: String, + * factory: Function, + * ctor: Function, + * spec: Object, + * pre_ops: [], + * post_ops: [] + * }; + * register(construction_spec); * - * Examples: + * // or by defining them separately as params: + * register(type, factory|ctor, spec); * - * May be defined by single construction spec object: - * var construction_spec = { - * type: String, - * factory: Function, - * ctor: Function, - * spec: Object, - * pre_ops: [], - * post_ops: [] - * }; - * register(construction_spec); + * @param {string|Object} type type or construction spec + * @param {Function} func ctor or factory function + * @param {Object} [default_spec] default spec object for given type * - * or by defining them separately as params: - * register(type, factory|ctor, spec); + * @returns {Object} */ register: function(type, func, default_spec) { @@ -91,9 +89,9 @@ define(['dojo/_base/declare', * Makes a copy of construct specification of original type. Extends * it with values in supplied construct specification. * - * @param {String} Original type - * @param {String} New type - * @param {Object} Construction specification + * @param {string} org_type Original type + * @param {string} new_type New type + * @param {Object} construct_spec Construction specification */ copy: function(org_type, new_type, construct_spec) { @@ -128,9 +126,9 @@ define(['dojo/_base/declare', * * When op is Object, the object gets mixed in into spec. * - * @param {type} type + * @param {string} type * @param {Function|Object} op - * @param {Boolean} move op to first position + * @param {boolean} move op to first position */ register_pre_op: function(type, op, first) { @@ -150,9 +148,9 @@ define(['dojo/_base/declare', * When op is Object, the object gets mixed in into built object. Use * with caution. * - * @param {type} type + * @param {string} type * @param {Function|Object} op - * @param {Boolean} move op to first position + * @param {boolean} first move op to first position */ register_post_op: function(type, op, first) { @@ -164,8 +162,8 @@ define(['dojo/_base/declare', /** * Gets construction specification for given type. * - * @param type {String} Type name - * @returns Object|null + * @param {string} string Type name + * @returns {Object|null} */ get: function(type) { return this._map[type] || null; diff --git a/install/ui/src/freeipa/_base/Phase_controller.js b/install/ui/src/freeipa/_base/Phase_controller.js index 095b90241..9147f201a 100644 --- a/install/ui/src/freeipa/_base/Phase_controller.js +++ b/install/ui/src/freeipa/_base/Phase_controller.js @@ -28,17 +28,66 @@ define([ '../ordered-map' ], function(lang, array, declare, Deferred, all, topic, ordered_map) { + + /** + * Phase + * + * This class does not exist, it's only for documentation purposes. + * + * @class _base.Phase_controller.phase + * @abstract + */ + /** + * Name + * @property {string} name + */ + /** + * Tasks + * @property {Array.<_base.Phase_controller.task>} tasks + */ + + /** + * Phase task + * + * This class does not exist, it's only for documentation purposes. + * + * @class _base.Phase_controller.task + * @abstract + */ + /** + * Name + * @property {number} priority + */ + /** + * Tasks + * @property {Function} handler + */ + + /** + * Phase Controller + * + * Creates synchronization points - phases - in application life cycle. + * + * Phases: + * + * - are ordered + * - consist of task + * - phase finishes when all task finishes + * - new phases can be added at runtime + * + * @class _base.Phase_controller + */ var Phase_controller = declare(null, { /** * Phases - * @type ordered_map[name, phase] + * @property {ordered_map.<string, _base.Phase_controller.phase>} */ phases: null, /** * Current phase name - * @type String + * @property {string} */ current: null, @@ -57,13 +106,14 @@ define([ /** * Runs phase - * 1) Sorts tasks of the phase based on their priority. - * 2) Runs all task sequentially. - * 3) Waits for all tasks to complete (in case of asynchronous ones) - * 4) Optionally runs next phase * - * @param {phase} Phase to runs - * @param {Boolean} Whether to run next phase when current finishes + * 1. Sorts tasks of the phase based on their priority. + * 2. Runs all task sequentially. + * 3. Waits for all tasks to complete (in case of asynchronous ones) + * 4. Optionally runs next phase + * + * @param {_base.Phase_controller.phase} phase Phase to run + * @param {boolean} next_phase Whether to run next phase when current finishes */ _run_phase: function(phase, next_phase) { @@ -104,7 +154,7 @@ define([ /** * Selects next phase and then runs it. * - * @param {Boolen} Whether to run phases continuously + * @param {boolean} continuous Whether to run phases continuously */ next_phase: function(continuous) { var phase; @@ -125,9 +175,9 @@ define([ * At phase execution, tasks are sorted by priority and executed in * that order. * - * @param {String} Name of associated phase - * @param {Function} Task handler. Should return promise if async. - * @param {Number} Priority of task. Default 10. + * @param {string} phase_name Name of associated phase + * @param {Function} handler Task handler. Should return promise if async. + * @param {number} [priority=10] Priority of task. */ add_task: function(phase_name, handler, priority) { @@ -150,12 +200,13 @@ define([ * Adds a phase * * Possible options: - * before: 'name-of-phase' - * after: 'name-of-phase' - * position: 'position for new phase' * - * @param {String} phase name - * @param {Array} tasks + * - before: 'name-of-phase' + * - after: 'name-of-phase' + * - position: 'position for new phase' + * + * @param {string} phase name + * @param {Array.<_base.Phase_controller.task>} tasks * @param {Object} options */ add_phase: function(name, tasks, options) { @@ -184,8 +235,8 @@ define([ /** * Checks if phases with given name exists * - * @param {String} name - * @return {Boolean} + * @param {string} name + * @return {boolean} */ exists: function(name) { return !!this.phases.get(name); diff --git a/install/ui/src/freeipa/_base/Provider.js b/install/ui/src/freeipa/_base/Provider.js index 8d769a2eb..b2e55aed3 100644 --- a/install/ui/src/freeipa/_base/Provider.js +++ b/install/ui/src/freeipa/_base/Provider.js @@ -30,15 +30,18 @@ * * Path is a plain object path within a source. * - * Ie. if source is { - * foo: { - * bar: { - * a: 'val' - * } - * } - * } + * // if source is + * { + * foo: { + * bar: { + * a: 'val' + * } + * } + * } * - * path: 'foo.bar.a' would return 'val' + * // path: `foo.bar.a` would return `val` + * + * @class _base.Provider * */ define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) { @@ -48,13 +51,13 @@ define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) { /** * Array of other providers - * @type Provider[] + * @property {_base.Provider[]} */ providers: null, /** * Source object or a function which returns source object. - * @type {Function|Object|Provider} + * @property {Function|Object|_base.Provider} */ source: null, @@ -63,19 +66,20 @@ define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) { * * When defined, all lookups in source are based on the object * defined by this path within a source. - * @type String + * @property {string} */ path: null, /** * Value which is returned if no value nor alternative is found + * @property {Mixed} */ null_value: null, /** * Specifies which type should be returned. Limited to output of * typeof operator. - * @type String + * @property {string} */ required_type: null, @@ -153,7 +157,7 @@ define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) { /** * Gets value. * - * @param {String|Object} Key or value + * @param {string|Object} Key or value * @param {Object} Alternate value */ get: function(key, alternate) { diff --git a/install/ui/src/freeipa/_base/Search_provider.js b/install/ui/src/freeipa/_base/Search_provider.js index fcd2b0ff7..10ef1562e 100644 --- a/install/ui/src/freeipa/_base/Search_provider.js +++ b/install/ui/src/freeipa/_base/Search_provider.js @@ -19,24 +19,30 @@ */ /** - * Search value providers. + * Search value provider * - * Serves for searching for values within a array in a source. + * Serves for searching for values within an array in a source object. + * + * Path has input formats as follows: * - * Path has following formats: * * key1:key2:key3 * * key1:key2 * * key2 * - * base_query: '%1.takes_params' - * array_attr: 'name' + * With: + * + * * base_query: `%1.takes_params` + * * array_attr: `name` + * + * Such path is translates into query: * - * It translates into following query: - * %key1.takes_params[name=%key2].$key3 + * * `%key1.takes_params[name=%key2].$key3` * * In a future we should support defining generic queries and thus not be * limited to simple search. * + * @class _base.Search_provider + * @extends _base.Provider */ define(['dojo/_base/declare','dojo/_base/lang', './Provider'], function(declare, lang, Provider) { @@ -46,6 +52,10 @@ define(['dojo/_base/declare','dojo/_base/lang', './Provider'], base_query: null, array_attr: null, + /** + * @inheritDoc + * @protected + */ _get: function(key) { var search_keys = key.substring(this._code_length); search_keys = search_keys.split(':'); @@ -80,6 +90,7 @@ define(['dojo/_base/declare','dojo/_base/lang', './Provider'], /** * Finds object with attr_name === value in array defined by key. + * @protected */ _find: function(array, attr, value, all) { diff --git a/install/ui/src/freeipa/_base/Singleton_registry.js b/install/ui/src/freeipa/_base/Singleton_registry.js index a3427d7e0..e0c55ca14 100644 --- a/install/ui/src/freeipa/_base/Singleton_registry.js +++ b/install/ui/src/freeipa/_base/Singleton_registry.js @@ -26,26 +26,25 @@ define(['dojo/_base/declare', './Construct_registry' ], function(declare, array, lang, construct, Builder, Construct_registry) { + /** + * Registry for storing singleton instances of various items based + * on their type. + * + * @class _base.Singleton_registry + */ var Singleton_registry = declare(null, { - /** - * Registry for storing singleton instances of various items based - * on their type. - * - * @class - * @name Singleton_registry - */ /** * Internal map for instances * @protected - * @type Object + * @property {Object} */ _map: {}, /** * Builder used for building new instances. Builder has to have a * Constructor registry set. - * @type Builder + * @property {_base.Builder} */ builder: null, @@ -55,8 +54,8 @@ define(['dojo/_base/declare', * * When an object is passed in, the function returns it. * - * @param type {String|Object} Type's name. Or the the object itself. - * @returns Object|null + * @param {string|Object} type Type's name. Or the the object itself. + * @return {Object|null} */ get: function(type) { @@ -79,8 +78,8 @@ define(['dojo/_base/declare', /** * Set object of given type - overwrites existing * - * @param {String} type - * @param {anything} object + * @param {string} type + * @param {Mixed} object */ set: function (type, obj) { this._map[type] = obj; @@ -89,7 +88,7 @@ define(['dojo/_base/declare', /** * Removes object of given type from registry * - * @param {String} type + * @param {string} type */ remove: function(type) { @@ -100,11 +99,11 @@ define(['dojo/_base/declare', /** * Registers construction specification * - * @param type {String|Object} type or construction spec - * @param func {Function} ctor or factory function - * @param [default_spec] {Object} default spec object for given type + * @param {string|Object} type type or construction spec + * @param {Function} func ctor or factory function + * @param {Object} [default_spec] default spec object for given type * - * @returns Object + * @return {Object} */ register: function(type, func, default_spec) { if (!lang.exists('builder.registry', this)) { diff --git a/install/ui/src/freeipa/_base/Spec_mod.js b/install/ui/src/freeipa/_base/Spec_mod.js index 60d4b9229..34f79f726 100644 --- a/install/ui/src/freeipa/_base/Spec_mod.js +++ b/install/ui/src/freeipa/_base/Spec_mod.js @@ -22,16 +22,23 @@ define(['dojo/_base/declare', 'dojo/_base/lang' ], function(declare, lang) { + /** + * Utility for common modification of specification objects + * + * @class _base.Spec_mod + */ var Spec_mod = declare(null, { /** * Modifies spec according to rules defined in diff object. * - * Diff should have following structure: { - * $add: array of add rules - * $del: array of del rules - * $set: array of set rules - * } + * Diff should have structure as follows: + * + * { + * $add: array of add rules + * $del: array of del rules + * $set: array of set rules + * } * * The order of modification is del, add, set. * @@ -54,8 +61,10 @@ define(['dojo/_base/declare', * Adds objects according to rules to array. * * A rule is a triple of path and a object and position to add: - * ['path.to.spec.array', {}, position] * + * ['path.to.spec.array', {}, position] + * @param {Object} spec + * @param {Array} rules */ add: function(spec, rules) { @@ -66,9 +75,11 @@ define(['dojo/_base/declare', * Deletes objects according to rules from an array. * * A rule is a pair of path and delete conditions: - * ['path.to.spec.array', [ { name: 'foo'}, { name: 'baz'} ]] * - * Deletes all objects with name 'baz' or 'foo'. + * ['path.to.spec.array', [ { name: 'foo'}, { name: 'baz'} ]] + * // Deletes all objects with name 'baz' or 'foo'. + * @param {Object} spec + * @param {Array} rules */ del: function(spec, rules) { @@ -77,7 +88,10 @@ define(['dojo/_base/declare', /** * A rule is a pair of path and a object to set. + * * ['path.to.spec.property', {}] + * @param {Object} spec + * @param {Array} rules */ set: function(spec, rules) { @@ -86,6 +100,7 @@ define(['dojo/_base/declare', /** * Removes all rule props + * @param {Object} diff */ del_rules: function(diff) { delete diff.$add; diff --git a/install/ui/src/freeipa/_base/construct.js b/install/ui/src/freeipa/_base/construct.js index 960596da2..ce675e588 100644 --- a/install/ui/src/freeipa/_base/construct.js +++ b/install/ui/src/freeipa/_base/construct.js @@ -23,14 +23,17 @@ define(['dojo/_base/declare', 'dojo/_base/lang' ], function(declare, array, lang) { + /** + * Helper module + * @class _base.construct + * @singleton + */ var construct = { - /** - * Helper modules - */ /** - * Checks if supplied object is a construtor function. - * It can recognize only classes declared by ''dojo/_base/declare''. + * Checks if supplied object is a constructor function. + * It can recognize only classes declared by `dojo/_base/declare`. + * @param {Object} obj */ is_ctor: function(obj) { @@ -38,13 +41,14 @@ define(['dojo/_base/declare', }, /** - * Finds out if object is a spec object. + * Finds out if object is a spec object. * - * Object is not a spec object when any of following applies: - * * has __fw_obj === true - * * has isInstanceOf function - basically tells if it's a instance of - * dojo-based class + * Object is not a spec object when any of following applies: * + * - has `__fw_obj === true` + * - has `isInstanceOf` function - basically tells if it's a instance of + * dojo-based class + * @param {Object} obj */ is_spec: function(obj) { var ret = false; @@ -56,13 +60,14 @@ define(['dojo/_base/declare', }, /** - * Deep clone. - * - does not clone framework objects - * - fails on cyclic non-framework objects + * Deep clone + * + * - does not clone framework objects + * - fails on cyclic non-framework objects * - * based on dojo/_base/lang.clone + * based on `dojo/_base/lang.clone` * - * @param {anything} object to clone + * @param {Mixed} src object to clone */ clone: function(src) { diff --git a/install/ui/src/freeipa/_base/i18n.js b/install/ui/src/freeipa/_base/i18n.js index 970705bba..ae9b40bca 100644 --- a/install/ui/src/freeipa/_base/i18n.js +++ b/install/ui/src/freeipa/_base/i18n.js @@ -21,11 +21,14 @@ /** * Gets translated message. * -* If a message starts with @i18n: it tries to get the message from +* If a message starts with `@i18n`: it tries to get the message from * message object. If it doesn't contain a string with * the key it returns alternate string. * * It all other cases the message itself or empty string is returned. +* @class _base.i18n +* @extends _base.Provider +* @singleton */ define(['dojo/_base/lang', './Provider'], function(lang, Provider) { diff --git a/install/ui/src/freeipa/aci.js b/install/ui/src/freeipa/aci.js index 3ca10dbfb..67dc46e3e 100644 --- a/install/ui/src/freeipa/aci.js +++ b/install/ui/src/freeipa/aci.js @@ -32,6 +32,16 @@ define([ './entity'], function(metadata_provider, IPA, $, phases, reg, text) { +/** + * Widgets, entities and fields related to Access Control that means + * Permissions, Privilege, Role, Delegation and Self-service. + * + * When loaded, this module is also accessible as `IPA.aci`. + * + * @class aci + * @alternateClassName IPA.aci + * @singleton + */ var exp = IPA.aci = {}; var make_permission_spec = function() { @@ -231,6 +241,10 @@ return { } };}; +/** + * @class aci.permission_details_facet + * @extends details.details_facet + */ IPA.aci.permission_details_facet = function(spec) { var that = IPA.details_facet(spec); @@ -463,7 +477,10 @@ return { } };}; - +/** + * @class IPA.attributes_widget + * @extends IPA.checkboxes_widget + */ IPA.attributes_widget = function(spec) { spec = spec || {}; @@ -609,6 +626,10 @@ IPA.attributes_widget = function(spec) { return that; }; +/** + * @class IPA.rights_widget + * @extends IPA.checkboxes_widget + */ IPA.rights_widget = function(spec) { var that = IPA.checkboxes_widget(spec); @@ -622,6 +643,10 @@ IPA.rights_widget = function(spec) { return that; }; +/** + * @class IPA.permission_target_widget + * @extends IPA.details_table_section_nc + */ IPA.permission_target_widget = function(spec) { spec = spec || {}; @@ -737,6 +762,11 @@ IPA.permission_target_widget = function(spec) { return that; }; +/** + * Permission target policy + * @class IPA.permission_target_policy + * @extends IPA.facet_policy + */ IPA.permission_target_policy = function (spec) { var that = IPA.facet_policy(); @@ -894,12 +924,40 @@ IPA.permission_target_policy = function (spec) { return that; }; +/** + * Permission entity spec + * @member aci + */ exp.permission_entity_spec = make_permission_spec(); + +/** + * Privilege entity spec + * @member aci + */ exp.privilege_entity_spec = make_privilege_spec(); + +/** + * Role entity spec + * @member aci + */ exp.role_entity_spec = make_role_spec(); + +/** + * Self-service entity spec + * @member aci + */ exp.selfservice_entity_spec = make_selfservice_spec(); + +/** + * Delegation entity spec + * @member aci + */ exp.delegation_entity_spec = make_delegation_spec(); +/** + * Register entities, widgets and fields to global registers. + * @member aci + */ exp.register = function() { var e = reg.entity; var w = reg.widget; diff --git a/install/ui/src/freeipa/add.js b/install/ui/src/freeipa/add.js index eb7ce38dc..00d499da5 100644 --- a/install/ui/src/freeipa/add.js +++ b/install/ui/src/freeipa/add.js @@ -22,6 +22,12 @@ define(['./ipa', './jquery', './navigation', './text', './field', './widget', './dialog'], function(IPA, $, navigation, text) { +/** + * Entity adder dialog + * @class + * @extends IPA.dialog + * @mixins IPA.confirm_mixin + */ IPA.entity_adder_dialog = function(spec) { spec = spec || {}; @@ -32,14 +38,34 @@ IPA.entity_adder_dialog = function(spec) { IPA.confirm_mixin().apply(that); + /** @property {string} method="add" API method for add command */ that.method = spec.method || 'add'; + /** @property {Function} on_error Custom add error handler */ that.on_error = spec.on_error ; + /** @property {boolean} retry=true Allow retry on error (same as in IPA.command)*/ that.retry = typeof spec.retry !== 'undefined' ? spec.retry : true; + /** + * Add command + * @property {IPA.command} + * @protected + */ that.command = null; + /** @property {IPA.observer} added Added event */ that.added = IPA.observer(); + /** @property {string} subject Name of added subject (usually entity label) */ that.subject = spec.subject || that.entity.metadata.label_singular; + /** + * Pkeys of containing entities to use in add command when adding nested entity + * @property {string[]} + */ that.pkey_prefix = spec.pkey_prefix || []; + /** + * Custom logic for navigation to edit page in case of 'Add and Edit' + * @property {Function} + * @param {entity.entity} entity + * @param {Object} result + */ that.show_edit_page = spec.show_edit_page || show_edit_page; var init = function() { @@ -94,6 +120,10 @@ IPA.entity_adder_dialog = function(spec) { }); }; + /** + * Invokes simple add + * @protected + */ that.on_add = function() { that.hide_message(); @@ -106,15 +136,29 @@ IPA.entity_adder_dialog = function(spec) { that.on_error); }; + /** + * Confirm handler + * @protected + */ that.on_confirm = function() { that.on_add(); }; + /** + * Get success notification message text + * @protected + * @param {Object} data Add command result + */ that.get_success_message = function(data) { var message = text.get('@i18n:dialogs.add_confirmation'); return message.replace('${entity}', that.subject); }; + /** + * Show success notification + * @protected + * @param {Object} data Add command result + */ that.notify_success = function(data) { IPA.notify_success(that.get_success_message(data)); }; @@ -131,6 +175,11 @@ IPA.entity_adder_dialog = function(spec) { navigation.show_entity(that.entity.name, 'default', pkeys); } + /** + * Create add command + * @protected + * @param {Object} record Saved data + */ that.create_add_command = function(record) { var pkey_name = that.entity.metadata.primary_key; @@ -163,6 +212,11 @@ IPA.entity_adder_dialog = function(spec) { return command; }; + /** + * Execute add command + * @param {Function} on_success + * @param {Function} on_error + */ that.add = function(on_success, on_error) { if (!that.validate()) return; @@ -177,6 +231,7 @@ IPA.entity_adder_dialog = function(spec) { that.command.execute(); }; + /** @inheritDoc */ that.create = function() { that.dialog_create(); diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index 37c4c08a8..2fbe0775c 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -18,9 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Application wrapper - */ define([ //core 'dojo/_base/lang', @@ -56,6 +53,14 @@ define([ 'dojo/domReady!' ],function(lang, Deferred, when, plugin_loader, phases, Application_controller, exports) { + /** + * Application wrapper + * + * Prepares application controller and registers phases. + * + * @class app + * @singleton + */ var app = { /** diff --git a/install/ui/src/freeipa/association.js b/install/ui/src/freeipa/association.js index ad427d66b..8709b9045 100644 --- a/install/ui/src/freeipa/association.js +++ b/install/ui/src/freeipa/association.js @@ -38,8 +38,17 @@ define([ function(Deferred, metadata_provider, IPA, $, navigation, phases, reg, su, text) { +/** + * Association module + * @class association + * @singleton + */ var exp = {}; +/** + * Associator base class + * @class + */ IPA.associator = function (spec) { spec = spec || {}; @@ -65,7 +74,10 @@ IPA.associator = function (spec) { /** + * Serial associator * This associator is built for the case where each association requires a separate rpc + * @class + * @extends IPA.associator */ IPA.serial_associator = function(spec) { @@ -112,6 +124,7 @@ IPA.serial_associator = function(spec) { /** * This associator is for the common case where all the asociations can be sent * in a single rpc + * @class */ IPA.bulk_associator = function(spec) { @@ -148,6 +161,8 @@ IPA.bulk_associator = function(spec) { /** * This dialog is for adding value of multivalued attribute which behaves like * association attribute. + * @class + * @extends IPA.entity_adder_dialog */ IPA.attribute_adder_dialog = function(spec) { @@ -206,6 +221,8 @@ IPA.attribute_adder_dialog = function(spec) { /** * This dialog is used for adding associations between two entities. + * @class + * @extends IPA.adder_dialog */ IPA.association_adder_dialog = function(spec) { @@ -310,6 +327,8 @@ IPA.association_adder_dialog = function(spec) { /** * This dialog is used for removing associations between two entities. + * @class + * @extends IPA.deleter_dialog */ IPA.association_deleter_dialog = function (spec) { @@ -347,7 +366,10 @@ IPA.association_deleter_dialog = function (spec) { return that; }; - +/** + * Association config + * @class + */ IPA.association_config = function (spec) { spec = spec || {}; @@ -362,6 +384,11 @@ IPA.association_config = function (spec) { return that; }; +/** + * Association table widget + * @class + * @extends IPA.table_widget + */ IPA.association_table_widget = function (spec) { spec = spec || {}; @@ -735,6 +762,11 @@ IPA.association_table_widget = function (spec) { return that; }; +/** + * Association table field + * @class + * @extends IPA.field + */ IPA.association_table_field = function (spec) { spec = spec || {}; @@ -771,6 +803,10 @@ IPA.association_table_field = function (spec) { return that; }; +/** + * Association facet pre-op + * @member association + */ exp.association_facet_pre_op = function(spec, context) { var has_indirect_attribute_member = function(spec) { @@ -880,6 +916,12 @@ exp.association_facet_pre_op = function(spec, context) { return spec; }; +/** + * Association facet + * @class association.association_facet + * @alternateClassName IPA.association_facet + * @extends facet.table_facet + */ exp.association_facet = IPA.association_facet = function (spec, no_init) { spec = spec || {}; @@ -1223,6 +1265,10 @@ exp.association_facet = IPA.association_facet = function (spec, no_init) { return that; }; +/** + * Attribute facet pre-op + * @member association + */ exp.attribute_facet_pre_op = function(spec, context) { var entity = context.entity; @@ -1295,6 +1341,12 @@ exp.attribute_facet_pre_op = function(spec, context) { return spec; }; +/** + * Association facet + * @class association.attribute_facet + * @alternateClassName IPA.attribute_facet + * @extends facet.table_facet + */ exp.attribute_facet = IPA.attribute_facet = function(spec, no_init) { spec = spec || {}; @@ -1455,7 +1507,13 @@ exp.attribute_facet = IPA.attribute_facet = function(spec, no_init) { return that; }; -IPA.sid_facet = function(spec, no_init) { +/** + * SID facet + * @class association.sid_facet + * @alternateClassName IPA.sid_facet + * @extends association.attribute_facet + */ +exp.sid_facet = IPA.sid_facet = function(spec, no_init) { spec.name = spec.name || 'sid_facet'; @@ -1501,7 +1559,11 @@ IPA.sid_facet = function(spec, no_init) { return that; }; - +/** + * Attriute read-only evaluator + * @class IPA.attr_read_only_evaluator + * @extends IPA.state_evaluator + */ IPA.attr_read_only_evaluator = function(spec) { spec.name = spec.name || 'attr_read_only_evaluator'; diff --git a/install/ui/src/freeipa/builder.js b/install/ui/src/freeipa/builder.js index 0a8c39921..6d2086917 100644 --- a/install/ui/src/freeipa/builder.js +++ b/install/ui/src/freeipa/builder.js @@ -18,12 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Global builder interface. - * - * Contains a map of builders for a specific object type. - * - */ define(['dojo/_base/declare', 'dojo/_base/array', 'dojo/_base/lang', @@ -34,13 +28,25 @@ define(['dojo/_base/declare', var builder_registry = new Singleton_registry(); builder_registry.builder.ctor = Builder; + /** + * Global builder interface. + * + * Contains a map of builders for a specific object type. + * + * @class builder + * @singleton + */ var exp = { + /** + * Registry of builders + * @property {_base.Singleton_registry} + */ builders: builder_registry, /** * Get builder for object type * - * @param {String} object type + * @param {string} object type */ get: function(obj_type) { return this.builders.get(obj_type); @@ -49,8 +55,8 @@ define(['dojo/_base/declare', /** * Set builder for object type. * - * @param {String} object type - * @param {Builder} builder + * @param {string} object type + * @param {_base.Builder} builder */ set: function(obj_type, builder) { this.builders.set(obj_type, builder); @@ -59,8 +65,8 @@ define(['dojo/_base/declare', /** * Build object by builder for given object type. * - * @param {String} object type. Uses generic builder if empty string. - * @param {String|Object|Function} spec + * @param {string} object type. Uses generic builder if empty string. + * @param {string|Object|Function} spec * @param {Object|null} context * @param {Object|null} build overrides */ diff --git a/install/ui/src/freeipa/config.js b/install/ui/src/freeipa/config.js index 8aa222cfd..632bc136d 100644 --- a/install/ui/src/freeipa/config.js +++ b/install/ui/src/freeipa/config.js @@ -18,12 +18,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Application configuration - */ + define([], function() { + /** + * Application configuration + * @class config + * @singleton + */ var config = { /** diff --git a/install/ui/src/freeipa/details.js b/install/ui/src/freeipa/details.js index 149a04e43..687926c8c 100644 --- a/install/ui/src/freeipa/details.js +++ b/install/ui/src/freeipa/details.js @@ -21,8 +21,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* IPA Object Details - populating definiton lists from entry data */ - define([ 'dojo/_base/lang', './builder', @@ -36,22 +34,75 @@ define([ './add'], function(lang, builder, IPA, $, phases, reg, su, text) { +/** + * Details module + * + * @class details + * @singleton + */ var exp = {}; +/** + * Name of expanded icon + * @member details + */ exp.expanded_icon = IPA.expanded_icon = 'expanded-icon'; + +/** + * Name of collapsed icon + * @member details + */ exp.collapsed_icon = IPA.collapsed_icon = 'collapsed-icon'; +/** + * Details builder + * + * Processes containers spec and builds sections, widget and fields according + * to that spec. Container is usually a details facet or a dialog. For its task + * it uses `section_builder`, `widget_builder` and `field_builder` each + * builder can be configured. Otherwise it uses default builders. + * + * @class details.details_builder + * @alternateClassName IPA.details_builder + */ exp.details_builder = IPA.details_builder = function(spec) { var that = IPA.object(); + /** + * Container's widget collection + * @protected + */ that.widgets = spec.container.widgets; + + /** + * Container's field collection + * @protected + */ that.fields = spec.container.fields; + /** + * Widget builder + * @property {IPA.widget_builder} + */ that.widget_builder = spec.widget_builder || IPA.widget_builder(); + + /** + * Fields builder + * @property {IPA.field_builder} + */ that.field_builder = spec.field_builder || IPA.field_builder(); + + /** + * Section builder + * @property {details.section_builder} + */ that.section_builder = spec.section_builder || IPA.section_builder(); + /** + * Build single widget according to its spec and add it to widget collection. + * @param {Object} spec widget spec + */ that.build_widget = function(spec) { if (!spec) return; @@ -59,6 +110,10 @@ exp.details_builder = IPA.details_builder = function(spec) { that.widget_builder.build_widget(spec, that.widgets); }; + /** + * Build multiple widgets and add them to widget collection. + * @param {Array.<Object>} specs widget specs + */ that.build_widgets = function(specs) { if (!specs) return; @@ -66,6 +121,10 @@ exp.details_builder = IPA.details_builder = function(spec) { that.widget_builder.build_widgets(specs, that.widgets); }; + /** + * Build single field and add it to field collection. + * @param {Object} spec field spec + */ that.build_field = function(spec) { if (!spec) return; @@ -73,6 +132,10 @@ exp.details_builder = IPA.details_builder = function(spec) { that.field_builder.build_field(spec, that.fields); }; + /** + * Build multiple fields and add them to field collection. + * @param {Array.<Object>} specs field spec + */ that.build_fields = function(specs) { if (!specs) return; @@ -80,6 +143,10 @@ exp.details_builder = IPA.details_builder = function(spec) { that.field_builder.build_fields(specs, that.fields); }; + /** + * Build sections + * @param {Array.<Object>} specs section specs + */ that.build_sections = function(specs) { if (!specs) return; @@ -87,6 +154,10 @@ exp.details_builder = IPA.details_builder = function(spec) { that.section_builder.build_sections(specs); }; + /** + * Build section, fields and widgets. + * @param {Object} spec facet or dialog spec + */ that.build = function(spec) { if (spec.sections) { @@ -111,18 +182,50 @@ exp.details_builder = IPA.details_builder = function(spec) { return that; }; +/** + * Section builder + * + * Section is a layout unit of details facet or a dialog. + * + * @class details.section_builder + * @alternateClassName IPA.section_builder + */ exp.section_builder = IPA.section_builder = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Container + * @property {IPA.facet|IPA.dialog} + */ that.container = spec.container; + + /** + * Default section factory + * + * TODO: should be modified so it can be a class too + * @property {IPA.composite_widget} + */ that.section_factory = spec.section_factory || IPA.details_table_section; + /** + * Field builder + * @property {IPA.field_builder} + */ that.field_builder = spec.field_builder; + + /** + * Widget builder + * @property {IPA.widget_builder} + */ that.widget_builder = spec.widget_builder; + /** + * Build multiple sections + * @param {Array.<Object>} sections section specs + */ that.build_sections = function(sections) { if(!sections) return; @@ -132,6 +235,12 @@ exp.section_builder = IPA.section_builder = function(spec) { } }; + /** + * Build single section + * @param {Object} section_spec + * @param {number|string} index value which is used in section name if name + * is not specified in spec + */ that.build_section = function(section_spec, index) { section_spec.entity = that.container.entity; section_spec.facet = that.container; @@ -152,13 +261,23 @@ exp.section_builder = IPA.section_builder = function(spec) { that.create_fields(section, section_spec.fields); }; - that.create_fields = function(section, fields_spec) { + /** + * Create fields and associated widgets + * @param {IPA.composite_widget} section + * @param {Array.<Object>} field_specs + */ + that.create_fields = function(section, fields_specs) { - for (var i=0; i < fields_spec.length; i++) { - that.create_field(section, fields_spec[i]); + for (var i=0; i < fields_specs.length; i++) { + that.create_field(section, fields_specs[i]); } }; + /** + * Create field and associated widget + * @param {IPA.composite_widget} section + * @param {Object} field_spec + */ that.create_field = function(section, field_spec) { var widget = that.widget_builder.build_widget(field_spec, section.widgets); @@ -176,35 +295,89 @@ exp.section_builder = IPA.section_builder = function(spec) { return that; }; +/** + * Facet policy + * + * Object which extends container's (facet or dialog) logic. + * + * @class details.facet_policy + * @alternateClassName IPA.facet_policy + */ exp.facet_policy = IPA.facet_policy = function() { var that = IPA.object(); + /** + * Container + * @property {IPA.facet|IPA.dialog} + */ + that.container = null; + + /** + * Init handler. + * + * Should be executed in container's init. + */ that.init = function() { }; + /** + * Post Create Handler + * + * Should be executed at the end of container's create + */ that.post_create = function() { }; + /** + * Post Load Handler + * + * Should be executed at the end of container's load + * @param {Object} data + */ that.post_load = function(data) { }; return that; }; +/** + * Facet policy collection + * + * @class details.facet_policies + * @alternateClassName IPA.facet_policies + */ exp.facet_policies = IPA.facet_policies = function(spec) { var that = IPA.object(); + /** + * Container + * @property {IPA.facet|IPA.dialog} + */ that.container = spec.container; + + /** + * Facet Policies + * @readonly + * @property {Array.<details.facet_policy>} + */ that.policies = []; + /** + * Add policy + * @param {details.facet_policy} policy + */ that.add_policy = function(policy) { policy.container = that.container; that.policies.push(policy); }; + /** + * Add multiple policies + * @param {Array.<details.facet_policy>} policies + */ that.add_policies = function(policies) { if (!policies) return; @@ -214,6 +387,11 @@ exp.facet_policies = IPA.facet_policies = function(spec) { } }; + /** + * Init handler + * + * Calls init handlers of all policies + */ that.init = function() { for (var i=0; i<that.policies.length; i++) { @@ -221,6 +399,11 @@ exp.facet_policies = IPA.facet_policies = function(spec) { } }; + /** + * Post Create handler + * + * Calls post create handlers of all policies + */ that.post_create = function() { for (var i=0; i<that.policies.length; i++) { @@ -228,6 +411,11 @@ exp.facet_policies = IPA.facet_policies = function(spec) { } }; + /** + * Post Load handler + * + * Calls post load handlers of all policies + */ that.post_load = function(data) { for (var i=0; i<that.policies.length; i++) { @@ -242,6 +430,19 @@ exp.facet_policies = IPA.facet_policies = function(spec) { return that; }; +/** + * Details facet build pre_op + * + * It + * - sets name, title, label if not present + * - adds default actions and related buttons + * - refresh + * - reset + * - update + * - adds dirty state evaluator + * + * @member details + */ exp.details_facet_pre_op = function(spec, context) { var entity = context.entity; @@ -286,22 +487,81 @@ exp.details_facet_pre_op = function(spec, context) { return spec; }; +/** + * Details facet + * @class details.details_facet + * @alternateClassName IPA.details_facet + * @extends facet.facet + */ exp.details_facet = IPA.details_facet = function(spec, no_init) { spec = spec || {}; var that = IPA.facet(spec, true); + /** + * Entity + * @property {IPA.entity} + */ that.entity = IPA.get_entity(spec.entity); + + /** + * Name of update command + * + * - defaults to 'mod' + * @property {string} + */ that.update_command_name = spec.update_command_name || 'mod'; + + /** + * Command mode + * Command mode determines how update information on update is collected. + * There are two modes: + * + * - `save` this uses field's `save()` method + * - `info` works with `details.update_info`. Update info is collected by + * `get_update_info()` method. + * @property {string} + */ that.command_mode = spec.command_mode || 'save'; // [save, info] + + /** + * Check rights + * + * Controls obtaining of attribute level rights on refresh and update + * + * @property {boolean} + */ that.check_rights = spec.check_rights !== undefined ? spec.check_rights : true; + /** + * Facet label + * @property {string} + */ that.label = text.get(spec.label) || text.get('facets.details'); + + /** + * Facet group + * @property {string} + */ that.facet_group = spec.facet_group || 'settings'; + /** + * Widgets + * @property {IPA.widget_container} + */ that.widgets = IPA.widget_container(); + + /** + * Fields + * @property {IPA.field_container} + */ that.fields = IPA.field_container({ container: that }); + + /** + * Policies + * @property {IPA.facet_policies} + */ that.policies = IPA.facet_policies({ container: that, policies: spec.policies @@ -315,9 +575,23 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { that.fields.container_add_field(field); }; + /** + * Dirty + * + * - true if any field is dirty + * @property {boolean} + */ that.dirty = false; + + /** + * Dirty changed + * @event + */ that.dirty_changed = IPA.observer(); + /** + * @inheritDoc + */ that.create = function(container) { if (that.entity.facets.length == 1) { if (that.disable_breadcrumb === undefined) { @@ -332,11 +606,19 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { that.policies.post_create(); }; + /** + * Create header controls + * + * - ie control buttons + */ that.create_controls = function() { that.create_control_buttons(that.controls); }; + /** + * @inheritDoc + */ that.create_header = function(container) { that.facet_create_header(container); @@ -389,6 +671,9 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { container.append('<hr/>'); }; + /** + * @inheritDoc + */ that.create_content = function(container) { that.content = $('<div/>', { @@ -403,12 +688,23 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { }).appendTo(container); }; + /** + * @inheritDoc + */ that.show = function() { that.facet_show(); var pkey = that.get_pkey(); that.header.set_pkey(pkey); }; + /** + * Field's dirty event handler + * + * Sets this dirty state + * + * @protected + * @param {boolean} dirty + */ that.field_dirty_changed = function(dirty) { var old_dirty = that.dirty; @@ -424,6 +720,12 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { } }; + /** + * Evaluates if facet is dirty. + * + * Facet is dirty if any child widget is dirty. + * @return {boolean} dirty + */ that.is_dirty = function() { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { @@ -434,6 +736,9 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return false; }; + /** + * @inheritDoc + */ that.load = function(data) { that.facet_load(data); @@ -447,6 +752,10 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { that.clear_expired_flag(); }; + /** + * Save fields' values into record + * @param {Object} record + */ that.save = function(record) { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { @@ -455,6 +764,19 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { } }; + /** + * Creates update info + * + * - used when in 'save' command mode + * + * This update info consists of fields' update information + * + * @param {boolean} [only_dirty=false] collect update information only from + * dirty fields + * @param {boolean} [require_value=false] collect update information from + * fields which has value + * @return {details.update_info} + */ that.save_as_update_info = function(only_dirty, require_value) { var record = {}; @@ -477,6 +799,9 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return update_info; }; + /** + * Reset facet + */ that.reset = function() { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { @@ -485,7 +810,10 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { } }; - + /** + * Validate all fields + * @return {boolean} all fields are valid + */ that.validate = function() { var valid = true; var fields = that.fields.get_fields(); @@ -496,6 +824,10 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return valid; }; + /** + * Notifies successful update + * @protected + */ that.nofify_update_success = function() { var msg = text.get('@i18n:details.updated'); var key = that.get_pkey(); @@ -504,16 +836,45 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { IPA.notify_success(msg); }; - + /** + * Update success handler + * + * Invokes Load by default. + * + * This is the method to override if different actions need to be taken + * on update success. + * + * @protected + * @param {Object} data + * @param {string} text_status + * @param {XMLHttpRequest} xhr + */ that.update_on_success = function(data, text_status, xhr) { that.load(data); that.on_update.notify(); that.nofify_update_success(); }; + /** + * Update error handler + * + * This is the method to override if different actions need to be taken + * on update error. + * + * @protected + * @param {XMLHttpRequest} xhr + * @param {string} text_status + * @param {Object} error_thrown + */ that.update_on_error = function(xhr, text_status, error_thrown) { }; + /** + * Adds update info as command options + * @protected + * @param {details.update_info} update_info + * @param {IPA.command} command + */ that.add_fields_to_command = function(update_info, command) { for (var i=0; i < update_info.fields.length; i++) { @@ -527,6 +888,12 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { } }; + /** + * Create update command based on field part of update info + * @protected + * @param {details.update_info} update_info + * @return {IPA.command} + */ that.create_fields_update_command = function(update_info) { var args = that.get_pkeys(); @@ -547,6 +914,15 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return command; }; + /** + * Create batch command from update info + * + * Created batch command consists of update info's commands and a mod command + * to reflect field part of update info (if present). + * @protected + * @param {details.update_info} update_info + * @return {IPA.batch_command} + */ that.create_batch_update_command = function(update_info) { var batch = IPA.batch_command({ @@ -571,6 +947,10 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return batch; }; + /** + * Show validation error + * @protected + */ that.show_validation_error = function() { var dialog = IPA.message_dialog({ name: 'validation_error', @@ -580,6 +960,11 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { dialog.open(); }; + /** + * Create update command + * @protected + * @return {IPA.command/IPA.batch_command} + */ that.create_update_command = function() { var command, update_info; @@ -601,6 +986,14 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return command; }; + /** + * Perform update operation + * + * Update reflects current state into data store. + * + * @param {Function} on_success success handler + * @param {Function} on_error error handler + */ that.update = function(on_success, on_error) { var command = that.create_update_command(); @@ -618,10 +1011,20 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { command.execute(); }; + /** + * Get refresh command name + * @protected + * @return {string} + */ that.get_refresh_command_name = function() { return that.entity.name+'_show'; }; + /** + * Create refresh command + * @protected + * @return {IPA.command} + */ that.create_refresh_command = function() { var options = { all: true }; @@ -641,16 +1044,33 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return command; }; + /** + * Refresh success handler + * @protected + * @param {Object} data + * @param {string} text_status + * @param {XMLHttpRequest} xhr + */ that.refresh_on_success = function(data, text_status, xhr) { that.load(data); that.show_content(); }; + /** + * Refresh error handler + * @protected + * @param {XMLHttpRequest} xhr + * @param {string} text_status + * @param {Object} error_thrown + */ that.refresh_on_error = function(xhr, text_status, error_thrown) { that.redirect_error(error_thrown); that.report_error(error_thrown); }; + /** + * @inheritDoc + */ that.refresh = function(on_success, on_error) { if (!that.get_pkey() && that.entity.redirect_facet) { @@ -673,12 +1093,22 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { command.execute(); }; + /** + * @inheritDoc + */ that.clear = function() { that.header.clear(); that.widgets.clear(); }; + /** + * Create update info + * + * - used in `update_info` command mode + * @protected + * @return {details.update_info} + */ that.get_update_info = function() { var update_info = IPA.update_info_builder.new_update_info(); @@ -695,6 +1125,10 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return update_info; }; + /** + * Create builders needed for initialization + * @protected + */ that.create_builder = function() { var widget_builder = IPA.widget_builder({ @@ -723,6 +1157,13 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { }); }; + /** + * Initialize details facet + * + * - called automatically if `no_init==true` is not present + * + * @protected + */ that.init_details_facet = function() { that.init_facet(); @@ -743,18 +1184,42 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { return that; }; +/** + * Update info + * @class details.update_info + * @alternateClassName IPA.update_info + */ exp.update_info = IPA.update_info = function(spec) { var that = IPA.object(); + /** + * Fields update info + * @property {Array.<details.field_info>} + */ that.fields = spec.fields || []; + + /** + * Update commands info + * @property {Array.<details.command_info>} + */ that.commands = spec.commands || []; + /** + * Create new field info and add it to collection + * @param {IPA.field} field + * @param {Object} value field's value + */ that.append_field = function(field, value) { var field_info = IPA.update_info_builder.new_field_info(field, value); that.fields.push(field_info); }; + /** + * Create new command info and add it to collection + * @param {IPA.command} command + * @param {number} priority + */ that.append_command = function (command, priority) { var command_info = IPA.update_info_builder.new_command_info(command, priority); that.commands.push(command_info); @@ -763,30 +1228,72 @@ exp.update_info = IPA.update_info = function(spec) { return that; }; +/** + * Command info + * @class details.command_info + * @alternateClassName IPA.command_info + */ exp.command_info = IPA.command_info = function(spec) { var that = IPA.object(); + /** + * Command + * @property {IPA.command} + */ that.command = spec.command; + + /** + * Priority + * + * - controls command execution order + * @property {number} + */ that.priority = spec.priority || IPA.config.default_priority; return that; }; +/** + * Field update info + * @class details.field_info + * @alternateClassName IPA.field_info + */ exp.field_info = IPA.field_info = function(spec) { var that = IPA.object(); + /** + * Field + * @property {IPA.field} + */ that.field = spec.field; + + /** + * Value + * @property {Object} + */ that.value = spec.value; return that; }; +/** + * Update info builder + * @class details.update_info_builder + * @alternateClassName IPA.update_info_builder + * @singleton + */ exp.update_info_builder = IPA.update_info_builder = function() { var that = IPA.object(); + /** + * Create update info from field and command infos + * @param {Array.<details.field_info>} fields + * @param {Array.<details.command_info>} commands + * @return {details.update_info} + */ that.new_update_info = function (fields, commands) { return IPA.update_info({ fields: fields, @@ -794,6 +1301,12 @@ exp.update_info_builder = IPA.update_info_builder = function() { }); }; + /** + * Create field info + * @param {details.field_info} field + * @param {Object} value + * @return {details.field_info} + */ that.new_field_info = function(field, value) { return IPA.field_info({ field: field, @@ -801,6 +1314,12 @@ exp.update_info_builder = IPA.update_info_builder = function() { }); }; + /** + * Create new command info + * @param {IPA.command} command + * @param {number} priority + * @return {details.command_info} + */ that.new_command_info = function(command, priority) { return IPA.command_info({ command: command, @@ -808,12 +1327,23 @@ exp.update_info_builder = IPA.update_info_builder = function() { }); }; + /** + * Merge two commands info into new one + * @param {details.command_info} a + * @param {details.command_info} b + * @return {details.command_info} + */ that.merge = function(a, b) { return that.new_update_info( a.fields.concat(b.fields), a.commands.concat(b.commands)); }; + /** + * Create copy of command info + * @param {details.command_info} original + * @return {details.command_info} copy + */ that.copy = function(original) { return that.new_update_info( original.fields.concat([]), @@ -823,10 +1353,23 @@ exp.update_info_builder = IPA.update_info_builder = function() { return that; }(); +/** + * Field add/mod command builder + * + * @class details.command_builder + * @alternateClassName IPA.command_builder + * @singleton + */ exp.command_builder = IPA.command_builder = function() { var that = IPA.object(); + /** + * Add option to command with field values + * @param {IPA.command} command + * @param {IPA.field} field + * @param {Array} values + */ that.add_field_option = function(command, field, values) { if (!field || !values) return; @@ -854,6 +1397,12 @@ exp.command_builder = IPA.command_builder = function() { return that; }(); +/** + * No-op action which serves only for displaying label + * @class details.select_action + * @alternateClassName IPA.select_action + * @extends facet.action + */ exp.select_action = IPA.select_action = function(spec) { spec = spec || {}; @@ -868,6 +1417,12 @@ exp.select_action = IPA.select_action = function(spec) { return that; }; +/** + * Invokes `facet.refresh` + * @class details.refresh_action + * @alternateClassName IPA.refresh_action + * @extends facet.action + */ exp.refresh_action = IPA.refresh_action = function(spec) { spec = spec || {}; @@ -883,6 +1438,12 @@ exp.refresh_action = IPA.refresh_action = function(spec) { return that; }; +/** + * Invokes `facet.reset` + * @class details.reset_action + * @alternateClassName IPA.reset_action + * @extends facet.action + */ exp.reset_action = IPA.reset_action = function(spec) { spec = spec || {}; @@ -899,6 +1460,12 @@ exp.reset_action = IPA.reset_action = function(spec) { return that; }; +/** + * Invokes validation and then `facet.update` + * @class details.update_action + * @alternateClassName IPA.update_action + * @extends facet.action + */ exp.update_action = IPA.update_action = function(spec) { spec = spec || {}; @@ -922,6 +1489,14 @@ exp.update_action = IPA.update_action = function(spec) { return that; }; +/** + * Sets state based on value of loaded boolean attribute. + * - evaluated on post load by default + * + * @class details.boolean_state_evaluator + * @alternateClassName IPA.boolean_state_evaluator + * @extends facet.state_evaluator + */ exp.boolean_state_evaluator = IPA.boolean_state_evaluator = function(spec) { spec = spec || {}; @@ -930,23 +1505,56 @@ exp.boolean_state_evaluator = IPA.boolean_state_evaluator = function(spec) { var that = IPA.state_evaluator(spec); + /** + * @inheritDoc + */ that.name = spec.name || 'boolean_state_evaluator'; - that.field = spec.field; + + /** + * Attribute's name + * + * - spec name: `field` + * @property {string} + */ + that.field_name = spec.field; + + /** + * State to set when value is `true` + * @property {string} + */ that.true_state = spec.true_state || that.field_name + '-true'; + + /** + * State to set when value is `false` + * @property {string} + */ that.false_state = spec.false_state || that.field_name + '-false'; + + /** + * Inverted logic + * @property {boolean} + */ that.invert_value = spec.invert_value; + + /** + * Value parser + * @property {IPA.boolean_formatter} + */ that.parser = IPA.build({ $factory: spec.parser || IPA.boolean_formatter, invert_value: that.invert_value }); + /** + * @inheritDoc + */ that.on_event = function(data) { var old_state = that.state; var record = data.result.result; that.state = []; - var value = that.parser.parse(record[that.field]); + var value = that.parser.parse(record[that.field_name]); if (value === true) { that.state.push(that.true_state); @@ -960,6 +1568,21 @@ exp.boolean_state_evaluator = IPA.boolean_state_evaluator = function(spec) { return that; }; +/** + * Evaluates enabled/disabled state + * + * // in facet spec + * evaluators: [ + * { + * $factory: IPA.enable_state_evaluator, + * field: 'ipaenabledflag' + * } + * ], + * + * @class details.enable_state_evaluator + * @alternateClassName IPA.enable_state_evaluator + * @extends details.boolean_state_evaluator + */ exp.enable_state_evaluator = IPA.enable_state_evaluator = function(spec) { spec = spec || {}; @@ -972,14 +1595,31 @@ exp.enable_state_evaluator = IPA.enable_state_evaluator = function(spec) { return that; }; +/** + * Create state for each attribute level right user has for specific attribute + * + * - on post load + * - state value is $(ATTR_NAME)_$(RIGHT) where right is a letter (one of 'rscwo') + * + * @class details.acl_state_evaluator + * @alternateClassName IPA.acl_state_evaluator + * @extends facet.state_evaluator + */ exp.acl_state_evaluator = IPA.acl_state_evaluator = function(spec) { spec.name = spec.name || 'acl_state_evaluator'; spec.event = spec.event || 'post_load'; var that = IPA.state_evaluator(spec); + /** + * Attribute name + * @property {string} + */ that.attribute = spec.attribute; + /** + * @inheritDoc + */ that.on_event = function(data) { var old_state, record, rights, i, state; @@ -1008,16 +1648,45 @@ exp.acl_state_evaluator = IPA.acl_state_evaluator = function(spec) { return that; }; +/** + * Evaluator which sets state when loaded value of specific attribute is equal + * to desired value. + * + * @class details.value_state_evaluator + * @alternateClassName IPA.value_state_evaluator + * @extends facet.state_evaluator + */ exp.value_state_evaluator = IPA.value_state_evaluator = function(spec) { spec.name = spec.name || 'value_state_evaluator'; spec.event = spec.event || 'post_load'; var that = IPA.state_evaluator(spec); + + /** + * Attribute name + * @property {string} + */ that.attribute = spec.attribute; + + /** + * Desired value + * @property {Mixed} + */ that.value = spec.value; + + /** + * State to set + * + * If not set, state is created from attribute name and value: + * `$(ATTR_NAME)_$(VALUE)` + * @property {string} + */ that.representation = spec.representation; + /** + * @inheritDoc + */ that.on_event = function(data) { var old_state, record, state, value, loaded_value; @@ -1037,6 +1706,15 @@ exp.value_state_evaluator = IPA.value_state_evaluator = function(spec) { that.notify_on_change(old_state); }; + /** + * Normalize value + * + * - it's expected that value will be in array (to work with multivalued + * attributes) + * - override point + * @protected + * @return {Mixed} value + */ that.normalize_value = function(original) { var value = original; @@ -1047,6 +1725,16 @@ exp.value_state_evaluator = IPA.value_state_evaluator = function(spec) { return value; }; + /** + * Create state + * + * If `representation` is not set, state is created from attribute name + * and value: + * `$(ATTR_NAME)_$(VALUE)` + * + * @protected + * @return {string} state + */ that.get_state_text = function() { var representation, value; @@ -1064,6 +1752,17 @@ exp.value_state_evaluator = IPA.value_state_evaluator = function(spec) { return that; }; +/** + * Object class evaluator + * + * Set state for each object class which loaded record has. + * + * State name is `oc_$(class)` + * + * @class details.object_class_evaluator + * @alternateClassName IPA.object_class_evaluator + * @extends facet.state_evaluator + */ exp.object_class_evaluator = IPA.object_class_evaluator = function(spec) { spec.name = spec.name || 'object_class_evaluator'; @@ -1072,6 +1771,9 @@ exp.object_class_evaluator = IPA.object_class_evaluator = function(spec) { var that = IPA.state_evaluator(spec); + /** + * @inheritDoc + */ that.on_event = function(data) { var old_state, classes, i; @@ -1091,16 +1793,42 @@ exp.object_class_evaluator = IPA.object_class_evaluator = function(spec) { return that; }; +/** + * Base class for executing specific entity methods + * - command options can be set + * - facet pkeys are set as command arguments + * - entity is fetched from facet + * @class details.object_action + * @alternateClassName IPA.object_action + * @extends facet.action + */ exp.object_action = IPA.object_action = function(spec) { spec = spec || {}; var that = IPA.action(spec); + /** + * Method name + * @property {string} + */ that.method = spec.method; + + /** + * @inheritDoc + */ that.confirm_msg = text.get(spec.confirm_msg || '@i18n:actions.confirm'); + + /** + * Command options + * @property {Object} + */ that.options = spec.options || {}; + /** + * @protected + * @inheritDoc + */ that.execute_action = function(facet, on_success, on_error) { var entity_name = facet.entity.name; @@ -1116,6 +1844,14 @@ exp.object_action = IPA.object_action = function(spec) { }).execute(); }; + /** + * Command success handler + * @protected + * @param {facet.facet} facet + * @param {Object} data + * @param {string} text_status + * @param {XMLHttpRequest} xhr + */ that.on_success = function(facet, data, text_status, xhr) { IPA.notify_success(data.result.summary); @@ -1123,10 +1859,25 @@ exp.object_action = IPA.object_action = function(spec) { facet.refresh(); }; + /** + * Command error handler + * @protected + * @param {facet.facet} facet + * @param {XMLHttpRequest} xhr + * @param {string} text_status + * @param {Object} error_thrown + */ that.on_error = function(facet, xhr, text_status, error_thrown) { facet.refresh(); }; + /** + * Combines given success handler with action success handler so both + * can be called. + * @protected + * @param {facet.facet} facet + * @param {Function} on_success success handler + */ that.get_on_success = function(facet, on_success) { return function(data, text_status, xhr) { that.on_success(facet, data, text_status, xhr); @@ -1134,6 +1885,13 @@ exp.object_action = IPA.object_action = function(spec) { }; }; + /** + * Combines given error handler with action error handler so both + * can be called. + * @protected + * @param {facet.facet} facet + * @param {Function} on_error error handler + */ that.get_on_error = function(facet, on_error) { return function(xhr, text_status, error_thrown) { that.on_error(facet, xhr, text_status, error_thrown); @@ -1141,6 +1899,10 @@ exp.object_action = IPA.object_action = function(spec) { }; }; + /** + * @protected + * @inheritDoc + */ that.get_confirm_message = function(facet) { var pkey = that.get_pkey(); var msg = that.confirm_msg.replace('${object}', pkey); @@ -1152,6 +1914,12 @@ exp.object_action = IPA.object_action = function(spec) { return that; }; +/** + * Call 'enable' method of current entity + * @class details.enable_action + * @alternateClassName IPA.enable_action + * @extends details.object_action + */ exp.enable_action = IPA.enable_action = function(spec) { spec = spec || {}; @@ -1166,6 +1934,12 @@ exp.enable_action = IPA.enable_action = function(spec) { return that; }; +/** + * Call 'disable' method of current entity + * @class details.disable_action + * @alternateClassName IPA.disable_action + * @extends details.object_action + */ exp.disable_action = IPA.disable_action = function(spec) { spec = spec || {}; @@ -1180,6 +1954,15 @@ exp.disable_action = IPA.disable_action = function(spec) { return that; }; +/** + * Call 'delete' method of current entity + * + * Redirects to facet's redirect target on success by default. + * + * @class details.delete_action + * @alternateClassName IPA.delete_action + * @extends details.object_action + */ exp.delete_action = IPA.delete_action = function(spec) { spec = spec || {}; @@ -1207,7 +1990,12 @@ exp.delete_action = IPA.delete_action = function(spec) { return that; }; - +/** + * Summary condition for 'enabled' state + * + * @class details.enabled_summary_cond + * @alternateClassName IPA.enabled_summary_cond + */ exp.enabled_summary_cond = IPA.enabled_summary_cond = function() { var that = IPA.object(); @@ -1220,6 +2008,12 @@ exp.enabled_summary_cond = IPA.enabled_summary_cond = function() { return that; }; +/** + * Summary condition for 'disabled' state + * + * @class details.disabled_summary_cond + * @alternateClassName IPA.disabled_summary_cond + */ exp.disabled_summary_cond = IPA.disabled_summary_cond = function() { var that = IPA.object(); lang.mixin(that, { @@ -1231,6 +2025,11 @@ exp.disabled_summary_cond = IPA.disabled_summary_cond = function() { return that; }; +/** + * Register facet and actions. + * + * @member details + */ exp.register = function() { var a = reg.action; var f = reg.facet; diff --git a/install/ui/src/freeipa/dialog.js b/install/ui/src/freeipa/dialog.js index d1428fecd..a31c9f453 100644 --- a/install/ui/src/freeipa/dialog.js +++ b/install/ui/src/freeipa/dialog.js @@ -22,16 +22,25 @@ define(['./ipa', './jquery', './text', './field', './widget'], function(IPA, $, text) { +/** + * Opened dialogs + * + * @class + * @singleton + */ IPA.opened_dialogs = { + /** Opened dialogs */ dialogs: [], + /** Get top dialog */ top_dialog: function() { var top = null; if (this.dialogs.length) top = this.dialogs[this.dialogs.length - 1]; return top; }, + /** Focus to dialog */ focus_top: function() { var top = this.top_dialog(); if (top) { @@ -40,30 +49,44 @@ IPA.opened_dialogs = { } }, + /** Add dialog */ add_dialog: function(dialog) { this.dialogs.push(dialog); }, + /** Remove dialog */ remove_dialog: function(dialog) { var index = this.dialogs.indexOf(dialog); if (index > -1) this.dialogs.splice(index, 1); } }; +/** + * Dialog button + * @class + */ IPA.dialog_button = function(spec) { spec = spec || {}; var that = IPA.object(); + /** @property {string} name Name */ that.name = spec.name; + /** @property {string} label Label */ that.label = text.get(spec.label || spec.name); + /** @property {Function} click Click handler */ that.click = spec.click || click; + /** @property {boolean} visible=true Button should be visible */ that.visible = spec.visible !== undefined ? spec.visible : true; function click() { } + /** + * Enabled setter + * @param {boolean} enabled + */ that.set_enabled = function(enabled) { if (enabled) { that.element.removeClass('ui-state-disabled'); @@ -72,6 +95,10 @@ IPA.dialog_button = function(spec) { } }; + /** + * Enabled getter + * @return {boolean} + */ that.is_enabled = function() { return !that.element.hasClass('ui-state-disabled'); }; @@ -81,6 +108,7 @@ IPA.dialog_button = function(spec) { /** * This is a base class for dialog boxes. + * @class */ IPA.dialog = function(spec) { @@ -88,28 +116,48 @@ IPA.dialog = function(spec) { var that = IPA.object(); + /** @property {entity.entity} entity Entity */ that.entity = IPA.get_entity(spec.entity); + /** @property {string} name="dialog" Name */ that.name = spec.name || 'dialog'; + /** @property {string} id ID */ that.id = spec.id; + /** @property {string} title Dialog title */ that.title = text.get(spec.title); + /** @property {number} width=500 Dialog width */ that.width = spec.width || 500; + /** @property {number} height Dialog height */ that.height = spec.height; + + /** + * Close dialog on Escape key press + * @property {boolean} close_on_escape=true + */ that.close_on_escape = spec.close_on_escape !== undefined ? spec.close_on_escape : true; // FIXME: remove facet reference // Purpose of facet reference is to obtain pkeys or ability to reload // facet. Such usage makes the code more spaghetti. It should be replaced. + /** + * Facet + * @property {facet.facet} + */ that.facet = spec.facet; + /** @property {IPA.widget_container} widgets Widgets */ that.widgets = IPA.widget_container(); + /** @property {IPA.field_container} fields Fields */ that.fields = IPA.field_container({ container: that }); + /** @property {ordered_map} buttons Buttons */ that.buttons = $.ordered_map(); + /** @property {details.facet_policies} policies Policies */ that.policies = IPA.facet_policies({ container: that, policies: spec.policies }); + /** Create and add button */ that.create_button = function(spec) { var factory = spec.$factory || IPA.dialog_button; var button = factory(spec); @@ -117,19 +165,32 @@ IPA.dialog = function(spec) { return button; }; + /** + * Add button + * @param {IPA.dialog_button} button + */ that.add_button = function(button) { that.buttons.put(button.name, button); }; + /** + * Get button + * @param {string} name + */ that.get_button = function(name) { return that.buttons.get(name); }; + /** + * Add field + * @param {IPA.field} field + */ that.field = function(field) { that.fields.add_field(field); return that; }; + /** Validate dialog fields */ that.validate = function() { var valid = true; var fields = that.fields.get_fields(); @@ -140,6 +201,7 @@ IPA.dialog = function(spec) { return valid; }; + /** Get ID */ that.get_id = function() { if (that.id) return that.id; if (that.name) return that.name; @@ -172,17 +234,23 @@ IPA.dialog = function(spec) { that.policies.post_create(); }; + /** + * Show message in dialog's message container + * @param {string} message + */ that.show_message = function(message) { that.message_container.text(message); that.message_container.css('display', ''); }; + /** Hide dialog message */ that.hide_message = function() { that.message_container.css('display', 'none'); }; /** * Open dialog + * @param {jQuery} container */ that.open = function(container) { @@ -217,9 +285,11 @@ IPA.dialog = function(spec) { that.focus_first_element(); }; + /** + * Set focus to the first tabbable element in the content area or the first button. + * If there are no tabbable elements, set focus on the dialog itself. + */ that.focus_first_element = function() { - // set focus to the first tabbable element in the content area or the first button - // if there are no tabbable elements, set focus on the dialog itself var element = that.container; var ui_dialog = that.container.parent('.ui-dialog'); // jq dialog div @@ -230,10 +300,20 @@ IPA.dialog = function(spec) { ui_dialog.get()))).eq(0).focus(); }; + /** + * Set jQuery dialog option + * @protected + * @param {string} name + * @param {Mixed} value + */ that.option = function(name, value) { that.container.dialog('option', name, value); }; + /** + * Set dialog buttons as jQuery dialog buttons + * @protected + */ that.set_buttons = function() { // create a map of button labels and handlers @@ -258,6 +338,10 @@ IPA.dialog = function(spec) { }); }; + /** + * Make buttons visible + * @param {string[]} names button names + */ that.display_buttons = function(names) { for (var i=0; i<that.buttons.values.length; i++) { @@ -268,6 +352,10 @@ IPA.dialog = function(spec) { that.set_buttons(); }; + /** + * Save fields' values into record object + * @param {Object} record + */ that.save = function(record) { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { @@ -276,6 +364,9 @@ IPA.dialog = function(spec) { } }; + /** + * Close dialog + */ that.close = function() { that.container.dialog('destroy'); that.container.remove(); @@ -284,6 +375,9 @@ IPA.dialog = function(spec) { IPA.opened_dialogs.focus_top(); }; + /** + * Reset dialog's fields + */ that.reset = function() { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { @@ -291,9 +385,27 @@ IPA.dialog = function(spec) { } }; + /** + * Called when dialog is opened. + * + * - override point + * @protected + */ that.register_listeners = function() {}; + + /** + * Called when dialog is closed. + * + * - override point + * @protected + */ that.remove_listeners = function() {}; + /** + * Create builder(s) which should build dialog's content (fields, + * widgets...) + * @protected + */ that.create_builder = function() { var widget_builder = IPA.widget_builder({ @@ -324,6 +436,10 @@ IPA.dialog = function(spec) { }); }; + /** + * Initializes dialog object + * @protected + */ that.init = function() { that.create_builder(); @@ -345,8 +461,18 @@ IPA.dialog = function(spec) { }; /** + * Adder dialog * This dialog provides an interface for searching and selecting * values from the available results. + * + * It has two tables: + * + * - available, contains values to choose from + * - selected, contains chosen values + * + * @class + * @extends IPA.dialog + * @mixins IPA.confirm_mixin */ IPA.adder_dialog = function(spec) { @@ -358,8 +484,17 @@ IPA.adder_dialog = function(spec) { IPA.confirm_mixin().apply(that); + /** + * External value can be added. + * + * In general external member doesn't represent any entity. + * @property {boolean} external=undefined + */ that.external = spec.external; + + /** @property {number} width=600 Width */ that.width = spec.width || 600; + /** @property {number} height=300 Height */ that.height = spec.height || 360; if (!that.entity) { @@ -390,19 +525,32 @@ IPA.adder_dialog = function(spec) { } }; + /** + * Get column + * @param {string} name + */ that.get_column = function(name) { return that.available_table.get_column(name); }; + /** Get all columns */ that.get_columns = function() { return that.available_table.get_columns(); }; + /** + * Add column to both tables. + * @param {IPA.column} column + */ that.add_column = function(column) { that.available_table.add_column(column); that.selected_table.add_column(column); }; + /** + * Replace columns in both tables + * @param {IPA.column[]} columns New columns + */ that.set_columns = function(columns) { that.clear_columns(); for (var i=0; i<columns.length; i++) { @@ -410,11 +558,19 @@ IPA.adder_dialog = function(spec) { } }; + /** + * Clear all columns in both tables. + */ that.clear_columns = function() { that.available_table.clear_columns(); that.selected_table.clear_columns(); }; + /** + * Create column from spec + * @param {Object} spec + * @return {IPA.column} + */ that.create_column = function(spec) { spec.entity = that.entity; var column = IPA.column(spec); @@ -422,6 +578,9 @@ IPA.adder_dialog = function(spec) { return column; }; + /** + * @inheritDoc + */ that.create = function() { // do not call that.dialog_create(); @@ -555,6 +714,7 @@ IPA.adder_dialog = function(spec) { that.search(); }; + /** @inheritDoc */ that.open = function(container) { var add_button = that.create_button({ @@ -579,16 +739,26 @@ IPA.adder_dialog = function(spec) { that.update_buttons(); }; + /** + * Move selected values in 'available' table to 'selected' table + */ that.add = function() { var rows = that.available_table.remove_selected_rows(); that.selected_table.add_rows(rows); }; + /** + * Move selected values in 'selected' table to 'available' table + */ that.remove = function() { var rows = that.selected_table.remove_selected_rows(); that.available_table.add_rows(rows); }; + /** + * Update button state based on selection + * @protected + */ that.update_buttons = function() { var values = that.selected_table.save(); @@ -597,29 +767,55 @@ IPA.adder_dialog = function(spec) { button.set_enabled(values && values.length); }; + /** + * Get value of 'available' filter + * @return {string} + */ that.get_filter = function() { return that.filter_field.val(); }; + /** + * Clear rows in available table + */ that.clear_available_values = function() { that.available_table.empty(); }; + /** + * Clear rows in selected table + */ that.clear_selected_values = function() { that.selected_table.empty(); }; + /** + * Add record to available table + * @param {Object} record + */ that.add_available_value = function(record) { that.available_table.add_record(record); }; + /** + * Get values in 'selected' table + */ that.get_selected_values = function() { return that.selected_table.save(); }; + /** + * Operation which has to be executed after selection confirmation + * + * - override point + */ that.execute = function() { }; + /** + * Confirm handler + * @protected + */ that.on_confirm = function() { var add_button = that.get_button('add'); @@ -636,7 +832,12 @@ IPA.adder_dialog = function(spec) { }; /** - * This dialog displays the values to be deleted. + * Deletion confirmation dialog + * + * - displays the values to be deleted. + * @class + * @extends IPA.dialog + * @mixins IPA.confirm_mixin */ IPA.deleter_dialog = function (spec) { @@ -648,19 +849,35 @@ IPA.deleter_dialog = function (spec) { spec.ok_label = spec.ok_label || '@i18n:buttons.remove'; var that = IPA.confirm_dialog(spec); + + /** + * Values to be deleted + * @property {string[]} values + */ that.values = spec.values || []; + + /** Positive confirmation handler */ that.on_ok = spec.on_ok || function() { that.execute(); }; + /** + * Add value + * @param {string} value + */ that.add_value = function(value) { that.values.push(value); }; + /** + * Replace values + * @param {string[]} values + */ that.set_values = function(values) { that.values = values; }; + /** @inheritDoc */ that.create = function() { $('<p/>', { @@ -702,6 +919,13 @@ IPA.deleter_dialog = function (spec) { return that; }; +/** + * Message dialog + * + * Displays a message. + * @class + * @extends IPA.confirm_dialog + */ IPA.message_dialog = function(spec) { spec = spec || {}; @@ -710,6 +934,7 @@ IPA.message_dialog = function(spec) { var that = IPA.confirm_dialog(spec); + /** @inheritDoc */ that.open = function(container) { that.confirm_dialog_open(container); @@ -723,6 +948,19 @@ IPA.message_dialog = function(spec) { return that; }; +/** + * Confirmation dialog + * + * Presents user a proposal(message). User then decides whether he would accept or + * decline the proposal. + * + * Acceptation is done by clicking on 'OK' button or hitting 'ENTER' key, + * refusal by clicking on 'Cancel' button or hitting 'ESCAPE' key. + * + * @class + * @extends IPA.dialog + * @mixins IPA.confirm_mixin + */ IPA.confirm_dialog = function(spec) { spec = spec || {}; @@ -732,20 +970,42 @@ IPA.confirm_dialog = function(spec) { var that = IPA.dialog(spec); IPA.confirm_mixin().apply(that); + /** @property {string} message Confirmation message */ that.message = text.get(spec.message); + + /** @property {Function} on_ok OK handler */ that.on_ok = spec.on_ok; + + /** @property {Function} on_cancel Cancel handler */ that.on_cancel = spec.on_cancel; + + /** @property {Function} ok_label OK button label */ that.ok_label = text.get(spec.ok_label || '@i18n:buttons.ok'); + + /** @property {Function} cancel_label Cancel button label */ that.cancel_label = text.get(spec.cancel_label || '@i18n:buttons.cancel'); + + /** + * Dialog is confirmed + * @protected + * @property {boolean} + */ that.confirmed = false; + + /** + * Dialog can be confirmed by hitting 'ENTER' key + * @property {boolean} confirm_on_enter=true + */ that.confirm_on_enter = spec.confirm_on_enter !== undefined ? spec.confirm_on_enter : true; + /** @inheritDoc */ that.create = function() { $('<p/>', { 'text': that.message }).appendTo(that.container); }; + /** @inheritDoc */ that.close = function() { that.dialog_close(); @@ -761,17 +1021,22 @@ IPA.confirm_dialog = function(spec) { } }; + /** @inheritDoc */ that.open = function(container) { that.confirmed = false; that.dialog_open(container); }; + /** + * Confirm handler + */ that.on_confirm = function() { that.confirmed = true; that.close(); }; + /** Create buttons */ that.create_buttons = function() { that.create_button({ @@ -801,16 +1066,40 @@ IPA.confirm_dialog = function(spec) { return that; }; +/** + * Confirm mixin + * + * Can extend a dialog by confirmation by keyboard functionality. When applied + * dialog can be: + * + * - confirmed by 'ENTER' key + * - declined by 'ESCAPE' key + * + * To apply: + * + * IPA.confirm_mixin().apply(dialog); + * + * @class + */ IPA.confirm_mixin = function() { return { mixin: { + /** + * Elements (tag names) or node types which should be ignored as + * confirmation event sources. + */ ignore_enter_rules: { src_elements: ['a', 'button'], src_types: ['textarea', 'select-one'] }, + /** + * Test if event is confirmation event + * @param {Event} event + * @return {boolean} + */ test_ignore: function(event) { var ir = this.ignore_enter_rules, @@ -822,6 +1111,9 @@ IPA.confirm_mixin = function() { return ignore; }, + /** + * Registration of keyboard event handlers + */ register_listeners: function() { var self = this; this._on_key_up_listener = function(e) { self.on_key_up(e); }; @@ -829,11 +1121,19 @@ IPA.confirm_mixin = function() { dialog_container.bind('keyup', this._on_key_up_listener); }, + /** + * Removal of registered event handlers + */ remove_listeners: function() { var dialog_container = this.container.parent('.ui-dialog'); dialog_container.unbind('keyup', this._on_key_up_listener); }, + /** + * Test if confirmation happened + * If so call dialog's `on_confirm` or `on_cancel` method. + * @param {Event} event + */ on_key_up: function(event) { if (event.keyCode === $.ui.keyCode.ENTER && !this.test_ignore(event) && diff --git a/install/ui/src/freeipa/entity.js b/install/ui/src/freeipa/entity.js index eef58d1a4..eb2d98a55 100644 --- a/install/ui/src/freeipa/entity.js +++ b/install/ui/src/freeipa/entity.js @@ -34,8 +34,22 @@ define([ function(lang, metadata_provider, Singleton_registry, builder, IPA, $, reg, text) { +/** + * Entity module + * + * @class entity + * @singleton + */ var exp = {}; +/** + * Entity + * + * Represents a business logic object type, ie. user. Maintains + * information related to that object. + * @class entity.entity + * @alternateClassName IPA.entity + */ exp.entity = IPA.entity = function(spec) { spec = spec || {}; @@ -47,34 +61,113 @@ exp.entity = IPA.entity = function(spec) { var that = IPA.object(); + /** + * Name + * @property {string} + */ that.name = spec.name; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + /** + * Entity has primary key(s) + * @property {boolean} defines_key=true + */ that.defines_key = spec.defines_key !== undefined ? spec.defines_key : true; + /** + * Metadata + * @property {Object} + */ that.metadata = spec.metadata; + /** + * Dialogs + * @protected + * @property {ordered_map} + */ that.dialogs = $.ordered_map(); + + /** + * Dialog specifications + * @property {Array.<Object>} + */ that.dialog_specs = spec.dialogs || []; + + /** + * Dialogs defined in `dialog_specs` were created -> `dialogs` is populated. + * @property {boolean} + */ that.dialogs_created = false; + /** + * Entity policies + * @property {IPA.entity_policies} + */ that.policies = IPA.entity_policies({ entity: that, policies: spec.policies }); + + /** + * Facets + * @protected + * @property {ordered_map} + */ that.facets = $.ordered_map(); + + /** + * Facet groups + * @property {ordered_map} + */ that.facet_groups = $.ordered_map(); + + /** + * Facet group object specifications + * @property {Array.<Object>} + */ that.facet_group_specs = spec.facet_groups; + + /** + * Facet object specifications + * @property {Array.<Object>} + */ that.facet_specs = spec.facets || []; + + /** + * Facets and facet groups were created + * @property {boolean} + */ that.facets_created = false; - // current facet + /** + * Current facet + * @property {IPA.facet} + */ that.facet = null; + /** + * Name of facet to which other facets should redirect in case of unexpected + * event. + * @property {string} + */ that.redirect_facet = spec.redirect_facet; + + /** + * Containing entity in case if this is a nested entity + * @property {entity.entity} + */ that.containing_entity = null; + /** + * Initialize entity. + * Should be called by builder if used. + */ that.init = function() { if (!that.metadata) { that.metadata = that.get_default_metadata(); @@ -88,14 +181,28 @@ exp.entity = IPA.entity = function(spec) { that.label = text.get(that.label) || that.metadata.label || that.name; }; + /** + * Initialize entity. + * Should be called by builder if used. + * @return Metadata + */ that.get_default_metadata = function() { return metadata_provider.get('@mo:'+that.name); }; + /** + * Getter for `containing_entity` + * @return {entity.entity} + */ that.get_containing_entity = function() { return that.containing_entity; }; + /** + * Builder overrides for dialogs belonging to this entity + * + * It's purpose is to set valid context and add the dialogs. + */ that.dialog_build_overrides = { $pre_ops: [ function (spec, context) { @@ -112,6 +219,14 @@ exp.entity = IPA.entity = function(spec) { $factory: IPA.dialog }; + /** + * Get dialog with given name + * + * Uses lazy creation - creates dialogs from spec if not done yet. + * + * @param {string} name + * @return Dialog + */ that.get_dialog = function(name) { //build all dialogs on the first time @@ -123,6 +238,12 @@ exp.entity = IPA.entity = function(spec) { return that.dialogs.get(name); }; + /** + * Add one or multiple dialog(s) to entity + * + * New dialogs are built if specs are supplied. + * @param {IPA.dialog|Array.<IPA.dialog>} dialog - dialog(s) or spec(s) to add + */ that.add_dialog = function(dialog) { var add = function (dialog) { @@ -143,24 +264,55 @@ exp.entity = IPA.entity = function(spec) { return that; }; + /** + * Add facet group + * @deprecated + */ that.add_facet_group = function(facet_group) { that.facet_groups.put(facet_group.name, facet_group); }; + /** + * Get facet group + * @deprecated + */ that.get_facet_group = function(name) { return that.facet_groups.get(name); }; + /** + * Remove facet group + * @deprecated + */ that.remove_facet_groups = function() { that.facet_groups.empty(); }; + /** + * This method is used only in get_facet method and there is no sense to + * use it alone. Will be removed. + * @deprecated + */ that.add_redirect_info = function(facet_name) { if (!that.redirect_facet && facet_name){ that.redirect_facet = facet_name; } }; + /** + * Get facet with given name. + * + * Uses lazy creation. All facets are created from facet specs upon first + * get_facet call. + * + * - returns current or first facet if name is *undefined*. + * - returns default facet if name == 'default' - first facet of non-empty + * facet group + * + * @param {string|undefined|"default"} name - facet name + * @return {IPA.facet} + * + */ that.get_facet = function(name) { var i, facets; @@ -202,6 +354,12 @@ exp.entity = IPA.entity = function(spec) { return that.facets.get(name); }; + /** + * Add facet to entity + * + * @param {IPA.facet} facet - facet to add + * @param {string} facet.facet_group - facet group to add the facet + */ that.add_facet = function(facet) { facet.entity = that; @@ -217,6 +375,11 @@ exp.entity = IPA.entity = function(spec) { return that; }; + /** + * Helper function - evaluates if entity as any attribute members. + * Useful for knowing when to add 'no_members' option to RPC call. + * @return {boolean} + */ that.has_members = function() { var members = that.metadata.attribute_members; var has = false; @@ -231,6 +394,9 @@ exp.entity = IPA.entity = function(spec) { return has; }; + /** + * Builder used for building this entity. + */ that.builder = spec.builder || IPA.entity_builder(that); that.entity_init = that.init; @@ -238,7 +404,19 @@ exp.entity = IPA.entity = function(spec) { return that; }; -exp.entity_builder =IPA.entity_builder = function(entity) { +/** + * Entity post builder + * + * - contains methods for entity post creation operations. + * - has chained API. + * - direct usage is not recommended. It's usable only when overriding standard + * behavior. By default, calls of most methods are registered as post operations + * for {@link _base.builder}. + * + * @class entity.entity_builder + * @alternateClassName IPA.entity_builder + */ +exp.entity_builder = IPA.entity_builder = function(entity) { var that = IPA.object(); @@ -246,6 +424,7 @@ exp.entity_builder =IPA.entity_builder = function(entity) { var facet = null; var section = null; + /** Default facet groups **/ that.default_facet_groups = [ 'member', 'settings', @@ -253,6 +432,10 @@ exp.entity_builder =IPA.entity_builder = function(entity) { 'managedby' ]; + /** + * Build and add facet group + * @param {Object} spec - facet group specification + */ that.facet_group = function(spec) { if (typeof spec === 'string') { @@ -276,6 +459,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Replace facet groups + * + * @param {Array.<Object>} specs - specifications of new facet groups + */ that.facet_groups = function(specs) { entity.remove_facet_groups(); @@ -288,6 +476,10 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add facet spec + * @param {Object} spec + */ that.facet = function(spec) { entity.facet_specs.push(spec); @@ -295,6 +487,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add search facet + * @deprecated + * @param {Object} spec + */ that.search_facet = function(spec) { spec.$type = spec.$type || 'search'; @@ -306,6 +503,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add nested search facet + * @deprecated + * @param {Object} spec + */ that.nested_search_facet = function(spec) { spec.$type = spec.$type || 'nested_search'; @@ -315,6 +517,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add details facet + * @deprecated + * @param {Object} spec + */ that.details_facet = function(spec) { spec.$type = spec.$type || 'details'; @@ -324,6 +531,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add association facet + * @deprecated + * @param {Object} spec + */ that.association_facet = function(spec) { spec.$type = spec.$type || 'association'; @@ -333,6 +545,11 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add attribute_facet facet + * @deprecated + * @param {Object} spec + */ that.attribute_facet = function(spec) { spec.$type = spec.$type || 'attribute'; @@ -342,6 +559,18 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add missing association facets + * + * Facets are based on entity attribute_members. Doesn't add duplicates so + * facet defined in entity spec are ignored and only the missing are added. + * + * Direct usage is deprecated. Use `standard_association_facets: true` + * in entity spec instead. + * + * @deprecated + * @param {Object} spec - object to be mixed-in in each new facet spec + */ that.standard_association_facets = function(spec) { spec = spec || {}; @@ -403,7 +632,7 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return null; } - /* + /** * If it's an indirect attribute member, return its direct facets spec * if it exists. */ @@ -426,6 +655,13 @@ exp.entity_builder =IPA.entity_builder = function(entity) { } } + /** + * Set containing(parent) entity + * + * Direct usage is deprecated. Set `containing_entity: 'entity_name'` in + * entity spec instead. + * @deprecated + */ that.containing_entity = function(entity_name) { add_redirect_info(); entity.containing_entity = IPA.get_entity(entity_name); @@ -450,6 +686,12 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that; }; + /** + * Add adder dialog spec + * + * Set `adder_dialog: { ... }` in entity instead. + * @deprecated + */ that.adder_dialog = function(spec) { spec.$factory = spec.$factory || IPA.entity_adder_dialog; spec.name = spec.name || 'add'; @@ -463,6 +705,12 @@ exp.entity_builder =IPA.entity_builder = function(entity) { return that.dialog(spec); }; + /** + * Add deleter_dialog spec + * + * Set `deleter_dialog: { ... }` in entity instead. + * @deprecated + */ that.deleter_dialog = function(spec) { spec.$factory = spec.$factory || IPA.search_deleter_dialog; spec.name = spec.name || 'remove'; @@ -472,11 +720,28 @@ exp.entity_builder =IPA.entity_builder = function(entity) { that.facet_groups(entity.facet_group_specs || that.default_facet_groups); - - return that; }; +/** + * Entity post build operations + * + * they: + * + * - invokes `enable_test()`, `init()` + * - sets containing entity + * - creates standard association facets + * - add adder dialog + * - adds deleter dialog + * + * @member entity + * @property {Object} entity_post_ops + * @property {Function} entity_post_ops.init + * @property {Function} entity_post_ops.containing_entity + * @property {Function} entity_post_ops.standard_association_facets + * @property {Function} entity_post_ops.adder_dialog + * @property {Function} entity_post_ops.deleter_dialog + */ exp.entity_post_ops = { init: function(entity, spec, context) { @@ -526,33 +791,75 @@ exp.entity_post_ops = { } }; +/** + * Entity policy base class + * + * Policy is a mediator object. Usually it handles inter-facet communication. + * + * Specific policy should override `facet_created` method. + * + * @class entity.entity_policy + * @alternateClassName IPA.entity_policy + * @abstract + */ exp.entity_policy = IPA.entity_policy = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Entity this policy is associated with + * @property {entity.entity} + */ that.entity = spec.entity; + /** + * Facet created + * + * Functional entry point. This method is called after facets are created. + * It allows the policy to registered various event handlers to facets or + * do other work. + */ that.facets_created = function() { }; return that; }; +/** + * Collection of entity policies. + * @class entity.entity_policies + * @alternateClassName IPA.entity_policies + */ exp.entity_policies = IPA.entity_policies = function(spec) { var that = IPA.object(); + /** + * Entity to be set to all policies + */ that.entity = spec.entity; + + /** + * Policies + */ that.policies = []; + /** + * Add policy + * @param {entity.entity_policy} policy + */ that.add_policy = function(policy) { policy.entity = that.entity; that.policies.push(policy); }; + /** + * Add policies + * @param {Array.<entity.entity_policy>} policies + */ that.add_policies = function(policies) { if (!policies) return; @@ -562,6 +869,9 @@ exp.entity_policies = IPA.entity_policies = function(spec) { } }; + /** + * Call each policy's `facet_policy` method + */ that.facets_created = function() { for (var i=0; i<that.policies.length; i++) { @@ -576,17 +886,53 @@ exp.entity_policies = IPA.entity_policies = function(spec) { return that; }; +/** + * Facet update policy + * + * This policy sets destination facet of destination entity as expired + * when specific event of source facet of this entity is raised. + * + * @class entity.facet_update_policy + * @extends entity.entity_policy + * @alternateClassName IPA.facet_update_policy + * + * @param {Object} spec + * @param {string} spec.event - event name + * @param {string} spec.source_facet - source facet name + * @param {string} spec.dest_facet - destination facet name + * @param {string} spec.dest_entity_name - destination entity name + * + */ exp.facet_update_policy = IPA.facet_update_policy = function(spec) { spec = spec || {}; var that = IPA.entity_policy(); + /** + * Source event name + * @property {string} event=on_update + */ that.event = spec.event || 'on_update'; + + /** + * Source facet name + */ that.source_facet_name = spec.source_facet; + + /** + * Destination facet name + */ that.dest_facet_name = spec.dest_facet; + + /** + * Destination entity name + */ that.dest_entity_name = spec.dest_entity; + /** + * @inheritDoc + */ that.facets_created = function() { that.source_facet = that.entity.get_facet(that.source_facet_name); @@ -605,6 +951,9 @@ exp.facet_update_policy = IPA.facet_update_policy = function(spec) { event.attach(that.set_expired_flag); }; + /** + * Set facet as expired + */ that.set_expired_flag = function() { that.dest_facet.set_expired_flag(); @@ -613,17 +962,36 @@ exp.facet_update_policy = IPA.facet_update_policy = function(spec) { return that; }; +/** + * Adder facet update policy + * + * Update destination details facet when new object is added (adder dialog + * 'added' event). + * + * @class entity.adder_facet_update_policy + * @extends entity.entity_policy + * @alternateClassName IPA.adder_facet_update_policy + * + */ exp.adder_facet_update_policy = IPA.adder_facet_update_policy = function(spec) { spec = spec || {}; var that = IPA.entity_policy(); + /** + * Source event name + * @property {string} event='added' + */ that.event = spec.event || 'added'; + /** Adder dialog name */ that.dialog_name = spec.dialog_name || 'add'; + /** Destination facet name */ that.dest_facet_name = spec.dest_facet || 'details'; + /** Destination entity name */ that.dest_entity_name = spec.dest_entity; + /** @inheritDoc */ that.facets_created = function() { that.dialog = that.entity.get_dialog(that.dialog_name); @@ -642,6 +1010,7 @@ exp.adder_facet_update_policy = IPA.adder_facet_update_policy = function(spec) { event.attach(that.set_expired_flag); }; + /** Set facet as expired */ that.set_expired_flag = function() { that.dest_facet.set_expired_flag(); @@ -650,6 +1019,16 @@ exp.adder_facet_update_policy = IPA.adder_facet_update_policy = function(spec) { return that; }; + +/** + * Search facet update policy + * + * Expires details facet when search facet is updated. + * + * @class entity.search_facet_update_policy + * @extends entity.facet_update_policy + * @alternateClassName IPA.search_facet_update_policy + */ exp.search_facet_update_policy = IPA.search_facet_update_policy = function(spec) { spec = spec || {}; @@ -659,6 +1038,15 @@ exp.search_facet_update_policy = IPA.search_facet_update_policy = function(spec) return IPA.facet_update_policy(spec); }; +/** + * Details facet update policy + * + * Expires search facet when details facet is updated. + * + * @class entity.details_facet_update_policy + * @extends entity.facet_update_policy + * @alternateClassName IPA.details_facet_update_policy + */ exp.details_facet_update_policy =IPA.details_facet_update_policy = function(spec) { spec = spec || {}; diff --git a/install/ui/src/freeipa/facet.js b/install/ui/src/freeipa/facet.js index b01452dd7..44ebfe5af 100644 --- a/install/ui/src/freeipa/facet.js +++ b/install/ui/src/freeipa/facet.js @@ -44,51 +44,65 @@ define([ Singleton_registry, builder, IPA, $, navigation, phases, reg, su, text) { /** + * Facet module + * + * @class facet + * @singleton + */ +var exp = {}; +exp.facet_spec = {}; + +/** * Facet represents the content of currently displayed page. * - * = Show, Clear, Refresh mechanism = + * ## Show, Clear, Refresh mechanism * * Use cases: - * a) Display facet with defined arguments. - * b) Switch to facet - * c) Update facet state * - * == Display facet by route == - * 1) somebody sets route - * 2) Route is evaluated, arguments extracted. - * 3) Facet state is updated `set_state(args, pkeys)`.(saves previous state) - * 4) Facet show() is called + * - Display facet with defined arguments. + * - Switch to facet + * - Update facet state + * + * ## Display facet by route + * + * 1. somebody sets route + * 2. Route is evaluated, arguments extracted. + * 3. Facet state is updated `set_state(args, pkeys)`.(saves previous state) + * 4. Facet show() is called + * + * ## Display facet with defined arguments + * + * 1. Somebody calls navigation.show(xxx); + * 2. Facet state is updated `set_state(args, pkeys)`.(saves previous state) + * 3. Route is updated, but the hash change is ignored + * 4. Facet show() is called. + * - First time show + * a. creates DOM + * b. display DOM + * c. refresh(); + * - Next time + * a. display DOM + * b. `needs_update()` (compares previous state with current) + * - true: + * 1. clear() - each facet can override to supress clear or + * control the behaviour + * 2. refresh() * - * == Display facet with defined arguments == - * 1) Somebody calls navigation.show(xxx); - * 2) Facet state is updated `set_state(args, pkeys)`.(saves previous state) - * 3) Route is updated, but the hash change is ignored - * 4) Facet show() is called. - * 5.1) First time show - * a) creates DOM - * b) display DOM - * c) refresh(); - * 5.2) Next time - * a) display DOM - * b) needs_update()? (compares previous state with current) - * true: - * 1) clear() - each facet can override to supress clear or - * control the behaviour - * 2) refresh() + * ## Swith to facet * - * == Swith to facet == * Same as display facet but only without arguments. Arguments are extracted at * step 2. * - * == Update facet state == - * 1) set_state(args, pkeys?) - * 2) needs_update()? - * true: - * a) clear() - * b) refresh() - * 2) Update route, ignore hash change event + * ## Update facet state * - * == Updating hash == + * 1. set_state(args, pkeys?) + * 2. needs_update()? + * - true: + * 1. clear() + * 2. refresh() + * 3. Update route, ignore hash change event + * + * ## Updating hash * Hash updates are responsibility of navigation component and application * controller. Application controller should listen to facet's `state_change` * event. And call something like navigation.update_hash(facet). @@ -96,77 +110,185 @@ define([ * navigation.update_hash should find all the necessary state properties (args, * pkeys). * - * == needs_update method == - * + * ## needs_update method + * todo * + * @class facet.facet + * @alternateClassName IPA.facet */ - -var exp = {}; -exp.facet_spec = {}; - exp.facet = IPA.facet = function(spec, no_init) { spec = spec || {}; var that = new Evented(); + /** + * Entity this facet belongs to + * @property {entity.entity} + */ that.entity = IPA.get_entity(spec.entity); + /** + * Facet name + * @property {string} + */ that.name = spec.name; + + /** + * Facet label + * @property {string} + */ that.label = text.get(spec.label); + + /** + * Facet title + * @property {string} + */ that.title = text.get(spec.title || that.label); + + /** + * Facet tab label + * @property {string} + */ that.tab_label = text.get(spec.tab_label || that.label); + + /** + * Facet element's CSS class + * @property {string} + */ that.display_class = spec.display_class; + + /** + * Flag. Marks the facet as read-only - doesn't support modify&update + * operation. + * @property {boolean} + */ that.no_update = spec.no_update; + /** + * Breadcrumb navigation is not displayed when set. + * @property {boolean} + */ that.disable_breadcrumb = spec.disable_breadcrumb; + + /** + * Facet tabs are not displayed when set. + * @property {boolean} + */ that.disable_facet_tabs = spec.disable_facet_tabs; + /** + * State object for actions + * @property {facet.state} + */ that.action_state = builder.build('', spec.state || {}, {}, { $factory: exp.state }); + + /** + * Collection of facet actions + * @property {facet.action_holder} + */ that.actions = builder.build('', { actions: spec.actions }, {}, { $factory: exp.action_holder } ); + /** + * Array of actions which are displayed in facet header + * @property {Array.<string>} + */ that.header_actions = spec.header_actions; + + /** + * Facet header + * @property {facet.facet_header} + */ that.header = spec.header || IPA.facet_header({ facet: that }); + /** + * Hard override for `needs_update()` logic. When set, `needs_update` + * should always return this value. + * @property {boolean} + */ that._needs_update = spec.needs_update; + + /** + * Marks facet as expired - needs update + * + * Difference between `_needs_update` is that `expired_flag` should be + * cleared after update. + * + * @property {boolean} + */ that.expired_flag = true; + + /** + * Last time when facet was updated. + * @property {Date} + */ that.last_updated = null; + + /** + * Timeout[s] from `last_modified` after which facet should be expired + * @property {number} expire_timeout=600 + */ that.expire_timeout = spec.expire_timeout || 600; //[seconds] + + /** + * Raised when facet gets updated + * @event + */ that.on_update = IPA.observer(); + + /** + * Raised after `load()` + * @event + */ that.post_load = IPA.observer(); + /** + * Dialogs + * @property {ordered_map} + */ that.dialogs = $.ordered_map(); /** * domNode of container * Suppose to contain domNode of this and other facets. + * @property {jQuery} */ that.container_node = spec.container_node; /** - * FIXME: that.container should be eliminated - * now it's the same as domNode - */ - //that.container - - /** * domNode which contains all content of a facet. * Should contain error content and content. When error is moved to * standalone facet it will replace functionality of content. + * @property {jQuery} */ that.domNode = null; - // facet group name + /** + * Facet group name + * @property {string} + */ that.facet_group = spec.facet_group; + /** + * Redirection target information. + * + * Can be facet and/or entity name. + * @property {Object} + * @param {string} entity entity name + * @param {string} facet facet name + */ that.redirect_info = spec.redirect_info; /** * Public state - * + * @property {facet.FacetState} */ that.state = new FacetState(); + /** + * Set and normalize pkeys. Merges with existing if present. If keys length + * differs, the alignment is from the last one to the first one. + */ that.set_pkeys = function(pkeys) { pkeys = that.get_pkeys(pkeys); @@ -176,7 +298,7 @@ exp.facet = IPA.facet = function(spec, no_init) { /** * Return THE pkey of this facet. Basically the last one of pkeys list. * - * @type String + * @return {string} pkey */ that.get_pkey = function() { var pkeys = that.get_pkeys(); @@ -194,7 +316,8 @@ exp.facet = IPA.facet = function(spec, no_init) { * One can get merge current pkeys with supplied if `pkeys` param is * specified. * - * @param String[] new pkeys to merge + * @param {string[]} pkeys new pkeys to merge + * @return {string[]} pkeys */ that.get_pkeys = function(pkeys) { var new_keys = []; @@ -228,6 +351,12 @@ exp.facet = IPA.facet = function(spec, no_init) { return new_keys; }; + /** + * Get pkey prefix. + * + * Opposite method to `get_pkey` - get's all pkeys except the last one. + * @return {Array.<string>} + */ that.get_pkey_prefix = function() { var pkeys = that.get_pkeys(); if (pkeys.length > 0) pkeys.pop(); @@ -235,6 +364,14 @@ exp.facet = IPA.facet = function(spec, no_init) { return pkeys; }; + /** + * Checks if two objects has the same properties with equal values. + * + * @param {Object} a + * @param {Object} b + * @return {boolean} `a` and `b` are value-equal + * @protected + */ that.state_diff = function(a, b) { var diff = false; var checked = {}; @@ -271,6 +408,11 @@ exp.facet = IPA.facet = function(spec, no_init) { return diff; }; + /** + * Reset facet state to supplied + * + * @param {Object} state state to set + */ that.reset_state = function(state) { if (state.pkeys) { @@ -279,12 +421,19 @@ exp.facet = IPA.facet = function(spec, no_init) { that.state.reset(state); }; + /** + * Get copy of current state + * + * @return {Object} state + */ that.get_state = function() { return that.state.clone(); }; /** * Merges state into current and notifies it. + * + * @param {Object} state object to merge into current state */ that.set_state = function(state) { @@ -294,11 +443,20 @@ exp.facet = IPA.facet = function(spec, no_init) { that.state.set(state); }; + /** + * Handle state set + * @param {Object} old_state + * @param {Object} state + */ that.on_state_set = function(old_state, state) { that._on_state_change(state); }; + /** + * Handle state change + * @protected + */ that._on_state_change = function(state) { // basically a show method without displaying the facet @@ -327,6 +485,13 @@ exp.facet = IPA.facet = function(spec, no_init) { } }; + /** + * Fires `facet-state-change` event with given state as event parameter. + * + * @fires facet-state-change + * @protected + * @param {Object} state + */ that._notify_state_change = function(state) { that.emit('facet-state-change', { facet: that, @@ -334,15 +499,29 @@ exp.facet = IPA.facet = function(spec, no_init) { }); }; + /** + * Get dialog with given name from facet dialog collection + * + * @param {string} name + * @return {IPA.dialog} dialog + */ that.get_dialog = function(name) { return that.dialogs.get(name); }; + /** + * Add dialog to facet dialog collection + * + * @param {IPA.dialog} dialog + */ that.dialog = function(dialog) { that.dialogs.put(dialog.name, dialog); return that; }; + /** + * Create facet's HTML representation + */ that.create = function() { var entity_name = !!that.entity ? that.entity.name : ''; @@ -389,6 +568,12 @@ exp.facet = IPA.facet = function(spec, no_init) { domNode.removeClass('active-facet'); }; + /** + * Create facet header + * + * @param {jQuery} container + * @protected + */ that.create_header = function(container) { that.header.create(container); @@ -398,9 +583,22 @@ exp.facet = IPA.facet = function(spec, no_init) { }).appendTo(container); }; + /** + * Create content + * + * @param {jQuery} container + * @protected + * @abstract + */ that.create_content = function(container) { }; + /** + * Create control buttons + * + * @param {jQuery} container + * @protected + */ that.create_control_buttons = function(container) { if (that.control_buttons) { @@ -408,11 +606,22 @@ exp.facet = IPA.facet = function(spec, no_init) { } }; + /** + * Update h1 element in title container + * + * @deprecated Please update title in facet header or it's widget instead. + */ that.set_title = function(container, title) { var element = $('h1', that.title_container); element.html(title); }; + /** + * Show facet + * + * - clear & refresh if needs update + * - mark itself as active facet + */ that.show = function() { that.entity.facet = that; // FIXME: remove @@ -442,36 +651,87 @@ exp.facet = IPA.facet = function(spec, no_init) { } }; + /** + * Show content container and hide error container. + * + * Opposite to `show_error`. + * @protected + */ that.show_content = function() { that.content.css('display', 'block'); that.error_container.css('display', 'none'); }; + /** + * Show error container and hide content container. + * + * Opposite to `show_content` + * @protected + */ that.show_error = function() { that.content.css('display', 'none'); that.error_container.css('display', 'block'); }; + /** + * Check if error is displayed (instead of content) + * + * @return {boolean} error visible + */ that.error_displayed = function() { return that.error_container && that.error_container.css('display') === 'block'; }; + /** + * Un-mark itself as active facet + */ that.hide = function() { that.domNode.removeClass('active-facet'); }; + /** + * Update widget content with supplied data + * @param {Object} data + */ that.load = function(data) { that.data = data; that.header.load(data); }; + /** + * Start refresh + * + * - get up-to-date data + * - load the data + * @abstract + */ that.refresh = function() { }; + /** + * Clear all widgets + * @abstract + */ that.clear = function() { }; + /** + * Check if facet needs update + * + * That means if: + * + * - new state (`state` or supplied state) is different that old_state + * (`old_state`) + * - facet is expired + * - `expired_flag` is set or + * - expire_timeout takes effect + * - error is displayed + * + * + * @param {Object} [new_state] supplied state + * @return {boolean} needs update + */ that.needs_update = function(new_state) { if (that._needs_update !== undefined) return that._needs_update; @@ -497,32 +757,48 @@ exp.facet = IPA.facet = function(spec, no_init) { return needs_update; }; + /** + * Sets expire flag + */ that.set_expired_flag = function() { that.expired_flag = true; }; + /** + * Clears `expired_flag` and resets `last_updated` + */ that.clear_expired_flag = function() { that.expired_flag = false; that.last_updated = Date.now(); }; + /** + * Check whether the facet is dirty + * + * Dirty can mean that value of displayed object was modified but the change + * was not reflected to data source + * + * @returns {boolean} + */ that.is_dirty = function() { return false; }; /** - * Wheater we can switch to different facet. - * @returns Boolean + * Whether we can switch to different facet. + * @returns {boolean} */ that.can_leave = function() { return !that.is_dirty(); }; /** - * Show dialog displaying a message explaining why we can't switch facet. + * Get dialog displaying a message explaining why we can't switch facet. * User can supply callback which is called when a leave is permitted. * - * Listeneres should set 'callback' property to listen state evaluation. + * TODO: rename to get_leave_dialog + * + * @param {Function} permit_callback */ that.show_leave_dialog = function(permit_callback) { @@ -535,6 +811,15 @@ exp.facet = IPA.facet = function(spec, no_init) { return dialog; }; + /** + * Display error page instead of facet content + * + * Use this call when unrecoverable error occurs. + * + * @param {Object} error_thrown - error to be displayed + * @param {string} error_thrown.name + * @param {string} error_thrown.message + */ that.report_error = function(error_thrown) { var add_option = function(ul, text, handler) { @@ -596,6 +881,11 @@ exp.facet = IPA.facet = function(spec, no_init) { that.show_error(); }; + /** + * Get facet based on `redirect_info` and {@link + * entity.entity.redirect_facet} + * @return {facet.facet} facet to be redirected to + */ that.get_redirect_facet = function() { var entity = that.entity; @@ -619,6 +909,9 @@ exp.facet = IPA.facet = function(spec, no_init) { return facet; }; + /** + * Redirect to redirection target + */ that.redirect = function() { var facet = that.get_redirect_facet(); @@ -628,6 +921,10 @@ exp.facet = IPA.facet = function(spec, no_init) { var redirect_error_codes = [4001]; + /** + * Redirect if error thrown is + * @protected + */ that.redirect_error = function(error_thrown) { /*If the error is in talking to the server, don't attempt to redirect, @@ -640,6 +937,10 @@ exp.facet = IPA.facet = function(spec, no_init) { } }; + /** + * Initialize facet + * @protected + */ that.init_facet = function() { that.action_state.init(that); @@ -672,14 +973,47 @@ exp.facet = IPA.facet = function(spec, no_init) { return that; }; +/** + * Facet header + * + * Widget-like object which purpose is to render facet's header. + * + * By default, facet header consists of: + * + * - breadcrumb navigation + * - title + * - action list + * - facet tabs + * + * @class facet.facet_header + * @alternateClassName IPA.facet_header + */ exp.facet_header = IPA.facet_header = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Facet this header belongs to + * @property {facet.facet} + */ that.facet = spec.facet; + /** + * Action list with facet's header actions + * @property {facet.action_list_widget} action_list + */ + + /** + * Facet title widget + * @property {facet.facet_title} title_widget + */ + + /** + * Initialize facet header + * @protected + */ that.init = function() { if (that.facet.header_actions) { @@ -705,6 +1039,9 @@ exp.facet_header = IPA.facet_header = function(spec) { that.title_widget = IPA.facet_title(); }; + /** + * Select tab with the same name as related facet or default + */ that.select_tab = function() { if (that.facet.disable_facet_tabs) return; @@ -718,6 +1055,13 @@ exp.facet_header = IPA.facet_header = function(spec) { } }; + /** + * Set new pkey in title and breadcrumb navigation + * + * Limits the pkey if it's too long. + * + * @param {string} value pkey + */ that.set_pkey = function(value) { if (!value) return; @@ -804,6 +1148,12 @@ exp.facet_header = IPA.facet_header = function(spec) { that.adjust_elements(); }; + /** + * Create link for facet tab + * @protected + * @param {jQuery} container + * @param {facet.facet} other_facet + */ that.create_facet_link = function(container, other_facet) { var li = $('<li/>', { @@ -827,6 +1177,12 @@ exp.facet_header = IPA.facet_header = function(spec) { }).appendTo(li); }; + /** + * Create facet tab group + * @protected + * @param {jQuery} container + * @param {Object} facet_group + */ that.create_facet_group = function(container, facet_group) { var section = $('<div/>', { @@ -849,6 +1205,10 @@ exp.facet_header = IPA.facet_header = function(spec) { } }; + /** + * Create header's HTML + * @param {jQuery} container + */ that.create = function(container) { that.container = container; @@ -904,6 +1264,18 @@ exp.facet_header = IPA.facet_header = function(spec) { } }; + /** + * Update displayed information with new data + * + * Data is result of FreeIPA RPC command. + * + * Updates (if present in data): + * + * - facet group links with number of records + * - facet group labels with facet's pkey + * + * @param {Object} data + */ that.load = function(data) { if (!data) return; var result = data.result.result; @@ -945,6 +1317,10 @@ exp.facet_header = IPA.facet_header = function(spec) { } }; + /** + * Reflect facet's action state summary into title widget class and icon + * tooltip. + */ that.update_summary = function() { var summary = that.facet.action_state.summary(); @@ -957,6 +1333,10 @@ exp.facet_header = IPA.facet_header = function(spec) { that.adjust_elements(); }; + /** + * Compute maximum pkey length to be displayed in header + * @return {number} length + */ that.get_max_pkey_length = function() { var label_w, max_pkey_w, max_pkey_l, al, al_w, icon_w, char_w, container_w; @@ -980,6 +1360,10 @@ exp.facet_header = IPA.facet_header = function(spec) { return max_pkey_l; }; + /** + * Adjust position of header widgets, mainly action list, according to + * title length. + */ that.adjust_elements = function() { if (that.action_list) { @@ -995,6 +1379,9 @@ exp.facet_header = IPA.facet_header = function(spec) { } }; + /** + * Clear displayed information + */ that.clear = function() { that.load(); if (that.action_list) that.action_list.clear(); @@ -1003,12 +1390,30 @@ exp.facet_header = IPA.facet_header = function(spec) { return that; }; +/** + * Facet title widget + * + * A widget-like object for title representation in a facet header. + * + * @class facet.facet_title + * @alternateClassName IPA.facet_title + */ exp.facet_title = IPA.facet_title = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Update displayed information with supplied data + * + * @param {Object} data + * @param {string} data.pkey + * @param {string} data.title + * @param {string} data.tooltip + * @param {string} data.icon_tooltip + * @param {string} data.css_class css class for title container + */ that.update = function(data) { var tooltip = data.tooltip || data.title; @@ -1029,6 +1434,9 @@ exp.facet_title = IPA.facet_title = function(spec) { that.set_icon_tooltip(icon_tooltip); }; + /** + * Create HTML elements + */ that.create = function(container) { that.title_container = $('<div/>', { @@ -1048,10 +1456,22 @@ exp.facet_title = IPA.facet_title = function(spec) { }).appendTo(h3); }; + /** + * Set maximum width of the widget + * + * @param {number|string} width + */ that.set_max_width = function(width) { that.title_container.css('max-width', width+'px'); }; + /** + * Set CSS class + * + * Can be used for various purposes like icon change. + * + * @param {string} css_class + */ that.set_class = function(css_class) { if (that.css_class) { @@ -1065,6 +1485,11 @@ exp.facet_title = IPA.facet_title = function(spec) { that.css_class = css_class; }; + /** + * Set icon tooltip + * + * @param {string} tooltip + */ that.set_icon_tooltip = function(tooltip) { that.icon.attr('title', tooltip); }; @@ -1072,42 +1497,114 @@ exp.facet_title = IPA.facet_title = function(spec) { return that; }; +/** + * Facet which displays information in a table + * + * @class facet.table_facet + * @extends facet.facet + * @alternateClassName IPA.table_facet + */ exp.table_facet = IPA.table_facet = function(spec, no_init) { spec = spec || {}; var that = IPA.facet(spec, no_init); + /** + * Entity of data displayed in the table + * @property {entity.entity} + */ that.managed_entity = spec.managed_entity ? IPA.get_entity(spec.managed_entity) : that.entity; + /** + * Show pagination control + * @property {boolean} + */ that.pagination = spec.pagination === undefined ? true : spec.pagination; + + /** + * Get complete records on search, otherwise pkeys only. + */ that.search_all_entries = spec.search_all_entries; + + /** + * Sort records + */ that.sort_enabled = spec.sort_enabled === undefined ? true : spec.sort_enabled; + + /** + * Records are selectable + * + * Ie. by checkboxes + */ that.selectable = spec.selectable === undefined ? true : spec.selectable; + + /** + * Raised when selection changes + * @event + */ that.select_changed = IPA.observer(); + /** + * Record's attribute name which controls whether row will be displayed + * as enabled or disabled. + * + * Mutually exclusive with `row_disabled_attribute` + * @property {string} + */ that.row_enabled_attribute = spec.row_enabled_attribute; + + /** + * Same as `row_enabled_attribute` + * @property {string} + */ that.row_disabled_attribute = spec.row_disabled_attribute; + + /** + * Name of record's details facet + * @property {string} + */ that.details_facet_name = spec.details_facet || 'default'; + /** + * Name of facet's table + */ that.table_name = spec.table_name; + /** + * Facet's table columns + */ that.columns = $.ordered_map(); + /** + * Get all columns + */ that.get_columns = function() { return that.columns.values; }; + /** + * Get column with given name + * @param {string} name column name + */ that.get_column = function(name) { return that.columns.get(name); }; + /** + * Add column + * @param {IPA.column} column + */ that.add_column = function(column) { column.entity = that.managed_entity; column.facet = that; that.columns.put(column.name, column); }; + /** + * Create column according to spec and add it to column collection + * @param {Object} spec column spec + */ that.create_column = function(spec) { var column; if (spec instanceof Object) { @@ -1124,15 +1621,31 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { return column; }; + /** + * Same as `create_column` + * @deprecated + */ that.column = function(spec){ that.create_column(spec); return that; }; + /** + * @inheritDoc + */ that.create_content = function(container) { that.table.create(container); }; + /** + * Transforms data into records and displays them in the end. + * + * 1. table is loaded with supplied data + * 2. expire flag is cleared + * + * @fires post_load + * @param {Object} data + */ that.load = function(data) { that.facet_load(data); @@ -1162,6 +1675,14 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { }; + /** + * Transforms data into records and displays them in the end. + * + * It's expected that `data` contain complete records. + * + * @protected + * @param {Object} data + */ that.load_all = function(data) { var result = data.result.result; @@ -1181,6 +1702,15 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { } }; + /** + * Create a map with records as values and pkeys as keys + * + * Extracts records from data, where data originates from RPC command. + * + * @protected + * @param {Object} data RPC command data + * @return {ordered_map} record map + */ that.get_records_map = function(data) { var records_map = $.ordered_map(); @@ -1198,6 +1728,17 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { return records_map; }; + /** + * Transforms data into records and displays them in the end. + * + * - subset is selected if data contains more than page-size results + * - page is selected based on `state.page` + * - get complete records by `get_records()` method when data contains only + * pkeys (skipped if table has only one column - pkey) + * + * @protected + * @param {Object} data + */ that.load_page = function(data) { // get primary keys (and the complete records if search_all_entries is true) @@ -1277,6 +1818,13 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { ); }; + /** + * Clear table and add new rows with supplied records. + * + * Select previously selected rows. + * + * @param {Array.<Object>} records + */ that.load_records = function(records) { that.table.empty(); for (var i=0; i<records.length; i++) { @@ -1285,6 +1833,15 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { that.table.set_values(that.selected_values); }; + /** + * Add new row to table + * + * Enables/disables row according to `row_enabled_attribute` or + * `row_disabled_attribute` and optional column formatter for that attr. + * + * @protected + * @param {Object} record + */ that.add_record = function(record) { var tr = that.table.add_record(record); @@ -1305,10 +1862,24 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { that.table.set_row_enabled(tr, value); }; + /** + * Get command name used in get_records + * @protected + * @return {string} command name + */ that.get_records_command_name = function() { return that.managed_entity.name+'_get_records'; }; + /** + * Create batch RPC command for obtaining complete records for each supplied + * primary key. + * + * @protected + * @param {Array.<string>} pkeys primary keys + * @param {Function} on_success command success handler + * @param {Function} on_failure command error handler + */ that.create_get_records_command = function(pkeys, on_success, on_error) { var batch = IPA.batch_command({ @@ -1336,6 +1907,14 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { return batch; }; + /** + * Execute command for obtaining complete records + * + * @protected + * @param {Array.<string>} pkeys primary keys + * @param {Function} on_success command success handler + * @param {Function} on_failure command error handler + */ that.get_records = function(pkeys, on_success, on_error) { var batch = that.create_get_records_command(pkeys, on_success, on_error); @@ -1343,10 +1922,24 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { batch.execute(); }; + /** + * Get values selected in a table (checked rows) + * @return {Array.<string>} values + */ that.get_selected_values = function() { return that.table.get_selected_values(); }; + /** + * Create table + * + * - reflect facet settings (pagination, scrollable, ...) + * - create columns + * - override handler for pagination + * + * @protected + * @param {entity.entity} entity table entity + */ that.init_table = function(entity) { that.table = IPA.table_widget({ @@ -1418,6 +2011,9 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { }; }; + /** + * Create and add columns based on spec + */ that.init_table_columns = function() { var columns = spec.columns || []; for (var i=0; i<columns.length; i++) { @@ -1432,69 +2028,208 @@ exp.table_facet = IPA.table_facet = function(spec, no_init) { return that; }; +/** + * Facet group + * + * Collection of facets with similar purpose. + * + * @class facet.facet_group + * @alternateClassName IPA.facet_group + */ exp.facet_group = IPA.facet_group = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Name + * @property {string} + */ that.name = spec.name; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + /** + * Facet collection + * @property {ordered_map} + */ that.facets = $.ordered_map(); + /** + * Add facet to the map + * @param {facet.facet} facet + */ that.add_facet = function(facet) { that.facets.put(facet.name, facet); }; + /** + * Get facet with given name + * @param {string} name + * @return {facet.facet/null} + */ that.get_facet = function(name) { return that.facets.get(name); }; + /** + * Get index of facet with given name + * @param {string} name + * @return {facet.facet/null} + */ that.get_facet_index = function(name) { return that.facets.get_key_index(name); }; + /** + * Get facet by position in collection + * @param {number} index + * @return {facet.facet/null} + */ that.get_facet_by_index = function(index) { return that.facets.get_value_by_index(index); }; - that.get_facet_count = function(index) { + /** + * Get number of facet in collection + * @return {number} count + */ + that.get_facet_count = function() { return that.facets.length; }; return that; }; +/** + * Action + * + * @class facet.action + * @alternateClassName IPA.action + */ exp.action = IPA.action = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Name + * + * Action identifier within facet + * @property {string} + */ that.name = spec.name; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + /** + * Enabled + * + * Action can't be executed when not enabled. + * @property {boolean} + * @readonly + */ that.enabled = spec.enabled !== undefined ? spec.enabled : true; + + /** + * List of states required by action to be enabled + * @property {Array.<string>} + */ that.enable_cond = spec.enable_cond || []; + + /** + * List of states which makes action disabled + * @property {Array.<string>} + */ that.disable_cond = spec.disable_cond || []; + + /** + * Value of `enabled` property changed + * @event + */ that.enabled_changed = IPA.observer(); + /** + * Controls whether action or representing widget should be visible. + * + * Action can't be executed when not visible. + * @property {boolean} + * @readonly + */ that.visible = spec.visible !== undefined ? spec.visible : true; + + /** + * List of states required by action to be visible + * @property {Array.<string>} + */ that.show_cond = spec.show_cond || []; + + /** + * List of states which makes action not visible + * @property {Array.<string>} + */ that.hide_cond = spec.hide_cond || []; + + /** + * Value of `visible` property changed + * @event + */ that.visible_changed = IPA.observer(); + /** + * Action execution logic + * + * One has to set `handler` or override `execute_action` method. + * + * @property {Function} handler + * @property {facet.facet} handler.facet + * @property {Function} handler.on_success + * @property {Function} handler.on_error + */ that.handler = spec.handler; + /** + * Controls whether action must be confirmed. + * + * If so, confirm dialog is displayed before actual execution. + * @property {boolean} + */ that.needs_confirm = spec.needs_confirm !== undefined ? spec.needs_confirm : false; + + /** + * Message to be displayed in confirm dialog + * @property {string} + */ that.confirm_msg = text.get(spec.confirm_msg || '@i18n:actions.confirm'); + /** + * Spec of confirm dialog + * + * Defaults to: {@link IPA.confirm_dialog} + */ that.confirm_dialog = spec.confirm_dialog !== undefined ? spec.confirm_dialog : IPA.confirm_dialog; - - + /** + * Performs actual action execution + * + * - override point + * + * @protected + * @param {facet.facet} facet + * @param {Function} on_success + * @param {Function} on_error + */ that.execute_action = function(facet, on_success, on_error) { if (that.handler) { @@ -1502,6 +2237,16 @@ exp.action = IPA.action = function(spec) { } }; + /** + * Execute action + * + * - only if enabled and visible + * - confirm dialog is display if configured + * + * @param {facet.facet} facet + * @param {Function} on_success + * @param {Function} on_error + */ that.execute = function(facet, on_success, on_error) { if (!that.enabled || !that.visible) return; @@ -1529,14 +2274,33 @@ exp.action = IPA.action = function(spec) { that.execute_action(facet, on_success, on_error); }; + /** + * Set confirm message to confirm dialog + * @protected + * @param {facet.facet} facet + */ that.update_confirm_dialog = function(facet) { that.dialog.message = that.get_confirm_message(facet); }; + /** + * Get confirm message + * + * - override point for message modifications + * + * @protected + * @param {facet.facet} facet + */ that.get_confirm_message = function(facet) { return that.confirm_msg; }; + /** + * Setter for `enabled` + * + * @fires enabled_changed + * @param {boolean} enabled + */ that.set_enabled = function(enabled) { var old = that.enabled; @@ -1548,6 +2312,12 @@ exp.action = IPA.action = function(spec) { } }; + /** + * Setter for `visible` + * + * @fires enabled_changed + * @param {boolean} visible + */ that.set_visible = function(visible) { var old = that.visible; @@ -1562,14 +2332,33 @@ exp.action = IPA.action = function(spec) { return that; }; +/** + * Action collection and state reflector + * + * - sets `enabled` and `visible` action properties at action state change + * and facet load + * + * @class facet.action_holder + * @alternateClassName IPA.action_holder + */ exp.action_holder = IPA.action_holder = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Collection of actions + * @property {ordered_map} + * @protected + */ that.actions = $.ordered_map(); + /** + * Build actions defined in spec. + * Register handlers for facet events(`action_state.changed`, `post_load`) + * @param {facet.facet} facet + */ that.init = function(facet) { var i, action, actions; @@ -1586,6 +2375,12 @@ exp.action_holder = IPA.action_holder = function(spec) { that.facet.post_load.attach(that.on_load); }; + /** + * Evaluate actions `visibility` and `enable` according to action conditions + * and supplied state + * + * @param {Array.<string>} state + */ that.state_changed = function(state) { var actions, action, i, enabled, visible; @@ -1603,14 +2398,29 @@ exp.action_holder = IPA.action_holder = function(spec) { } }; + /** + * Get action with given named + * @param {string} name + * @return {facet.action} + */ that.get = function(name) { return that.actions.get(name); }; + /** + * Add action to collection + * @param {facet.action} action + */ that.add = function(action) { that.actions.put(action.name, action); }; + /** + * Facet load event handler + * + * - gets action state and evaluates action conditions + * @protected + */ that.on_load = function() { var state = that.facet.action_state.get(); that.state_changed(state); @@ -1619,22 +2429,60 @@ exp.action_holder = IPA.action_holder = function(spec) { return that; }; +/** + * Facet action state + * + * @class facet.state + * @alternateClassName IPA.state + */ exp.state = IPA.state = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * State map + * + * - key: evaluator's name + * - value: evaluator's value + * @property {ordered_map} + * @protected + */ that.state = $.ordered_map(); - //when state changes. Params: state, Context: this + /** + * Raised when state changes. + * + * - params: state + * - context: this + * @event + */ that.changed = IPA.observer(); + /** + * State evaluators + * @property {Array.<facet.state_evaluator>} + */ that.evaluators = builder.build('state_evaluator', spec.evaluators) || []; + + /** + * Summary evaluators + * @property {facet.summary_evaluator} + */ that.summary_evaluator = builder.build('', spec.summary_evaluator || IPA.summary_evaluator); + /** + * Summary conditions + * @property {Array.<Object>} + */ that.summary_conditions = builder.build('', spec.summary_conditions) || []; + /** + * Initializes evaluators + * + * @param {facet.facet} facet + */ that.init = function(facet) { var i, evaluator; @@ -1648,6 +2496,10 @@ exp.state = IPA.state = function(spec) { } }; + /** + * Event handler for evaluator's 'changed' event + * @protected + */ that.on_eval_changed = function() { var evaluator = this; @@ -1657,6 +2509,11 @@ exp.state = IPA.state = function(spec) { that.notify(); }; + /** + * Get unified state + * + * @return {Array.<string>} + */ that.get = function() { var state, i; @@ -1672,12 +2529,21 @@ exp.state = IPA.state = function(spec) { return state; }; + /** + * Evaluate and get summary + * @return {Object} summary + */ that.summary = function() { var summary = that.summary_evaluator.evaluate(that); return summary; }; + /** + * Raise change event with state as parameter + * @protected + * @fires changed + */ that.notify = function(state) { state = state || that.get(); @@ -1688,6 +2554,11 @@ exp.state = IPA.state = function(spec) { return that; }; +/** + * Summary evaluator for {@link facet.state} + * @class facet.summary_evaluator + * @alternateClassName IPA.summary_evaluator + */ exp.summary_evaluator = IPA.summary_evaluator = function(spec) { spec = spec || {}; @@ -1723,20 +2594,66 @@ exp.summary_evaluator = IPA.summary_evaluator = function(spec) { return that; }; +/** + * State evaluator for {@link facet.state}. + * + * - Base class for specific evaluators. + * - Evaluator observes facet and reflect its state by a list of string tags + * (evaluated state). + * - Default behavior is that evaluator listens to event, specified by + * `event_name` property. The event is handled by `on_event` method. + * Descendant classes should override this method. Methods like `on_event` + * should notify state change using `notify_on_change` method. + * + * @class facet.state_evaluator + * @alternateClassName IPA.state_evaluator + */ exp.state_evaluator = IPA.state_evaluator = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Name + * @property {string} + */ that.name = spec.name || 'state_evaluator'; + + /** + * Event name + * @property {string} + */ that.event_name = spec.event; - //when state changes. Params: state, Context: this + /** + * State changes + * + * - Params: state + * - Context: this + * @event + * @property {IPA.observer} + */ that.changed = IPA.observer(); + + /** + * Evaluated state + * @property {Array.<string>} + */ that.state = []; + + /** + * State is changed for the first time + * @property {boolean} + */ that.first_pass = true; + /** + * Init the evaluator + * + * - register event listener + * @param {facet.facet} facet + */ that.init = function(facet) { if (that.event_name && facet[that.event_name]) { @@ -1744,9 +2661,20 @@ exp.state_evaluator = IPA.state_evaluator = function(spec) { } }; + /** + * Event handler + * + * @localdoc - intended to be overridden + */ that.on_event = function() { }; + /** + * Notify state change + * @fires changed + * @protected + * @param {Array.<string>} old_state + */ that.notify_on_change = function(old_state) { if (that.first_pass || IPA.array_diff(that.state, old_state)) { @@ -1758,6 +2686,12 @@ exp.state_evaluator = IPA.state_evaluator = function(spec) { return that; }; +/** + * Sets 'dirty' state when facet is dirty + * @class facet.dirty_state_evaluator + * @extends facet.state_evaluator + * @alternateClassName IPA.dirty_state_evaluator + */ exp.dirty_state_evaluator = IPA.dirty_state_evaluator = function(spec) { spec = spec || {}; @@ -1767,6 +2701,10 @@ exp.dirty_state_evaluator = IPA.dirty_state_evaluator = function(spec) { var that = IPA.state_evaluator(spec); that.name = spec.name || 'dirty_state_evaluator'; + /** + * Handles 'dirty_changed' event + * @param {boolean} dirty + */ that.on_event = function(dirty) { var old_state = that.state; @@ -1782,6 +2720,13 @@ exp.dirty_state_evaluator = IPA.dirty_state_evaluator = function(spec) { return that; }; +/** + * Sets 'item-selected' state when table facets selection changes and some + * record is selected. + * @class facet.selected_state_evaluator + * @extends facet.state_evaluator + * @alternateClassName IPA.selected_state_evaluator + */ exp.selected_state_evaluator = IPA.selected_state_evaluator = function(spec) { spec = spec || {}; @@ -1791,6 +2736,10 @@ exp.selected_state_evaluator = IPA.selected_state_evaluator = function(spec) { var that = IPA.state_evaluator(spec); that.name = spec.name || 'selected_state_evaluator'; + /** + * Handles 'select_changed' event + * @param {Array} selected + */ that.on_event = function(selected) { var old_state = that.state; @@ -1806,6 +2755,12 @@ exp.selected_state_evaluator = IPA.selected_state_evaluator = function(spec) { return that; }; +/** + * Sets 'self-service' state when in self-service mode + * @class facet.self_service_state_evaluator + * @extends facet.state_evaluator + * @alternateClassName IPA.self_service_state_evaluator + */ exp.self_service_state_evaluator = IPA.self_service_state_evaluator = function(spec) { spec = spec || {}; @@ -1815,6 +2770,9 @@ exp.self_service_state_evaluator = IPA.self_service_state_evaluator = function(s var that = IPA.state_evaluator(spec); that.name = spec.name || 'self_service_state_evaluator'; + /** + * Evaluates self-service + */ that.on_event = function() { var old_state = that.state; @@ -1830,6 +2788,14 @@ exp.self_service_state_evaluator = IPA.self_service_state_evaluator = function(s return that; }; +/** + * Set desired state when facet parameter is equal to desired value after + * facet event(`post_load` by default). + * + * @class facet.facet_attr_state_evaluator + * @extends facet.state_evaluator + * @alternateClassName IPA.facet_attr_state_evaluator + */ exp.facet_attr_state_evaluator = IPA.facet_attr_state_evaluator = function(spec) { spec = spec || {}; @@ -1838,10 +2804,27 @@ exp.facet_attr_state_evaluator = IPA.facet_attr_state_evaluator = function(spec) var that = IPA.state_evaluator(spec); that.name = spec.name || 'facet_attr_se'; + + /** + * Facet attribute name + * @property {string} + */ that.attribute = spec.attribute; + + /** + * Value to compare + */ that.value = spec.value; + + /** + * State to add when value is equal + * @property {string} + */ that.state_value = spec.state_value; + /** + * Compare facet's value with desired and set state if equal. + */ that.on_event = function() { var old_state = that.state; @@ -1859,6 +2842,13 @@ exp.facet_attr_state_evaluator = IPA.facet_attr_state_evaluator = function(spec) return that; }; +/** + * Set `read_only` state when facet is `read_only` + * + * @class facet.read_only_state_evaluator + * @extends facet.facet_attr_state_evaluator + * @alternateClassName IPA.read_only_state_evaluator + */ exp.read_only_state_evaluator = IPA.read_only_state_evaluator = function(spec) { spec = spec || {}; @@ -1872,6 +2862,13 @@ exp.read_only_state_evaluator = IPA.read_only_state_evaluator = function(spec) { return that; }; +/** + * Set `direct` state when facet's association_type property is `direct` + * + * @class facet.association_type_state_evaluator + * @extends facet.facet_attr_state_evaluator + * @alternateClassName IPA.association_type_state_evaluator + */ exp.association_type_state_evaluator = IPA.association_type_state_evaluator = function(spec) { spec = spec || {}; @@ -1885,26 +2882,92 @@ exp.association_type_state_evaluator = IPA.association_type_state_evaluator = fu return that; }; +/** + * Button for executing facet action + * + * Usable as facet control button in {@link facet.control_buttons_widget}. + * + * @class facet.action_button_widget + * @extends IPA.widget + * @alternateClassName IPA.action_button_widget + */ exp.action_button_widget = IPA.action_button_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); + /** + * Name + * @property {string} + */ that.name = spec.name; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + + /** + * Tooltip + * @property {string} + */ that.tooltip = text.get(spec.tooltip); + + /** + * Button href + * + * - purely visual thing, the click itself is handled internally. + * @property {string} + */ that.href = spec.href || that.name; + + /** + * Icon name + * @property {string} + */ that.icon = spec.icon; + /** + * Name of action this button should execute + * @property {string} + */ that.action_name = spec.action || that.name; + /** + * Enabled + * @property {boolean} + * @readonly + */ that.enabled = spec.enabled !== undefined ? spec.enabled : true; + + /** + * Visible + * @property {boolean} + * @readonly + */ that.visible = spec.visible !== undefined ? spec.visible : true; + /** + * Subject to removal + * @deprecated + */ that.show_cond = spec.show_cond || []; + + /** + * Subject to removal + * @deprecated + */ that.hide_cond = spec.hide_cond || []; + /** + * Init button + * + * - set facet, action + * - register event listeners + * @param {facet.facet} facet + */ that.init = function(facet) { that.facet = facet; @@ -1913,6 +2976,9 @@ exp.action_button_widget = IPA.action_button_widget = function(spec) { that.action.visible_changed.attach(that.set_visible); }; + /** + * @inheritDoc + */ that.create = function(container) { that.widget_create(container); @@ -1932,6 +2998,11 @@ exp.action_button_widget = IPA.action_button_widget = function(spec) { that.set_visible(that.action.visible); }; + /** + * Button click handler + * + * Executes action by default. + */ that.on_click = function() { if (!that.enabled) return; @@ -1939,6 +3010,10 @@ exp.action_button_widget = IPA.action_button_widget = function(spec) { that.action.execute(that.facet); }; + /** + * Enabled setter + * @param {boolean} enabled + */ that.set_enabled = function(enabled) { that.widget_set_enabled(enabled); @@ -1951,6 +3026,10 @@ exp.action_button_widget = IPA.action_button_widget = function(spec) { } }; + /** + * Visible setter + * @param {boolean} visible + */ that.set_visible = function(visible) { that.visible = visible; @@ -1967,15 +3046,30 @@ exp.action_button_widget = IPA.action_button_widget = function(spec) { return that; }; +/** + * Facet button bar + * + * @class facet.control_buttons_widget + * @extends IPA.widget + * @alternateClassName IPA.control_buttons_widget + */ exp.control_buttons_widget = IPA.control_buttons_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); + /** + * Buttons + * @property {Array.<facet.action_button_widget>} + */ that.buttons = builder.build('widget', spec.buttons, {}, { $factory: exp.action_button_widget} ) || []; + /** + * Initializes buttons + * @param {facet.facet} facet + */ that.init = function(facet) { var i; @@ -1987,6 +3081,9 @@ exp.control_buttons_widget = IPA.control_buttons_widget = function(spec) { } }; + /** + * @inheritDoc + */ that.create = function(container) { that.container = $('<div/>', { @@ -2003,6 +3100,13 @@ exp.control_buttons_widget = IPA.control_buttons_widget = function(spec) { return that; }; +/** + * Evaluate state by enable and disable condition + * + * @member facet + * @return {boolean} true - all enable condition are met and none disable condition + * is met + */ exp.eval_cond = IPA.eval_cond = function(enable_cond, disable_cond, state) { var i, cond; @@ -2028,6 +3132,13 @@ exp.eval_cond = IPA.eval_cond = function(enable_cond, disable_cond, state) { return true; }; +/** + * Action list widget to be displayed in facet header + * + * @class facet.action_list_widget + * @extends IPA.composite_widget + * @alternateClassName IPA.action_list_widget + */ exp.action_list_widget = IPA.action_list_widget = function(spec) { spec = spec || {}; @@ -2051,9 +3162,27 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { var that = IPA.composite_widget(spec); + /** + * Names of actions, which should be later obtained from facet + * @property {Array.<string>} + */ that.action_names = spec.actions || []; + + /** + * Actions + * @property {ordered_map} + */ that.actions = $.ordered_map(); + /** + * Initializes action list + * + * - set facet + * - get actions from facet + * - init child widgets + * + * @param {facet.facet} facet + */ that.init = function(facet) { var options, actions, action, name, i; @@ -2075,6 +3204,14 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { that.init_options(); }; + /** + * Add action + * @param {facet.action} action + * @param {boolean} [batch] Set to `true` when adding multiple actions to + * prevent unnecessary option initialization and + * recreation. Set it back to `false` when adding + * last option. + */ that.add_action = function(action, batch) { that.actions.put(action.name, action); action.enabled_changed.attach(that.action_enabled_changed); @@ -2087,6 +3224,9 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { } }; + /** + * Create and set select options from actions + */ that.init_options = function() { var options, actions, action, i; @@ -2105,17 +3245,30 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { that.action_select.options = options; }; + /** + * Force select to recreate options + */ that.recreate_options = function() { that.action_select.create_options(); }; + /** + * Handler for action selection in select + * @protected + */ that.on_action_change = function() { var action = that.get_selected(); that.apply_button.set_enabled(action.enabled); }; + /** + * Handler for click on apply button. + * + * - executes selected action if enabled + * @protected + */ that.on_apply = function() { var action = that.get_selected(); @@ -2127,12 +3280,31 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { } }; + /** + * Global action success handler + * + * @localdoc - override point + * @protected + * @abstract + */ that.on_action_success = function() { }; + /** + * Global action error handler + * + * @localdoc - override point + * @protected + * @abstract + */ that.on_action_error = function() { }; + /** + * Handle action's `enabled_changed` event. + * @protected + * @param {boolean} enabled + */ that.action_enabled_changed = function(enabled) { var action = this; var selected_action = that.get_selected(); @@ -2143,12 +3315,23 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { } }; + /** + * Get selected action + * @return {facet.action} + */ that.get_selected = function() { var selected = that.action_select.save()[0]; var action = that.actions.get(selected); return action; }; + /** + * Subject to removal + * + * This method is full of bugs. + * + * @deprecated + */ that.get_disabled = function() { var disabled = []; @@ -2164,6 +3347,9 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { return disabled; }; + /** + * Select first enabled action + */ that.select_first_enabled = function() { var actions = that.actions.values; @@ -2181,6 +3367,9 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { that.action_select.update([first]); }; + /** + * @inheritDoc + */ that.clear = function() { that.select_first_enabled(); @@ -2189,6 +3378,12 @@ exp.action_list_widget = IPA.action_list_widget = function(spec) { return that; }; +/** + * Facet state + * @extends Stateful + * @mixins Evented + * @class facet.FacetState + */ var FacetState = exp.FacetState = declare([Stateful, Evented], { /** @@ -2230,7 +3425,7 @@ var FacetState = exp.FacetState = declare([Stateful, Evented], { * * Can be called with hash of name/value pairs. * - * Raises 'set' event. + * @fires set */ set: function(name, value) { @@ -2251,7 +3446,7 @@ var FacetState = exp.FacetState = declare([Stateful, Evented], { /** * Set completely new state. Old state is cleared. * - * Raises 'reset' event. + * @fires reset */ reset: function(object) { var old_state = this.clone(); @@ -2270,16 +3465,26 @@ var registry = new Singleton_registry(); reg.set('facet', registry); builder.set('facet', registry.builder); -// Action builder and registry +/** + * Action builder with registry + * @member facet + */ exp.action_builder = builder.get('action'); exp.action_builder.factory = exp.action; reg.set('action', exp.action_builder.registry); -// State Evaluator builder and registry +/** + * State Evaluator builder and registry + * @member facet + */ exp.state_evaluator_builder = builder.get('state_evaluator'); exp.state_evaluator.factory = exp.action; reg.set('state_evaluator', exp.state_evaluator.registry); +/** + * Register widgets to global registry + * @member facet + */ exp.register = function() { var w = reg.widget; 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; diff --git a/install/ui/src/freeipa/hbac.js b/install/ui/src/freeipa/hbac.js index e977b4d26..0fb40373e 100644 --- a/install/ui/src/freeipa/hbac.js +++ b/install/ui/src/freeipa/hbac.js @@ -224,6 +224,7 @@ return { };}; /** + * @ignore * @param {Object} facet spec */ var add_hbacrule_details_facet_widgets = function (spec) { diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js index 0e9fdefb9..b3017650d 100644 --- a/install/ui/src/freeipa/ipa.js +++ b/install/ui/src/freeipa/ipa.js @@ -31,6 +31,16 @@ define(['./jquery', './text'], function($, JSON, i18n, metadata_provider, builder, reg, text) { +/** + * @class + * @singleton + * + * Defined in ipa module. Other modules extend it. + * + * There is a long-term goal to reduce the number of items in this namespace + * and move them to separate modules. + * + */ var IPA = function() { var that = { @@ -40,6 +50,10 @@ var IPA = function() { // live server path that.url = '/ipa/ui/'; + /** + * jQuery AJAX options used by RPC commands + * @property + */ that.ajax_options = { type: 'POST', contentType: 'application/json', @@ -48,17 +62,57 @@ var IPA = function() { processData: false }; + /** + * i18n messages + * @deprecated + * @property {Object} + */ that.messages = {}; + + /** + * User information + * + * - output of ipa user-find --whoami + */ that.whoami = {}; + /** + * Map of entities + * @deprecated + * @property {ordered_map} + */ that.entities = $.ordered_map(); + + /** + * Map of entity factories + * @deprecated + */ that.entity_factories = {}; + /** + * Number of currently active command calls - controls visibility of network indicator + */ that.network_call_count = 0; + /** + * UI state + * @property {boolean} initialized - Intialization completed: + * - metadata + * - user information + * - server configuration + * @property {boolean} logged_kerberos - User authenticated by + * Kerberos negotiation + * @property {boolean} logged_password - User authenticated by password + */ that.ui = {}; - /* initialize the IPA JSON-RPC helper */ + /** + * Load initialization data and initialize UI + * @param {Object} params + * @param {string} params.url - URL of JSON RPC interface + * @param {Function} params.on_success - success callback + * @param {Function} params.on_error - error callback + */ that.init = function(params) { // if current path matches live server path, use live data @@ -160,6 +214,12 @@ var IPA = function() { batch.execute(); }; + /** + * Prepares `user-find --whoami` command + * @protected + * @param {boolean} batch - Specifies if it will be used as single command or + * in a batch. + */ that.get_whoami_command = function(batch) { return IPA.command({ entity: 'user', @@ -175,6 +235,13 @@ var IPA = function() { }); }; + /** + * Executes RPC commands to load metadata + * @protected + * @param {Object} params + * @param {Function} params.on_success + * @param {Function} params.on_error + */ that.init_metadata = function(params) { var objects = IPA.command({ @@ -216,6 +283,12 @@ var IPA = function() { metadata_command.execute(); }; + /** + * Register entity factory in global registry + * @deprecated + * @param {string} name - Entity name + * @param {Function} factory - Entity factory + */ that.register = function(name, factory) { reg.entity.remove(name); reg.entity.register({ @@ -225,15 +298,28 @@ var IPA = function() { }); }; + /** + * Return entity instance with given name from global entity registry + * @deprecated + * @param {string} name - entity name + */ that.get_entity = function(name) { return reg.entity.get(name); }; + /** + * Display network activity indicator + */ that.display_activity_icon = function() { that.network_call_count++; $('.network-activity-indicator').css('visibility', 'visible'); }; + /** + * Hide network activity indicator + * + * - based on network_call_count + */ that.hide_activity_icon = function() { that.network_call_count--; @@ -249,19 +335,26 @@ var IPA = function() { }(); /** - * Basic object - * - * Framework objects created by factories should use this instead of {} when - * creating base objects. As an alternative they can just set __fw_obj - * property. + * Framework objects created by factories should use this + * instead of empty object when creating base objects. As an alternative + * they can just set __fw_obj property. * * __fw_obj property serves for telling the framework that it's instantiated * object and not an object specification (spec). + * + * @class */ -IPA.object = function() { +IPA.object = function(s) { return new IPA.obj_cls(); }; +/** + * Make request on Kerberos authentication url to initialize Kerberos negotiation. + * + * Set result to IPA.ui.logged_kerberos. + * + * @member IPA + */ IPA.get_credentials = function() { var status; @@ -289,6 +382,14 @@ IPA.get_credentials = function() { return status; }; +/** + * Logout + * + * - terminate the session. + * - redirect to logout landing page on success + * + * @member IPA + */ IPA.logout = function() { function show_error(message) { @@ -335,6 +436,14 @@ IPA.logout = function() { $.ajax(request); }; +/** + * Login by username and password + * + * @member IPA + * @param {string} username + * @param {string} password + * @return {string} Logout status - {password-expired, denied, invalid, success} + */ IPA.login_password = function(username, password) { var result = 'invalid'; @@ -383,6 +492,17 @@ IPA.login_password = function(username, password) { return result; }; +/** + * Reset user's password + * + * @member IPA + * @param {string} username + * @param {string} old_password + * @param {string} new_password + * @return {Object} result + * @return {string} result.status + * @return {string} result.message + */ IPA.reset_password = function(username, old_password, new_password) { //possible results: 'ok', 'invalid-password', 'policy-error' @@ -439,6 +559,13 @@ IPA.reset_password = function(username, old_password, new_password) { return result; }; +/** + * Check if password is about to expired (based on + * IPA.server_config.ipapwdexpadvnotify). If so, display a notification + * message with a link to reset password dialog. + * + * @member IPA + */ IPA.update_password_expiration = function() { var now, expires, notify_days, diff, message, container; @@ -476,6 +603,11 @@ IPA.update_password_expiration = function() { } }; +/** + * Show password dialog for self-service change of password. + * + * @member IPA + */ IPA.password_selfservice = function() { var reset_dialog = IPA.user_password_dialog({ self_service: true, @@ -495,6 +627,11 @@ IPA.password_selfservice = function() { reset_dialog.open(); }; +/** + * Parse value as UTC date + * @member IPA + * @return Data + */ IPA.parse_utc_date = function(value) { if (!value) return null; @@ -527,14 +664,17 @@ IPA.parse_utc_date = function(value) { /** * Call an IPA command over JSON-RPC. * - * Arguments: - * name - command name (optional) - * entity - command entity (optional) - * method - command method - * args - list of arguments, e.g. [username] - * options - dict of options, e.g. {givenname: 'Pavel'} - * on_success - callback function if command succeeds - * on_error - callback function if command fails + * @class IPA.command + * + * @param {Object} spec - construct specification + * @param {string} spec.name - command name (optional) + * @param {string} spec.entity - command entity(name) (optional) + * @param {string} spec.method - command method + * @param {string[]} spec.args - list of arguments, e.g. ['username'] + * @param {Object} spec.options - dict of options, e.g. {givenname: 'Petr'} + * @param {Function} spec.on_success - callback function if command succeeds + * @param {Function} spec.on_error - callback function if command fails + * */ IPA.command = function(spec) { @@ -542,44 +682,109 @@ IPA.command = function(spec) { var that = IPA.object(); + /** @property {string} name Name */ that.name = spec.name; + /** @property {entity.entity} entity Entity */ that.entity = spec.entity; + + /** @property {string} method Method */ that.method = spec.method; + /** @property {string[]} args Command Arguments */ that.args = $.merge([], spec.args || []); + + /** @property {Object} options Option map */ that.options = $.extend({}, spec.options || {}); + /** + * Success handler + * @property {Function} + * @param {Object} data + * @param {string} text_status + * @param {XMLHttpRequest} xhr + */ that.on_success = spec.on_success; + + /** + * Error handler + * @property {Function} + * @param {XMLHttpRequest} xhr + * @param {string} text_status + * @param {{name:string,message:string}} error_thrown + */ that.on_error = spec.on_error; + /** + * Allow retrying of execution if previous ended as error + * + * Manifested by error dialog. Set it to `false` for custom error dialogs or + * error handling without any dialog. + * @property {Boolean} retry=true + */ that.retry = typeof spec.retry == 'undefined' ? true : spec.retry; + /** @property {string} error_message Default error message */ that.error_message = text.get(spec.error_message || '@i18n:dialogs.batch_error_message', 'Some operations failed.'); + + /** @property {ordered_map.<number,string>} error_messages Error messages map */ that.error_messages = $.ordered_map({ 911: 'Missing HTTP referer. <br/> You have to configure your browser to send HTTP referer header.' }); + /** + * Get command name + * + * - it's `entity.name + '_' + method` + * - or `method` + * @return {string} + */ that.get_command = function() { return (that.entity ? that.entity+'_' : '') + that.method; }; + /** + * Add argument + * @param {string} arg + */ that.add_arg = function(arg) { that.args.push(arg); }; + /** + * Add arguments + * @param {string[]} args + */ that.add_args = function(args) { $.merge(that.args, args); }; + /** + * Set option + * @param {string} name + * @param {Mixed} value + */ that.set_option = function(name, value) { that.options[name] = value; }; + /** + * Extends options map with another options map + * + * @param {{opt1:Mixed, opt2:Mixed}} options + */ that.set_options = function(options) { $.extend(that.options, options); }; + /** + * Add value to an option + * + * - creates a new option if it does not exist yet + * - for option overriding use `set_option` method + * @param {string} name + * @param {Mixed} value + */ that.add_option = function(name, value) { var values = that.options[name]; if (!values) { @@ -589,14 +794,26 @@ IPA.command = function(spec) { values.push(value); }; + /** + * Get option value + * @return {Mixed} + */ that.get_option = function(name) { return that.options[name]; }; + /** + * Remove option from option map + */ that.remove_option = function(name) { delete that.options[name]; }; + /** + * Execute the command. + * + * Set `on_success` and/or `on_error` handlers to be informed about result. + */ that.execute = function() { function dialog_open(xhr, text_status, error_thrown) { @@ -788,6 +1005,15 @@ IPA.command = function(spec) { $.ajax(that.request); }; + /** + * Parse successful command result and get all errors. + * @protected + * @param {IPA.command} command + * @param {Object} result + * @param {string} text_status + * @param {XMLHttpRequest} xhr + * @return {IPA.error_list} + */ that.get_failed = function(command, result, text_status, xhr) { var errors = IPA.error_list(); if(result && result.failed) { @@ -809,12 +1035,21 @@ IPA.command = function(spec) { return errors; }; + /** + * Check if command accepts option + * @param {string} option_name + * @return {Boolean} + */ that.check_option = function(option_name) { var metadata = IPA.get_command_option(that.get_command(), option_name); return metadata !== null; }; + /** + * Encodes command into JSON-RPC command object + * @return {Object} + */ that.to_json = function() { var json = {}; @@ -827,6 +1062,10 @@ IPA.command = function(spec) { return json; }; + /** + * Encodes command into CLI command string + * @return {string} + */ that.to_string = function() { var string = that.get_command().replace(/_/g, '-'); @@ -844,7 +1083,18 @@ IPA.command = function(spec) { return that; }; -IPA.batch_command = function (spec) { +/** + * Call multiple IPA commands in a batch over JSON-RPC. + * + * @class IPA.batch_command + * @extends IPA.command + * + * @param {Object} spec + * @param {Array.<IPA.command>} spec.commands - IPA commands to be executed + * @param {Function} spec.on_success - callback function if command succeeds + * @param {Function} spec.on_error - callback function if command fails + */ +IPA.batch_command = function(spec) { spec = spec || {}; @@ -852,22 +1102,40 @@ IPA.batch_command = function (spec) { var that = IPA.command(spec); + /** @property {IPA.command[]} commands Commands */ that.commands = []; + /** @property {IPA.error_list} errors Errors */ that.errors = IPA.error_list(); + + /** + * Show error if some command fail + * @property {Boolean} show_error=true + */ that.show_error = typeof spec.show_error == 'undefined' ? true : spec.show_error; + /** + * Add command + * @param {IPA.command} command + */ that.add_command = function(command) { that.commands.push(command); that.add_arg(command.to_json()); }; + /** + * Add commands + * @param {IPA.command[]} commands + */ that.add_commands = function(commands) { for (var i=0; i<commands.length; i++) { that.add_command(commands[i]); } }; + /** + * @inheritDoc + */ that.execute = function() { that.errors.clear(); @@ -886,6 +1154,16 @@ IPA.batch_command = function (spec) { command.execute(); }; + /** + * Internal XHR success handler + * + * Parses data and looks for errors. `on_success` or `on_error` is then + * called. + * @protected + * @param {Object} data + * @param {string} text_status + * @param {XMLHttpRequest} xhr + */ that.batch_command_on_success = function(data, text_status, xhr) { for (var i=0; i<that.commands.length; i++) { @@ -965,6 +1243,13 @@ IPA.batch_command = function (spec) { } }; + /** + * Internal XHR error handler + * @protected + * @param {XMLHttpRequest} xhr + * @param {string} text_status + * @param {{name:string,message:string}} error_thrown + */ that.batch_command_on_error = function(xhr, text_status, error_thrown) { // TODO: undefined behavior if (that.on_error) { @@ -975,16 +1260,45 @@ IPA.batch_command = function (spec) { return that; }; - +/** + * Call multiple IPA commands over JSON-RPC separately and wait for every + * command's response. + * + * - concurrent command fails if any command fails + * - result is reported when each command finishes + * + * @class IPA.concurrent_command + * + * @param {Object} spec - construct specification + * @param {Array.<IPA.command>} spec.commands - IPA commands to execute + * @param {Function} spec.on_success - callback function if each command succeed + * @param {Function} spec.on_error - callback function one command fails + * + */ IPA.concurrent_command = function(spec) { spec = spec || {}; var that = IPA.object(); + /** @property {IPA.command[]} commands Commands */ that.commands = []; + + /** + * Success handler + * @property {Function} + */ that.on_success = spec.on_success; + + /** + * Error handler + * @property {Function} + */ that.on_error = spec.on_error; + /** + * Add commands + * @param {IPA.command[]} commands + */ that.add_commands = function(commands) { if(commands && commands.length) { @@ -996,6 +1310,9 @@ IPA.concurrent_command = function(spec) { } }; + /** + * Execute the commands one by one. + */ that.execute = function() { var command_info, command, i; @@ -1035,6 +1352,10 @@ IPA.concurrent_command = function(spec) { } }; + /** + * Internal error handler + * @protected + */ that.error_handler = function(command_info, xhr, text_status, error_thrown) { command_info.completed = true; @@ -1046,6 +1367,10 @@ IPA.concurrent_command = function(spec) { that.command_completed(); }; + /** + * Internal success handler + * @protected + */ that.success_handler = function(command_info, data, text_status, xhr) { command_info.completed = true; @@ -1057,6 +1382,11 @@ IPA.concurrent_command = function(spec) { that.command_completed(); }; + /** + * Check if all commands finished. + * If so, report it. + * @protected + */ that.command_completed = function() { var all_completed = true; @@ -1077,6 +1407,10 @@ IPA.concurrent_command = function(spec) { } }; + /** + * Call each command's success handler and `on_success`. + * @protected + */ that.on_success_all = function() { for(var i=0; i < that.commands.length; i++) { @@ -1095,6 +1429,10 @@ IPA.concurrent_command = function(spec) { } }; + /** + * Call each command's error handler and `on_success`. + * @protected + */ that.on_error_all = function() { if(that.on_error) { @@ -1116,11 +1454,24 @@ IPA.concurrent_command = function(spec) { return that; }; +/** + * Build object with {@link builder}. + * @member IPA + * @param {Object} spec - contruction spec + * @param {Object} context + * @param {Object} overrides + */ IPA.build = function(spec, context, overrides) { return builder.build(null, spec, context, overrides); }; +/** + * Create a object defined by spec with IPA.object as parent. + * @member IPA + * @param {Object} spec + * @return {Object} new object with all properties as spec + */ IPA.default_factory = function(spec) { spec = spec || {}; @@ -1132,23 +1483,46 @@ IPA.default_factory = function(spec) { return that; }; -/* helper function used to retrieve information about an attribute */ +/** + * Helper function used to retrieve information about an object attribute from metadata + * @member IPA + * @param {string} entity_name + * @param {string} name - attribute name + */ IPA.get_entity_param = function(entity_name, name) { return metadata_provider.get(['@mo-param', entity_name, name].join(':')); }; +/** + * Helper function used to retrieve information about an command argument from metadata + * @member IPA + * @param {string} command_name + * @param {string} arg_name - argument name + */ IPA.get_command_arg = function(command_name, arg_name) { return metadata_provider.get(['@mc-arg', command_name, arg_name].join(':')); }; + +/** + * Helper function used to retrieve information about an command option from metadata + * @member IPA + * @param {string} command_name + * @param {string} option_name - argument name + */ IPA.get_command_option = function(command_name, option_name) { return metadata_provider.get(['@mc-opt', command_name, option_name].join(':')); }; -/* helper function used to retrieve attr name with members of type `member` */ +/** + * Helper function used to retrieve information about an attribute member + * @member IPA + * @param {string} obj_name - object(entity) name + * @param {string} member - attribute member + */ IPA.get_member_attribute = function(obj_name, member) { var obj = metadata_provider.get('@mo:'+obj_name); @@ -1169,6 +1543,11 @@ IPA.get_member_attribute = function(obj_name, member) { return null; }; +/** + * Create HTML representation of network spinner. + * @member IPA + * @return {HTMLElement} Network spinner node + */ IPA.create_network_spinner = function(){ var span = $('<span/>', { 'class': 'network-activity-indicator' @@ -1179,6 +1558,18 @@ IPA.create_network_spinner = function(){ return span; }; +/** + * Dirty dialog + * + * Should be used as an indication of unsaved changes on page when leaving the + * page. Offers user to safe/reset the changes or cancel the action. + * + * @class + * @extends IPA.dialog + * @param {Object} spec + * @param {IPA.facet} spec.facet - Dirty facet + * @param {string} [spec.message] - Displayed message + */ IPA.dirty_dialog = function(spec) { spec = spec || {}; @@ -1186,9 +1577,14 @@ IPA.dirty_dialog = function(spec) { spec.width = spec.width || '25em'; var that = IPA.dialog(spec); + + /** @property {facet.facet} facet Facet*/ that.facet = spec.facet; + + /** @property {string} message Dirty message*/ that.message = text.get(spec.message || '@i18n:dialogs.dirty_message'); + /** @inheritDoc */ that.create = function() { that.container.append(that.message); }; @@ -1222,12 +1618,25 @@ IPA.dirty_dialog = function(spec) { } }); + /** + * Function which is called when user click on 'update' or 'delete' button + */ that.callback = function() { }; return that; }; +/** + * Error dialog + * + * Serves for notifying an error in RPC command. + * + * @class + * @extends IPA.dialog + * @mixins IPA.confirm_mixin + * @param {Object} spec + */ IPA.error_dialog = function(spec) { spec = spec || {}; @@ -1240,20 +1649,32 @@ IPA.error_dialog = function(spec) { IPA.confirm_mixin().apply(that); + /** @property {XMLHttpRequest} xhr Command's xhr */ that.xhr = spec.xhr || {}; + /** @property {string} text_status Command's text status */ that.text_status = spec.text_status || ''; + /** @property {{name:string,message:string}} error_thrown Command's error */ that.error_thrown = spec.error_thrown || {}; + /** @property {IPA.command} command Command */ that.command = spec.command; + /** @property {IPA.error_list} errors Errors */ that.errors = spec.errors; + /** @property {string[]} visible_buttons=['retry', 'cancel'] Visible button names */ that.visible_buttons = spec.visible_buttons || ['retry', 'cancel']; - + /** + * Beautify error message + * + * Multi-lined text may contain TAB character as first char of the line + * to hint at marking the whole line differently. + * @param {jQuery} container Container to add the beautified message. + * @param {string} message + */ that.beautify_message = function(container, message) { var lines = message.split(/\n/g); var line_span; for(var i=0; i<lines.length; i++) { - // multi-lined text may contain TAB character as first char of the line - // to hint at marking the whole line differently + if (lines[i].charAt(0) == '\t') { line_span = $('<p />', { 'class': 'error-message-hinted', @@ -1267,6 +1688,7 @@ IPA.error_dialog = function(spec) { } }; + /** @inheritDoc */ that.create = function() { if (that.error_thrown.url) { $('<p/>', { @@ -1327,12 +1749,15 @@ IPA.error_dialog = function(spec) { } }; + /** + * Create dialog buttons + * @protected + */ that.create_buttons = function() { - /** - * When a user initially opens the Web UI without a Kerberos - * ticket, the messages including the button labels have not - * been loaded yet, so the button labels need default values. - */ + + // When a user initially opens the Web UI without a Kerberos + // ticket, the messages including the button labels have not + // been loaded yet, so the button labels need default values. var visible = that.visible_buttons.indexOf('retry') > -1; var label = text.get('@i18n:buttons.retry', 'Retry'); @@ -1368,19 +1793,35 @@ IPA.error_dialog = function(spec) { }); }; + /** + * Retry handler + * @protected + */ that.on_retry = function() { that.close(); that.command.execute(); }; + /** + * OK button handler + * @protected + */ that.on_ok = function() { that.close(); }; + /** + * Cancel button and negative confirmation handler + * @protected + */ that.on_cancel = function() { that.close(); }; + /** + * Positive confirmation handler + * @protected + */ that.on_confirm = function() { if (that.visible_buttons.indexOf('retry') > -1) that.on_retry(); else that.on_ok(); @@ -1391,13 +1832,23 @@ IPA.error_dialog = function(spec) { return that; }; +/** + * Error list + * + * Collection for RPC command errors. + * + * @class IPA.error_list + * @private + */ IPA.error_list = function() { var that = IPA.object(); + /** Clear errors */ that.clear = function() { that.errors = []; }; + /** Add error */ that.add = function(command, name, message, status) { that.errors.push({ command: command, @@ -1407,10 +1858,15 @@ IPA.error_list = function() { }); }; + /** Add errors */ that.add_range = function(error_list) { that.errors = that.errors.concat(error_list.errors); }; + /** + * Check if there are no errors + * @return {Boolean} + */ that.is_empty = function () { return that.errors.length === 0; }; @@ -1419,6 +1875,14 @@ IPA.error_list = function() { return that; }; +/** + * Error handler for IPA.command which handles error #4304 as success. + * + * 4304 is raised when part of an operation succeeds and the part that failed + * isn't critical. + * @member IPA + * @param {IPA.entity_adder_dialog} adder_dialog + */ IPA.create_4304_error_handler = function(adder_dialog) { var set_pkey = function(result) { @@ -1460,6 +1924,16 @@ IPA.create_4304_error_handler = function(adder_dialog) { }; }; +/** + * Unauthorized dialog + * + * Notifies that user's session is expired. It supports forms-based authentication + * and password reset when password is expired. + * + * @class IPA.unauthorized_dialog + * @extends IPA.error_dialog + * @param {Object} spec + */ IPA.unauthorized_dialog = function(spec) { spec = spec || {}; @@ -1515,14 +1989,18 @@ IPA.unauthorized_dialog = function(spec) { var that = IPA.error_dialog(spec); + /** @inheritDoc */ that.title = spec.title || text.get('@i18n:login.login', "Login"); + /** @property {string} message Session expired message */ that.message = text.get(spec.message || '@i18n:ajax.401.message', "Your session has expired. Please re-login."); + /** @property {string} form_auth_msg Forms authentication message */ that.form_auth_msg = text.get(spec.form_auth_msg || '@i18n:login.form_auth', "To login with username and password, enter them in the fields below then click Login."); + /** @property {string} krb_auth_msg Kerberos authentication message */ that.krb_auth_msg = text.get(spec.krb_auth_msg || '@i18n:login.krb_auth_msg', " To login with Kerberos, please make sure you" + " have valid tickets (obtainable via kinit) and " + @@ -1531,21 +2009,29 @@ IPA.unauthorized_dialog = function(spec) { that.krb_auth_msg = that.krb_auth_msg.replace('${host}', window.location.hostname); + /** @property {string} form_auth_failed Forms authentication failure message */ that.form_auth_failed = "<p><strong>Please re-enter your username or password</strong></p>" + "<p>The password or username you entered is incorrect. " + "Please try again (make sure your caps lock is off).</p>" + "<p>If the problem persists, contact your administrator.</p>"; + /** @property {string} password_expired Password expired message */ that.password_expired = "Your password has expired. Please enter a new password."; + /** @property {string} denied Login denied message */ that.denied = "Sorry you are not allowed to access this service."; + /** @inheritDoc */ that.create = function() { that.session_expired_form(); that.create_reset_form(); }; + /** + * Create session expired form + * @protected + */ that.session_expired_form = function() { that.session_form = $('<div\>').appendTo(that.container); @@ -1585,6 +2071,10 @@ IPA.unauthorized_dialog = function(spec) { that.username_widget.value_changed.attach(that.on_username_change); }; + /** + * Create password reset form + * @protected + */ that.create_reset_form = function() { that.reset_form = $('<div\>', { @@ -1611,6 +2101,10 @@ IPA.unauthorized_dialog = function(spec) { that.verify_password_widget = that.widgets.get_widget('reset.verify_password'); }; + /** + * Create dialog buttons + * @protected + */ that.create_buttons = function() { that.buttons.empty(); @@ -1649,12 +2143,17 @@ IPA.unauthorized_dialog = function(spec) { }); }; + /** @inheritDoc */ that.open = function() { that.dialog_open(); that.show_session_form(); that.check_error_reason(); }; + /** + * Check if response contains IPA specific rejection reason. + * @protected + */ that.check_error_reason = function() { if (this.xhr) { var reason = this.xhr.getResponseHeader("X-IPA-Rejection-Reason"); @@ -1664,6 +2163,10 @@ IPA.unauthorized_dialog = function(spec) { } }; + /** + * User name field value change handler + * @protected + */ that.on_username_change = function() { var password_field = that.fields.get_field('password'); @@ -1672,6 +2175,11 @@ IPA.unauthorized_dialog = function(spec) { if (!user_specified) that.password_widget.clear(); }; + /** + * Enable fields with given name + * @protected + * @param {string[]} field_names + */ that.enable_fields = function(field_names) { var field, fields, i, enable; @@ -1683,6 +2191,10 @@ IPA.unauthorized_dialog = function(spec) { } }; + /** + * Shows session expired form. Hides other. + * @protected + */ that.show_session_form = function() { that.current_view = 'session'; @@ -1693,6 +2205,10 @@ IPA.unauthorized_dialog = function(spec) { that.username_widget.focus_input(); }; + /** + * Shows password reset form. Hides other. + * @protected + */ that.show_reset_form = function() { that.current_view = 'reset'; @@ -1706,6 +2222,11 @@ IPA.unauthorized_dialog = function(spec) { that.new_password_widget.focus_input(); }; + /** + * Show login error message - based on reason + * @protected + * @param {"invalid"|"denied"|string} reason + */ that.show_login_error_message = function(reason) { var errors = { 'invalid': that.form_auth_failed, @@ -1720,6 +2241,10 @@ IPA.unauthorized_dialog = function(spec) { } }; + /** + * Cancel handler + * @protected + */ that.on_cancel = function() { that.username_widget.clear(); @@ -1731,6 +2256,10 @@ IPA.unauthorized_dialog = function(spec) { that.show_session_form(); }; + /** + * Initiates login procedure + * @protected + */ that.on_login = function() { var username = that.username_widget.save(); @@ -1760,6 +2289,10 @@ IPA.unauthorized_dialog = function(spec) { } }; + /** + * Login success handler + * @protected + */ that.on_login_success = function() { that.login_error_box.css('display', 'none'); @@ -1769,6 +2302,10 @@ IPA.unauthorized_dialog = function(spec) { that.on_retry(); }; + /** + * Initiates password reset procedure + * @protected + */ that.on_reset = function() { if (!that.validate()) return; @@ -1791,6 +2328,10 @@ IPA.unauthorized_dialog = function(spec) { } }; + /** + * Password reset success handler + * @protected + */ that.on_reset_success = function() { that.login_error_box.css('display', 'none'); @@ -1807,7 +2348,10 @@ IPA.unauthorized_dialog = function(spec) { that.on_login(); }; - //replaces confirm_mixin method + /** + * Key up handler for proper keyboard usage. + * @protected + */ that.on_key_up = function(event) { if (that.switching) { @@ -1836,6 +2380,15 @@ IPA.unauthorized_dialog = function(spec) { return that; }; +/** + * Shorten text to desired number of characters. + * + * If shortened, '...' is appended to the shortened text. + * @member IPA + * @param {string} value - text to shorten + * @param {number} max_length - maximum number of characters + * @return {string} shortened text + */ IPA.limit_text = function(value, max_length) { if (!value) return ''; @@ -1849,6 +2402,13 @@ IPA.limit_text = function(value, max_length) { return limited_text; }; + +/** + * Convert strings to options. + * @member IPA + * @param {string[]} values - options as strings + * @return {Array.<{value,label}>} options + */ IPA.create_options = function(values) { var options = []; @@ -1870,6 +2430,19 @@ IPA.create_options = function(values) { return options; }; +/** + * Check if value is not defined. + * + * True when: + * + * - value is undefined or null or '' + * - value is empty Array + * - value is Array with an empty string ('') + * - value is empty Object- {} + * @member IPA + * @param value - value to check + * @return {boolean} + */ IPA.is_empty = function(value) { var empty = false; @@ -1893,11 +2466,29 @@ IPA.is_empty = function(value) { return empty; }; +/** + * Check if value is not null or undefined. + * @member IPA + * @param value + * @param {boolean} check_empty_str - additional check for empty string + */ IPA.defined = function(value, check_empty_str) { return value !== null && value !== undefined && ((check_empty_str && value !== '') || !check_empty_str); }; + +/** + * Check if arrays differ. + * + * False when: + * + * - length and items or arrays equals (===) + * - undefined state of both is the same + * @member IPA + * @param a + * @param b + */ IPA.array_diff = function(a, b) { if (a === b || (!a && !b)) return false; @@ -1913,10 +2504,23 @@ IPA.array_diff = function(a, b) { return false; }; +/** + * Shows confirm dialog with message + * @member IPA + * @param {string} msg - message + * @return {boolean} confirmed state + */ IPA.confirm = function(msg) { return window.confirm(msg); }; +/** + * Display positive notification message + * @member IPA + * @param {string} message + * @param {number} [timeout=IPA.config.message_timeout] - duration for the + * message to be displayed [ms] + */ IPA.notify_success = function(message, timeout) { if (!message) return; // don't show undefined, null and such @@ -1951,6 +2555,12 @@ IPA.notify_success = function(message, timeout) { }, timeout || IPA.config.message_timeout); }; +/** + * Get number of succeeded commands in RPC command + * @member IPA + * @param {Object} data - RPC command data + * @return {number} + */ IPA.get_succeeded = function(data) { var succeeded = data.result.completed; @@ -1966,6 +2576,15 @@ IPA.get_succeeded = function(data) { return succeeded; }; +/** + * Global configuration + * @member IPA + * @property {number} default_priority - command default priority. Used in + * 'update info' concept + * @property {number} message_timeout - timeout for notification messages + * @property {number} message_fadeout_time + * @property {number} message_fadein_time + */ IPA.config = { default_priority: 500, message_timeout: 3000, // [ms] diff --git a/install/ui/src/freeipa/menu.js b/install/ui/src/freeipa/menu.js index c903e16df..3da643890 100644 --- a/install/ui/src/freeipa/menu.js +++ b/install/ui/src/freeipa/menu.js @@ -18,11 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Menu proxy. - * - * Exports public interface for dealing with menu items. - */ + define([ 'dojo/_base/lang', './app', // creates circular dependency @@ -37,12 +33,20 @@ define([ }, /** + * Menu proxy. + * + * Exports public interface for dealing with menu items. + * @class menu + */ + + /** * Adds menu item. * Takes a spec of menu item. * Normalizes item's name, parent, adds children if specified * - * @param {menu_item} items - * @param {String|menu_item} parent + * @method add_item + * @param {navigation.MenuItem} item + * @param {string|navigation.MenuItem} parent * @param {Object} options */ add_item = function(item, parent, options) { @@ -53,8 +57,8 @@ define([ /** * Removes menu item * - * @param {String|menu_item} name or menu item to remove - * + * @method remove_item + * @param {string|navigation.MenuItem} name or menu item to remove */ remove_item = function(item) { @@ -66,9 +70,10 @@ define([ * Query internal data store by using default search options or supplied * search options. * - * @param Object Query filter - * @param ?Object Search options, overrides default - * @return QueryResult + * @method query + * @param {Object} query + * @param {Object} [search_options] Search options, overrides default + * @return {QueryResult} */ query = function(query, search_options) { @@ -83,6 +88,8 @@ define([ /** * Get current instance of menu + * @method get + * @return {navigation.Menu} */ get = function() { return get_menu(); diff --git a/install/ui/src/freeipa/metadata.js b/install/ui/src/freeipa/metadata.js index d33d3ae8f..cfdc91b11 100644 --- a/install/ui/src/freeipa/metadata.js +++ b/install/ui/src/freeipa/metadata.js @@ -24,6 +24,22 @@ define([ './_base/Search_provider' ], function(lang, Provider, Search_provider) { + /** + * Metadata provider + * + * Contains multiple providers for specific metadata searches: + * + * - `@m:`: provider for whole metadata + * - `@mo:`: provider for objects (entities) + * - `@mc:`: provider for commands + * - `@mo-param:`: search provider for object params + * - `@mc-arg:`: search provider for command arguments + * - `@mc-opt:`: search provider for command options + * + * @class metadata + * @singleton + * @extends _base.Provider + */ var metadata = new Provider({ code: '@m:' }); diff --git a/install/ui/src/freeipa/navigation.js b/install/ui/src/freeipa/navigation.js index 038732cee..8b96d6f97 100644 --- a/install/ui/src/freeipa/navigation.js +++ b/install/ui/src/freeipa/navigation.js @@ -18,15 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Navigation tells application to show certain facet. - * - * It's proxy for navigation/Router instance in current running - * application. - * - * Modules just use the interface, they don't have to care about the logic in - * the background. - */ + define([ 'dojo/_base/lang', './app', // creates circular dependency @@ -41,14 +33,25 @@ define([ }, /** + * Navigation tells application to show certain facet. + * + * It's proxy for navigation/Router instance in current running + * application. + * + * Modules just use the interface, they don't have to care about the logic in + * the background. + * @class navigation + */ + + /** * Sets property of params depending on arg type following way: * for String sets params.facet * for Facet sets params.facet (based on show function) * for Object sets params.args * for Array sets params.pkeys - * + * @ignore * @param Object params - * @param {Object|Facet|String|Function} arg + * @param {Object|facet.facet|string|Function} arg */ set_params = function(params, arg) { if (lang.isArray(arg)) { @@ -78,7 +81,10 @@ define([ * * When it's an object (Facet) and has an entity set it will be * dealt as entity facet. - * + * @method show + * @param {Object|facet.facet|string|Function} arg1 + * @param {Object|facet.facet|string|Function} arg2 + * @param {Object|facet.facet|string|Function} arg3 */ show = function(arg1, arg2, arg3) { @@ -111,17 +117,17 @@ define([ }, /** - * Show entity facet. - * - * @param String Enity name - * @param {Object|Facet|String|Function} arg1 - * @param {Object|Facet|String|Function} arg2 - * @param {Object|Facet|String|Function} arg3 + * Show entity facet * * arg1,arg2,arg3 are: * facet name as String * pkeys as Array * args as Object + * @method show_entity + * @param String Enity name + * @param {Object|facet.facet|string|Function} arg1 + * @param {Object|facet.facet|string|Function} arg2 + * @param {Object|facet.facet|string|Function} arg3 */ show_entity = function(entity_name, arg1, arg2, arg3) { var nav = get_router(); @@ -134,6 +140,10 @@ define([ params.pkeys, params.args); }, + /** + * Show default facet + * @method show_default + */ show_default = function() { // TODO: make configurable return show_entity('user', 'search'); diff --git a/install/ui/src/freeipa/navigation/Menu.js b/install/ui/src/freeipa/navigation/Menu.js index 677171d4d..a5e96d3c9 100644 --- a/install/ui/src/freeipa/navigation/Menu.js +++ b/install/ui/src/freeipa/navigation/Menu.js @@ -18,7 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - define(['dojo/_base/declare', 'dojo/store/Memory', 'dojo/_base/array', @@ -29,67 +28,45 @@ define(['dojo/_base/declare', '../text' ], function(declare, Memory_store, array, lang, Observable, Evented, reg, text) { -/** - * Menu store - * - * Maintains menu hierarchy and selection state. - */ -return declare([Evented], { +return declare([Evented], /** - * Following properties can be specified in menu item spec: - * @property {String} name - * @property {String} label - * @property {String} title - * @property {Number} position: position among siblings - * @property {menu_item_spec_array} children - * @property {String} entity: entity name - * @property {String} facet: facet name - * @property {Boolean} hidden: menu item is no visible, but can serve for - * other evaluations (nested entities) - * - * Following properties are not in created menu item: - * - children + * Menu store * + * Maintains menu hierarchy and selection state. * - * Following properties can be stored in menu item at runtime: - * - * @property {Boolean} selected - * @property {String} parent: name of parent menu item - * @property {String} selected_child: last selected child. Can be set even - * if the child is not selected + * @class navigation.Menu * */ - + { /** * Menu name - * @type String + * @property {String} */ name: null, /** * Dojo Store of menu items - * @type: Store + * @property {Store} */ items: null, /** * Delimiter used in name creation * To avoid having multiple menu items with the same name. - * @type String + * @property {String} */ path_delimiter: '/', /** * Selected menu items - * @type Array of menu items + * @property {Array} */ selected: [], /** * Default search options: sort by position - * - * @type Object + * @property {Object} */ search_options: { sort: [{attribute:'position'}]}, @@ -97,8 +74,8 @@ return declare([Evented], { * Takes a spec of menu item. * Normalizes item's name, parent, adds children if specified * - * @param {menu_item} items - * @param {String|menu_item} parent + * @param {Object} item - spec + * @param {string/Object} parent - name or menu item * @param {Object} options */ add_item: function(item, parent, options) { @@ -168,6 +145,10 @@ return declare([Evented], { return true; }, + /** + * Add multiple items + * @param {Array} items - spec of items + */ add_items: function(/* Array */ items) { array.forEach(items, function(item) { this.add_item(item); @@ -177,8 +158,8 @@ return declare([Evented], { /** * Query internal data store by using default search options. * - * @param Object Query filter - * @return QueryResult + * @param {Object} Query filter + * @return {QueryResult} */ query: function(query) { return this.items.query(query, this.search_options); @@ -186,6 +167,7 @@ return declare([Evented], { /** * Marks item and all its parents as selected. + * @private */ _select: function(item) { @@ -202,7 +184,7 @@ return declare([Evented], { /** * Selects a menu item and all it's ancestors. - * @param {string|menu_item} Menu item to select + * @param {string/navigation.MenuItem} item menu item to select */ select: function(item) { @@ -234,6 +216,11 @@ return declare([Evented], { return select_state; }, + /** + * @param {Object} spec - Specification object + * @param {Array} spec.items - Menu items + * @param {string} spec.name - Name + */ constructor: function(spec) { spec = spec || {}; this.items = new Observable( new Memory_store({ @@ -246,4 +233,4 @@ return declare([Evented], { declare.safeMixin(this, spec); } }); //declare freeipa.menu -}); //define
\ No newline at end of file +}); //define diff --git a/install/ui/src/freeipa/navigation/MenuItem.js b/install/ui/src/freeipa/navigation/MenuItem.js new file mode 100644 index 000000000..1b2358cf6 --- /dev/null +++ b/install/ui/src/freeipa/navigation/MenuItem.js @@ -0,0 +1,98 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2013 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/>. +*/ + +/** + * This is a virtual object which serves only for documentation purposes. It + * describes what properties has the created menu item and what can be part + * of menu item object specification. + * + * Following properties are not in created in menu item: + * + * - children + * + * Following properties may be part of menu item at runtime: + * + * - selected + * - parent + * - selected_child + * + * @class navigation.MenuItem + * @abstract + */ +var MenuItem = { + /** + * Name - menu item identifier + */ + name: '', + + /** + * Visible text + */ + label: '', + + /** + * Title/Tooltip + */ + title: '', + + /** + * Position for ordering + */ + position: 0, + + /** + * Children + * + * - next navigation level + * @property {Array.<navigation.MenuItem>} + */ + children: null, + + /** + * Entity name + */ + entity: '', + + /** + * Facet name + */ + facet: '', + + /** + * Control item visibility but can serve for other evaluations (nested entities) + */ + hidden: '', + + /** + * Runtime property + * Should not be set by hand. Indicates whether item is selected. + */ + selected: false, + + /** + * Parent menu item's name + */ + parent: null, + + /** + * Some child is selectd. Runtime property. + */ + selected_child: null +}; diff --git a/install/ui/src/freeipa/navigation/Router.js b/install/ui/src/freeipa/navigation/Router.js index 3ac8357ed..a3b2a6791 100644 --- a/install/ui/src/freeipa/navigation/Router.js +++ b/install/ui/src/freeipa/navigation/Router.js @@ -30,31 +30,34 @@ define(['dojo/_base/declare', function(declare, lang, array, Evented, ioquery, router, IPA, reg) { /** - * Class navigation + * Router + * * This component keeps menu and routes in sync. It signalizes * other components to display facet by sending 'show-facet' event. * Other components can use navigate_to_* methods to change currently * displayed facet. This change can be canceled in 'facet-change' * event handler. + * @class navigation.Router */ var navigation = declare([Evented], { /** * Holds references to register route handlers. * Can be used for unregistering routes. - * @type Array + * @property {Array.<Function>} */ route_handlers: [], /** * Prefix of all routes for this navigation. Useful for multiple * navigation objects in one application. - * @type String + * @property {string} */ route_prefix: '', /** * Variations of entity routes + * @property {Array.<string>} */ entity_routes: [ '/e/:entity/:facet/:pkeys/*args', @@ -66,6 +69,7 @@ define(['dojo/_base/declare', /** * Variations of simple page routes + * @property {Array.<string>} */ page_routes: [ '/p/:page/*args', @@ -75,7 +79,7 @@ define(['dojo/_base/declare', /** * Used during facet changing. Set it to true in 'facet-change' * event handler to stop the change. - * @type Boolean + * @property {boolean} */ canceled: false, @@ -83,7 +87,7 @@ define(['dojo/_base/declare', * Flag to indicate that next hash change should not invoke showing a * facet. * Main purpose: updating hash. - * @type Boolen + * @property {boolean} */ ignore_next: false, @@ -92,8 +96,8 @@ define(['dojo/_base/declare', * Register a route-handler pair to a dojo.router * Handler will be run in context of this object * - * @param {string|array} route or routes to register - * @param {function} handler to be associated with the route(s) + * @param {string|Array.<string>} route or routes to register + * @param {Function} handler to be associated with the route(s) */ register_route: function(route, handler) { // TODO: add multiple routes for one handler @@ -121,6 +125,7 @@ define(['dojo/_base/declare', /** * Handler for entity routes * Shouldn't be invoked directly. + * @param {Object} event route event args */ entity_route_handler: function(event) { @@ -157,6 +162,7 @@ define(['dojo/_base/declare', /** * General facet route handler * Shouldn't be invoked directly. + * @param {Object} event route event args */ page_route_handler: function(event) { @@ -249,8 +255,8 @@ define(['dojo/_base/declare', /** * Changes hash to supplied * - * @param {String} Hash to set - * @param {Boolean} Whether to suppress following hash change handler + * @param {string} Hash to set + * @param {boolean} Whether to suppress following hash change handler */ update_hash: function(hash, ignore_change) { this.ignore_next = !!ignore_change; @@ -299,7 +305,7 @@ define(['dojo/_base/declare', /** * Creates hash from supplied facet and state. * - * @param {facet} facet + * @param {facet.facet} facet * @param {Object} state */ create_hash: function(facet, state) { @@ -346,6 +352,11 @@ define(['dojo/_base/declare', return keys; }, + /** + * Raise 'error' + * @protected + * @fires error + */ _error: function(msg, type, context) { this.emit('error', { diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index 66116b583..9d4c5eff9 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -20,7 +20,15 @@ define([], function() { +/** + * Specification of menu + * @singleton + * @class navigation.menu_spec + */ var nav = {}; + /** + * Admin menu + */ nav.admin = { name: 'admin', items: [ @@ -131,6 +139,9 @@ var nav = {}; ] }; +/** + * Self-service menu + */ nav.self_service = { name: 'self-service', items: [ diff --git a/install/ui/src/freeipa/netgroup.js b/install/ui/src/freeipa/netgroup.js index 4b2de497a..2922d5b94 100644 --- a/install/ui/src/freeipa/netgroup.js +++ b/install/ui/src/freeipa/netgroup.js @@ -77,6 +77,7 @@ var spec = { /** + * @ignore * @param {Object} facet spec */ var add_netgroup_details_facet_widgets = function (spec) { diff --git a/install/ui/src/freeipa/ordered-map.js b/install/ui/src/freeipa/ordered-map.js index c8c735a39..70e23b1cf 100644 --- a/install/ui/src/freeipa/ordered-map.js +++ b/install/ui/src/freeipa/ordered-map.js @@ -24,6 +24,7 @@ define([ ], function($) { /** * Wrapper for jquery.ordered_map + * @class ordered_map */ return $.ordered_map; });
\ No newline at end of file diff --git a/install/ui/src/freeipa/phases.js b/install/ui/src/freeipa/phases.js index 18acecc8a..86605e3d6 100644 --- a/install/ui/src/freeipa/phases.js +++ b/install/ui/src/freeipa/phases.js @@ -18,18 +18,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Phases module provides access mainly serves as an registration point for - * phase tasks. It also provides access to Phase controller. - */ define([ './_base/Phase_controller' ], function(Phase_controller) { /** * Phases specification object. - * - * @type String[] + * @ignore + * @property {string[]} */ var spec = { phases: [ @@ -47,6 +43,12 @@ define([ /** * Phases module + * + * Provides access mainly serves as an registration point for + * phase tasks. It also provides access to Phase controller. + * + * @class phases + * @singleton */ var phases = { /** @@ -57,9 +59,9 @@ define([ /** * Registers a phase task * - * @param {String} Phase name - * @param {Function} Task handler. Should return promise if async. - * @param {Number} Priority of task. Default 10. + * @param {string} phase_name + * @param {Function} handler Task handler. Should return promise if async. + * @param {number} [priority=10] */ on: function(phase_name, handler, priority) { this.controller.add_task(phase_name, handler, priority); @@ -73,8 +75,8 @@ define([ * after: 'name-of-phase' * position: 'position for new phase' * - * @param {String} Phase name - * @param {Object} Options + * @param {string} phase_name + * @param {Object} options */ add: function(phase_name, options) { this.controller.add_phase(phase_name, null, options); @@ -83,8 +85,8 @@ define([ /** * Checks if phases with given name exists * - * @param {String} Name - * @return {Boolean} + * @param {string} name + * @return {boolean} */ exists: function(name) { return this.controller.exists(name); diff --git a/install/ui/src/freeipa/plugin_loader.js b/install/ui/src/freeipa/plugin_loader.js index ce545301b..de3bc4357 100644 --- a/install/ui/src/freeipa/plugin_loader.js +++ b/install/ui/src/freeipa/plugin_loader.js @@ -18,9 +18,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Plugin loader - */ define([ 'dojo/_base/array', 'dojo/_base/lang', @@ -28,8 +25,17 @@ define([ 'dojo/promise/all' ],function(array, lang, Deferred, all) { + /** + * Plugin loader + * @class + * @singleton + */ var plugin_loader = { + /** + * Register plugins + * @param {Array.<string>} plugins + */ register_plugins: function(plugins) { var packages = []; @@ -44,6 +50,11 @@ define([ require({ packages: packages}); }, + /** + * Load plugin + * @param {string} name + * @return {Promise} + */ load_plugin: function(name) { var plugin_loaded = new Deferred(); @@ -56,6 +67,12 @@ define([ return plugin_loaded.promise; }, + /** + * Load plugins + * + * - loads plugin list from `freeipa/plugins` module. + * @return {Promise} + */ load_plugins: function() { var plugins_loaded = new Deferred(); diff --git a/install/ui/src/freeipa/reg.js b/install/ui/src/freeipa/reg.js index 62319d9bc..e045d4617 100644 --- a/install/ui/src/freeipa/reg.js +++ b/install/ui/src/freeipa/reg.js @@ -25,19 +25,22 @@ * Construct registry do. It's expected that there will be different types of * registries for various object types. * - * Existing registries can be access directly by properties. + * Existing registries can be accessed directly by properties. * * Use set method for setting new registry. - * Use get/registry method for getting/registering of object in a registry. + * Use get/registry method for getting/registering object in a registry. + * + * Registries should be named by their object type in singular form: * - * Registries should be named by their object type in singular form. - * I.e.: * * entity * * widget * * action * * formatter * * facet * * dialog + * + * @class reg + * @singleton */ define(['dojo/_base/declare', 'dojo/_base/array', @@ -49,17 +52,36 @@ define(['dojo/_base/declare', reg.builder.ctor = Singleton_registry; var exp = reg._map; + + /** + * Get registry + * @param {string} object_type + * @param {string} type + * @return {_base.Construct_registry/_base.Singleton_registry} + */ exp.get = function(object_type, type) { var registry = reg.get(object_type); return registry.get(type); }; + /** + * Create and register new registry + * @param {string} object_type + * @param {string} type + * @param {Function} func + * @param {Object} default_spec + */ exp.register = function(object_type, type, func, default_spec) { var registry = reg.get(object_type); registry.register(type, func, default_spec); }; + /** + * Set new registry + * @param {string} object_type + * @param {_base.Construct_registry|_base.Singleton_registry} registry + */ exp.set = function(object_type, registry) { reg.set(object_type, registry); }; diff --git a/install/ui/src/freeipa/selinux.js b/install/ui/src/freeipa/selinux.js index 8a308b434..e71487667 100644 --- a/install/ui/src/freeipa/selinux.js +++ b/install/ui/src/freeipa/selinux.js @@ -108,6 +108,7 @@ var spec = { /** + * @ignore * @param {Object} facet spec */ var add_selinux_details_facet_widgets = function (spec) { diff --git a/install/ui/src/freeipa/spec_util.js b/install/ui/src/freeipa/spec_util.js index 7d60dc101..1053ebd1d 100644 --- a/install/ui/src/freeipa/spec_util.js +++ b/install/ui/src/freeipa/spec_util.js @@ -25,13 +25,18 @@ define(['dojo/_base/declare', var undefined; + /** + * Utility for working with specification objects + * @class spec_util + * @singleton + */ var exp = { /** * Set default value of spec's attribute when not already * * @param {Object} spec - * @param {String} attribute name + * @param {string} attribute name * @param {*} default value * @param {Object} object */ diff --git a/install/ui/src/freeipa/sudo.js b/install/ui/src/freeipa/sudo.js index cb620e41b..b01621ddd 100644 --- a/install/ui/src/freeipa/sudo.js +++ b/install/ui/src/freeipa/sudo.js @@ -226,6 +226,7 @@ return { };}; /** + * @ignore * @param {Object} facet spec */ var add_sudorule_details_facet_widgets = function (spec) { diff --git a/install/ui/src/freeipa/text.js b/install/ui/src/freeipa/text.js index a439237ae..776b9ee0d 100644 --- a/install/ui/src/freeipa/text.js +++ b/install/ui/src/freeipa/text.js @@ -18,15 +18,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** - * Text provider - * - * Serves for returning labels, titles, messages from various providers. - * Other providers can extends functionality. - */ define(['./_base/Provider', './_base/i18n', './metadata'], function(Provider, i18n, metadata) { + /** + * Text provider + * + * Serves for returning labels, titles, messages from: + * + * - {@link _base.i18n} provider + * - and {@link metadata} provider + * + * Other providers can extends its functionality. + * + * @class text + * @singleton + * @extends _base.Provider + */ var text = new Provider({ providers: [ i18n, diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js index 1fdcf25a5..34c3a7adf 100644 --- a/install/ui/src/freeipa/user.js +++ b/install/ui/src/freeipa/user.js @@ -34,6 +34,12 @@ define([ './certificate'], function(IPA, $, phases, reg, text) { +/** + * User module + * @class user + * @alternateClassName IPA.user + * @singleton + */ var exp = IPA.user = {}; var make_spec = function() { @@ -409,6 +415,7 @@ IPA.user.details_facet = function(spec) { }; /** + * @member user * Makes user association facets read-only in self service */ IPA.user.association_facet_ss_pre_op = function(spec, context) { diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js index d42b2008d..3891bf9b1 100644 --- a/install/ui/src/freeipa/widget.js +++ b/install/ui/src/freeipa/widget.js @@ -33,38 +33,122 @@ define(['dojo/_base/array', ], function(array, lang, builder, IPA, $, phases, reg, text) { +/** + * Widget module + * ============= + * + * External usage: + * + * var widget = require('freeipa/widget') + * @class widget + * @singleton + */ var exp = {}; +/** + * Width of column which contains only checkbox + * @member IPA + * @property {number} + */ IPA.checkbox_column_width = 22; + +/** + * String to show next to required fields to indicate that the field is required. + * @member IPA + * @property {string} + */ IPA.required_indicator = '*'; +/** + * Base widget + * @class + * @param {Object} spec + * @abstract + */ IPA.widget = function(spec) { spec = spec || {}; var that = IPA.object(); + /** + * Widget name. Should be container unique. + */ that.name = spec.name; + + /** + * Widget element ID. + * @deprecated + */ that.id = spec.id; + + /** + * Label + * @property {string} + */ that.label = text.get(spec.label); + + /** + * Helper text + * @property {string} + */ that.tooltip = text.get(spec.tooltip); + + /** + * Measurement unit + * @property {string} + */ that.measurement_unit = spec.measurement_unit; + + /** + * Parent entity + * @deprecated + * @property {IPA.entity} + */ that.entity = IPA.get_entity(spec.entity); //some old widgets still need it + + /** + * Parent facet + * @property {IPA.facet} + */ that.facet = spec.facet; + + /** + * Widget is enabled - can be focus and edited (depends also on writable + * and read_only) + * @property {boolean} + */ that.enabled = spec.enabled === undefined ? true : spec.enabled; + /** + * Create HTML representation of a widget. + * @method + * @param {HTMLElement} container - Container node + */ that.create = function(container) { container.addClass('widget'); that.container = container; }; + /** + * Reset widget content. All user-modifiable information have to be + * changed back to widgets defaults. + */ that.clear = function() { }; + /** + * Set enabled state. + * @param {boolean} value - True - enabled; False - disabled + */ that.set_enabled = function(value) { that.enabled = value; }; + /** + * Whether widget should be displayed. + * @param {boolean} value - True - visible; False - hidden + */ that.set_visible = function(visible) { if (visible) { @@ -74,6 +158,12 @@ IPA.widget = function(spec) { } }; + /** + * Utility method. Build widget based on spec with this widget's context. + * @param {boolean} spec - Widget specification object + * @param {Object} context - Context object. Gets mixed with this widget context. + * @param {Object} overrides - Build overrides + */ that.build_child = function(spec, context, overrides) { var def_c = { @@ -91,27 +181,90 @@ IPA.widget = function(spec) { return that; }; +/** + * Base class for input gathering widgets. + * @class + * @extends IPA.widget + * @abstract + */ IPA.input_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); + /** + * Widget's width. + * @deprecated + * @property {number} + */ that.width = spec.width; + + /** + * Widget's height. + * @deprecated + * @property {number} + */ that.height = spec.height; + /** + * Enable undo button showing. Undo button is displayed when user + * modifies data. + * @property {boolean} undo=true + */ that.undo = spec.undo === undefined ? true : spec.undo; + + /** + * User has rights to modify widgets content. Ie. based on LDAP ACL. + * @property {boolean} writable=true + */ that.writable = spec.writable === undefined ? true : spec.writable; + + /** + * This widget content is read-only. + * @property {boolean} + */ that.read_only = spec.read_only; + + /** + * Mark the widget to be hidden (form or dialog may not display it). + * @property {boolean} + */ that.hidden = spec.hidden; //events //each widget can contain several events + /** + * Value changed event. + * + * Raised when user modifies data by hand. + * + * @event + */ that.value_changed = IPA.observer(); + + /** + * Undo clicked event. + * + * @event + */ that.undo_clicked = IPA.observer(); + + /** + * Updated event. + * + * Raised when widget content gets updated - raised by + * {@link IPA.input_widget#update} method. + * + * @event + */ that.updated = IPA.observer(); + /** + * Creates HTML representation of error link + * @param {HTMLElement} container - node to place the error link + */ that.create_error_link = function(container) { container.append(' '); @@ -122,6 +275,10 @@ IPA.input_widget = function(spec) { }).appendTo(container); }; + /** + * Creates HTML representation of required indicator. + * @param {HTMLElement} container - node to place the indicator + */ that.create_required = function(container) { that.required_indicator = $('<span/>', { 'class': 'required-indicator', @@ -130,6 +287,11 @@ IPA.input_widget = function(spec) { }).appendTo(container); }; + /** + * Update displayed information by supplied values. + * @param {Object|Array|null} values - values to be edited/displayed by + * widget. + */ that.update = function() { }; @@ -137,6 +299,7 @@ IPA.input_widget = 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. + * @returns {Array|null} entered values */ that.save = function() { return []; @@ -147,6 +310,8 @@ IPA.input_widget = function(spec) { * On_undo is a link click callback. It can be specified to custom * callback. If a callback isn't set, default callback is used. If * spefified to value other than a function, no callback is registered. + * @param {HTMLElement} container + * @param {Function} link clicked callback */ that.create_undo = function(container, on_undo) { container.append(' '); @@ -170,33 +335,60 @@ IPA.input_widget = function(spec) { } }; + /** + * Get reference to undo element + * @return {jQuery} undo button jQuery reference + */ that.get_undo = function() { return $(that.undo_span); }; + /** + * Display undo button + */ that.show_undo = function() { that.get_undo().css('display', 'inline'); }; + /** + * Hide undo button + */ that.hide_undo = function() { $(that.undo_span).css('display', 'none'); }; + /** + * Get error link reference + * @return {jQuery} error link jQuery reference + */ that.get_error_link = function() { return $('span[name="error_link"]', that.container); }; + /** + * Show error message + * @protected + * @param {string} message + */ that.show_error = function(message) { var error_link = that.get_error_link(); error_link.html(message); error_link.css('display', 'block'); }; + /** + * Hide error message + * @protected + */ that.hide_error = function() { var error_link = that.get_error_link(); error_link.css('display', 'none'); }; + /** + * Set required + * @param {boolean} required + */ that.set_required = function(required) { that.required = required; @@ -206,6 +398,10 @@ IPA.input_widget = function(spec) { } }; + /** + * Set enabled + * @param {boolean} value - enabled + */ that.set_enabled = function(value) { that.widget_set_enabled(value); @@ -214,16 +410,35 @@ IPA.input_widget = function(spec) { } }; + /** + * Raise value change event + * @protected + */ that.on_value_changed = function() { var value = that.save(); that.value_changed.notify([value], that); }; + /** + * Widget is writable + * @return {boolean} + */ that.is_writable = function() { return !that.read_only && !!that.writable; }; + /** + * Focus input element + * @abstract + */ that.focus_input = function() {}; + + /** + * Mark element as deleted. + * + * Ie. textbox with strike-through + * @abstract + */ that.set_deleted = function() {}; // methods that should be invoked by subclasses @@ -233,7 +448,14 @@ IPA.input_widget = function(spec) { return that; }; -/*uses a browser specific technique to select a range.*/ +/** + * Select text in input. + * Uses a browser specific technique to select a range. + * @member IPA + * @param {jQuery} input jQuery reference + * @param {number} start + * @param {number} end + */ IPA.select_range = function(input,start, end) { input.focus(); if (input[0].setSelectionRange) { @@ -247,20 +469,40 @@ IPA.select_range = function(input,start, end) { } }; - +/** + * A textbox widget. Displayed as label when not modifiable. + * @class + * @extends IPA.input_widget + */ IPA.text_widget = function(spec) { spec = spec || {}; var that = IPA.input_widget(spec); + /** + * Size of the input. + * @property {number} + */ that.size = spec.size || 30; + + /** + * Input type + * @property {string} input_type='text' + */ that.input_type = spec.input_type || 'text'; + + /** + * Select range of text + */ that.select_range = function(start, end){ IPA.select_range(that.input, start, end); }; + /** + * @inheritDoc + */ that.create = function(container) { that.widget_create(container); @@ -294,6 +536,9 @@ IPA.text_widget = function(spec) { that.set_enabled(that.enabled); }; + /** + * @inheritDoc + */ that.update = function(values) { var value = values && values.length ? values[0] : ''; @@ -310,6 +555,9 @@ IPA.text_widget = function(spec) { that.updated.notify([], that); }; + /** + * @inheritDoc + */ that.save = function() { if (!that.is_writable()) { return null; @@ -319,15 +567,24 @@ IPA.text_widget = function(spec) { } }; + /** + * @inheritDoc + */ that.clear = function() { that.input.val(''); that.display_control.text(''); }; + /** + * @inheritDoc + */ that.focus_input = function() { that.input.focus(); }; + /** + * @inheritDoc + */ that.set_deleted = function(deleted) { if(deleted) { that.input.addClass('strikethrough'); @@ -342,6 +599,11 @@ IPA.text_widget = function(spec) { return that; }; +/** + * @class + * @extends IPA.text_widget + * A textbox widget where input type is 'password'. + */ IPA.password_widget = function(spec) { spec = spec || {}; @@ -351,6 +613,12 @@ IPA.password_widget = function(spec) { return that; }; +/** + * Widget which allows to edit multiple values. It display one + * editor (text widget by default) for each value. + * @class + * @extends IPA.input_widget + */ IPA.multivalued_widget = function(spec) { spec = spec || {}; @@ -644,26 +912,35 @@ IPA.multivalued_widget = function(spec) { }; /** - * Widget base for checkboxes and radios. Doesn't handle dirty state's but - * its supposed to be nestable. + * Option widget base + * + * @class IPA.option_widget_base + * @mixin + * + * Widget base for checkboxes and radios. Doesn't handle dirty states but + * it's nestable. * * Nesting rules: - * 1. parent should be checked when one of its child is checked + * + * 1. parent should be checked when one of its child is checked + * * Consequences: - * * childs get unchecked when parent gets unchecked - * * parent will be checked when child is checked even when input + * - childs get unchecked when parent gets unchecked + * - parent will be checked when child is checked even when input * values don't contain parent's value. - * 2. parent can be configured not to include it's value when children are + * 2. parent can be configured not to include it's value when children are * checked - * 3. each subtree containing a checked input has to return at least one value + * 3. each subtree containing a checked input has to return at least one value * on save() - * 4. each option has to have unique value + * 4. each option has to have unique value * * Has subset of widget interface - overrides the values in widget - * * save(): get values - * * update(values): set values - * * value_changed: event when change happens - * * create: creates HTML + * + * - save(): get values + * - update(values): set values + * - value_changed: event when change happens + * - create: creates HTML + * */ IPA.option_widget_base = function(spec, that) { @@ -717,6 +994,7 @@ IPA.option_widget_base = function(spec, that) { /** * Normalizes options spec + * @protected */ that.prepare_options = function(options) { @@ -732,14 +1010,17 @@ IPA.option_widget_base = function(spec, that) { }; /** - * Option has following interface: { - * label: String - * value: String - * nested: Boolean - * widget: Widget - * combine_values: Boolean, default true. Whether to include this value - * if some of its children is specified - * } + * Prepare option + * + * Transform option spec to option. + * @protected + * @param {Object} spec + * @param {string} spec.label + * @param {string} spec.value + * @param {boolean} spec.nested + * @param {IPA.widget} spec.widget + * @param {boolean} spec.combine_values - default true. Whether to + * include this value if some of its children is specified */ that.prepare_option = function(spec) { @@ -1071,15 +1352,27 @@ IPA.option_widget_base = function(spec, that) { return that; }; + +/** + * Radio widget + * + * - default layout: inline + * + * @class IPA.radio_widget + * @extends IPA.input_widget + * @mixins IPA.option_widget_base + */ IPA.radio_widget = function(spec) { spec = spec || {}; + spec.input_type = spec.input_type || 'radio'; spec.layout = spec.layout || 'inline'; var that = IPA.input_widget(spec); IPA.option_widget_base(spec, that); + /** @inheritDoc */ that.create = function(container) { that.widget_create(container); that.owb_create(container); @@ -1094,6 +1387,13 @@ IPA.radio_widget = function(spec) { return that; }; +/** + * Single checkbox widget + * + * @class + * @extends IPA.input_widget + * @mixins IPA.option_widget_base + */ IPA.checkbox_widget = function (spec) { var checked = 'checked'; @@ -1128,6 +1428,15 @@ IPA.checkbox_widget = function (spec) { return that; }; +/** + * Multiple checkbox widget + * + * - default layout: vertical + * + * @class + * @extends IPA.input_widget + * @mixins IPA.option_widget_base + */ IPA.checkboxes_widget = function (spec) { spec = spec || {}; spec.input_type = spec.input_type || 'checkbox'; @@ -1136,6 +1445,12 @@ IPA.checkboxes_widget = function (spec) { return that; }; +/** + * Select widget + * + * @class + * @extends IPA.input_widget + */ IPA.select_widget = function(spec) { spec = spec || {}; @@ -1248,6 +1563,12 @@ IPA.select_widget = function(spec) { return that; }; +/** + * Textarea widget + * + * @class + * @extends IPA.input_widget + */ IPA.textarea_widget = function (spec) { spec = spec || {}; @@ -1313,20 +1634,40 @@ IPA.textarea_widget = function (spec) { return that; }; +/** + * Base class for formatters + * + * Formatter can be used in various widgets such as column to perform value + * parsing, normalization and output formatting. + * + * @class + */ IPA.formatter = function(spec) { spec = spec || {}; var that = IPA.object(); - that.type = spec.type; // default is text + /** + * Type of output format + * + * - default: plain text + * @property {string} + */ + that.type = spec.type; - // parse attribute value into a normalized value + /** + * Parse attribute value into a normalized value + * @return parsed value + */ that.parse = function(value) { return value; }; - // format normalized value + /** + * Format normalized value + * @return formatted value + */ that.format = function(value) { return value; }; @@ -1334,18 +1675,30 @@ IPA.formatter = function(spec) { return that; }; +/** + * Formatter for boolean values + * @class + * @extends IPA.formatter + */ IPA.boolean_formatter = function(spec) { spec = spec || {}; var that = IPA.formatter(spec); + /** Formatted value for true */ that.true_value = text.get(spec.true_value || '@i18n:true'); + /** Formatted value for false */ that.false_value = text.get(spec.false_value || '@i18n:false'); + /** Show formatted value if parsed value is false */ that.show_false = spec.show_false; + /** Parse return inverted value */ that.invert_value = spec.invert_value; - // convert string boolean value into real boolean value, or keep the original value + /** + * Convert string boolean value into real boolean value, or keep + * the original value + */ that.parse = function(value) { if (value === undefined || value === null) return ''; @@ -1371,7 +1724,9 @@ IPA.boolean_formatter = function(spec) { return value; }; - // convert boolean value into formatted string, or keep the original value + /** + * Convert boolean value into formatted string, or keep the original value + */ that.format = function(value) { if (typeof value === 'boolean') { @@ -1396,6 +1751,11 @@ IPA.boolean_formatter = function(spec) { return that; }; +/** + * Format as HTML disabled/enabled status icon + * @class + * @extends IPA.boolean_formatter + */ IPA.boolean_status_formatter = function(spec) { spec = spec || {}; @@ -1418,7 +1778,11 @@ IPA.boolean_status_formatter = function(spec) { return that; }; -/* Take an LDAP format date in UTC and format it */ +/** + * Take an LDAP format date in UTC and format it + * @class + * @extends IPA.formatter + */ IPA.utc_date_formatter = function(spec) { spec = spec || {}; @@ -1436,9 +1800,23 @@ IPA.utc_date_formatter = function(spec) { return that; }; -/* - The entity name must be set in the spec either directly or via entity.name -*/ +/** + * Column for {@link IPA.table_widget} + * + * Handles value rendering. + * + * @class + * @param {Object} spec + * @param {string|IPA.entity} spec.entity Entity or its name + * @param {string} spec.name + * @param {string} [spec.label] + * @param {number} [spec.width] + * @param {string} [spec.primary_key] + * @param {boolean} spec.link + * render as link + * @param {IPA.formatter|Object} spec.formatter + * formatter or its spec + */ IPA.column = function (spec) { spec = spec || {}; @@ -1461,6 +1839,24 @@ IPA.column = function (spec) { }; } + /** + * Extract value from record and set formatted value to a container + * + * - parses and formats value if formatter is set + * - also works with promises. Value can be a promise or promise can be + * encapsulated in a object along with temporal value. + * + * { + * promise: deffered.promise, + * temp: 'temporal value to be shown until promise is resolve' + * } + * + * + * + * @param {jQuery} container + * @param {Object} record - value is located using 'name' property + * @param {boolean} suppress_link + */ that.setup = function(container, record, suppress_link) { var value = record[that.name]; var type; @@ -1493,6 +1889,10 @@ IPA.column = function (spec) { that.set_value(container, value, type, suppress_link); }; + /** + * Set value to the container + * @protected + */ that.set_value = function(container, value, type, suppress_link) { value = value ? value.toString() : ''; @@ -1518,6 +1918,11 @@ IPA.column = function (spec) { } }; + /** + * Handle clicks on link. + * + * Intended to be overridden. + */ that.link_handler = function(value) { return false; }; @@ -1535,6 +1940,25 @@ IPA.column = function (spec) { return that; }; +/** + * Table + * + * TODO: document properties and methods + * + * @class + * @extends IPA.input_widget + * + * @param {Object} spec + * @param {boolean} [spec.scrollable] + * @param {boolean} [spec.selectable=true] + * @param {boolean} [spec.save_values=true] + * @param {string} [spec.class] css class + * @param {boolean} [spec.pagination] render pagination + * @param {number} [spec.page_length=20] + * @param {boolean} [spec.multivalued=true] + * @param {Array} columns columns or columns specs + * @param {string} [value_attr_name=name] + */ IPA.table_widget = function (spec) { spec = spec || {}; @@ -1937,6 +2361,13 @@ IPA.table_widget = function (spec) { return $('input[name="'+that.name+'"]:checked', that.tbody).closest('tr'); }; + /** + * Create record from supplied result. + * + * @param {Object} result + * @param {number} index Used when record information for each individual + * column is located in an array at given index + */ that.get_record = function(result, index) { var record = {}; @@ -2066,7 +2497,29 @@ IPA.table_widget = function (spec) { return that; }; - +/** + * Attribute table + * + * A table which acks as `IPA.association_table` but serves only for one + * multivalued attribute. + * + * TODO: document properties and methods + * + * @class + * @extends IPA.table_widget + * + * @param {Object} spec + * @param {string} [spec.attribute_nam] name of attribute if different + * than widget name + * @param {boolean} [spec.adder_dialog] adder dialog spec + * @param {boolean} [spec.css_class] + * @param {string} [spec.add_command] add command/method name + * @param {string} [spec.remove_command] remove command/method name + * @param {Function} [spec.on_add] + * @param {Function} [spec.on_add_error] + * @param {Function} [spec.on_remove] + * @param {Function} [spec.on_remove_error] + */ IPA.attribute_table_widget = function(spec) { @@ -2408,6 +2861,27 @@ IPA.attribute_table_widget = function(spec) { return that; }; +/** + * Combobox widget + * + * Widget which allows to select a value from a predefined set or write custom + * value. + * + * TODO: document properties and methods + * + * @class + * @extends IPA.input_widget + * + * @param {Object} spec + * @param {string} [spec.attribute_nam] name of attribute if different + * than widget name + * @param {boolean} [spec.editable] user can write his own values + * @param {boolean} [spec.searchable] + * @param {number} [spec.size] number of rows in dropdown select + * @param {boolean} [spec.empty_option] has empty option + * @param {Array} [spec.options] - options to pick from + * @param {number} [spec.z_index] + */ IPA.combobox_widget = function(spec) { spec = spec || {}; @@ -2830,6 +3304,23 @@ IPA.combobox_widget = function(spec) { return that; }; +/** + * Entity select widget + * + * Specialized combobox which allows to select an entity. Widget performs + * search - an RPC call - to get a list of entities. + * + * TODO: document properties and methods + * + * @class + * @extends IPA.combobox_widget + * + * @param {Object} spec + * @param {string} [spec.other_entity] + * @param {string} [spec.other_field] + * @param {Array} [spec.options] + * @param {Object} [spec.filter_options] RPC command options + */ IPA.entity_select_widget = function(spec) { spec = spec || {}; @@ -2887,13 +3378,27 @@ IPA.entity_select_widget = function(spec) { return that; }; - +/** + * Display value as a link or text + * + * @class + * @extends IPA.input_widget + * + * @param {Object} spec + * @param {boolean} [spec.is_link=false] + */ IPA.link_widget = function(spec) { var that = IPA.input_widget(spec); that.is_link = spec.is_link || false; + + /** + * Raised when link is clicked + * @event + */ that.link_clicked = IPA.observer(); + /** @inheritDoc */ that.create = function(container) { that.widget_create(container); that.link = @@ -2911,6 +3416,7 @@ IPA.link_widget = function(spec) { appendTo(container); }; + /** @inheritDoc */ that.update = function (values){ if (values || values.length > 0) { @@ -2932,6 +3438,7 @@ IPA.link_widget = function(spec) { that.updated.notify([], that); }; + /** @inheritDoc */ that.clear = function() { that.nonlink.text(''); that.link.text(''); @@ -2941,6 +3448,25 @@ IPA.link_widget = function(spec) { return that; }; +/** + * Create link which behaves as button + * + * @method + * @member IPA + * + * @param {Object} spec + * @param {string} [spec.name] + * @param {string} [spec.label] button text + * @param {string} [spec.title=label] button title + * @param {string} [spec.icon] icon name (class) + * @param {string} [spec.id] + * @param {string} [spec.href] + * @param {string} [spec.style] css style + * @param {string} [spec.class] + * @param {Function} [spec.click] click handler + * @param {boolean} [spec.focusable] button is focusable + * + */ IPA.action_button = function(spec) { spec = spec || {}; @@ -2978,6 +3504,25 @@ IPA.action_button = function(spec) { return button; }; +/** + * Create button + * + * Has different styling than action button. + * + * @method + * @member IPA + * + * @param {Object} spec + * @param {string} [spec.name] + * @param {string} [spec.label] button text + * @param {string} [spec.title=label] button title + * @param {string} [spec.icon] icon name (class) + * @param {string} [spec.id] + * @param {string} [spec.href] + * @param {string} [spec.style] css style + * @param {string} [spec.class] + * @param {Function} [spec.click] click handler + */ IPA.button = function(spec) { spec = spec || {}; @@ -3001,18 +3546,34 @@ IPA.button = function(spec) { return button; }; +/** + * Widget encapsulating an `IPA.button` + * + * @class + * @extends IPA.widget + */ IPA.button_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); + /** Displayed link */ that.href = spec.href; + /** CSS style */ that.style = spec.style; + /** Click handler */ that.click = spec.click; + /** CSS class */ that['class'] = spec['class']; + /** CSS class for disabled button */ that.disabled_class = 'button-disabled'; + /** + * Widget click handler. + * + * Calls provided click handler. + */ that.on_click = function() { if (that.click) { @@ -3021,6 +3582,7 @@ IPA.button_widget = function(spec) { return false; }; + /** @inheritDoc */ that.create = function(container) { that.button = IPA.button({ id: that.id, @@ -3036,6 +3598,7 @@ IPA.button_widget = function(spec) { that.set_enabled(that.enabled); }; + /** @inheritDoc */ that.set_enabled = function(enabled) { that.widget_set_enabled(enabled); @@ -3051,13 +3614,21 @@ IPA.button_widget = function(spec) { return that; }; +/** + * Widget just for rendering provided HTML code + * + * @class + * @extends IPA.widget + */ IPA.html_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); + /** Code to render */ that.html = spec.html; + /** Container CSS class */ that.css_class = spec.css_class; that.create = function(container) { @@ -3076,6 +3647,12 @@ IPA.html_widget = function(spec) { return that; }; +/** + * Widget just for rendering provided HTML code + * + * @class + * @extends IPA.widget + */ IPA.composite_widget = function(spec) { spec = spec || {}; @@ -3105,6 +3682,11 @@ IPA.composite_widget = function(spec) { return that; }; +/** + * Section which can be collapsed + * @class + * @extends IPA.composite_widget + */ IPA.collapsible_section = function(spec) { spec = spec || {}; @@ -3155,13 +3737,27 @@ IPA.collapsible_section = function(spec) { return that; }; +/** + * Section which can be collapsed + * @class + * @extends IPA.collapsible_section + */ IPA.details_section = IPA.collapsible_section; +/** + * Base layout + * @class + */ IPA.layout = function(spec) { return {}; }; -// Creates list of widgets into table with two columns: label and widget +/** + * Table layout + * Creates list of widgets into table with two columns: label and widget + * @class + * @extends IPA.layout + */ IPA.table_layout = function(spec) { spec = spec || {}; @@ -3237,6 +3833,11 @@ IPA.table_layout = function(spec) { return that; }; +/** + * Collapsible section with table layout + * @class + * @extends IPA.details_section + */ IPA.details_table_section = function(spec) { spec = spec || {}; @@ -3280,6 +3881,11 @@ IPA.details_table_section = function(spec) { return that; }; +/** + * Policy which hides specific widget if it doesn't have any value + * @class + * @extends IPA.facet_policy + */ IPA.hide_empty_row_policy = function (spec) { spec = spec || {}; @@ -3301,7 +3907,12 @@ IPA.hide_empty_row_policy = function (spec) { return that; }; -//non-collabsible section +/** + * Not collabsible details section with table layout + * + * @class + * @extends IPA.details_table_section + */ IPA.details_table_section_nc = function(spec) { spec = spec || {}; @@ -3313,6 +3924,19 @@ IPA.details_table_section_nc = function(spec) { return that; }; +/** + * Section which can contain multiple subsections + * + * Only one subsection can be active. Widgets in non-active subsections are + * disabled. + * + * Selection of active section is based on radio button selection. + * + * Presence of `multiple_choice_section_policy` is required. + * + * @class + * @extends IPA.composite_widget + */ IPA.multiple_choice_section = function(spec) { spec = spec || {}; @@ -3457,6 +4081,9 @@ IPA.multiple_choice_section = function(spec) { return that; }; +/** + * Policy which makes `multiple_choice_section` work properly. + */ IPA.multiple_choice_section_policy = function(spec) { spec = spec || {}; @@ -3475,6 +4102,13 @@ IPA.multiple_choice_section_policy = function(spec) { return that; }; +/** + * Enable widget + * Basically a radio button + * + * @class + * @extends IPA.radio_widget + */ IPA.enable_widget = function(spec) { spec = spec || {}; @@ -3484,7 +4118,13 @@ IPA.enable_widget = function(spec) { return that; }; - +/** + * Header widget + * + * Can be used as subsection header. + * @class + * @extends IPA.widget + */ IPA.header_widget = function(spec) { spec = spec || {}; @@ -3505,16 +4145,32 @@ IPA.header_widget = function(spec) { return that; }; +/** + * Event + * + * @class IPA.observer + */ IPA.observer = function(spec) { var that = IPA.object(); + /** + * Listeners + */ that.listeners = []; + /** + * Register new listener + * @param {Function} callback + */ that.attach = function(callback) { that.listeners.push(callback); }; + /** + * Remove registered listener + * @param {Function} callback + */ that.detach = function(callback) { for(var i=0; i < that.listeners.length; i++) { if(callback === that.listeners[i]) { @@ -3524,6 +4180,13 @@ IPA.observer = function(spec) { } }; + /** + * Call all listeners in order of registration with given context and + * arguments. + * + * @param {Array} arguments + * @param {Object} context + */ that.notify = function(args, context) { args = args || []; context = context || this; @@ -3536,11 +4199,26 @@ IPA.observer = function(spec) { return that; }; +/** + * Utility class for HMTL generation + * @class + */ IPA.html_util = function() { var that = IPA.object(); + + /** + * Last used ID + * @property {number} + */ that.id_count = 0; + /** + * Creates unique ID + * Usable for controls where same id/name would cause unintended + * interactions. IE. radios with same name influence each other. + * @param {string} prefix is concatenated with current `id_count` + */ that.get_next_id = function(prefix) { that.id_count++; return prefix ? prefix + that.id_count : that.id_count; @@ -3549,6 +4227,14 @@ IPA.html_util = function() { return that; }(); +/** + * Widget container + * + * - provides means for nesting widgets + * - used ie in facets, dialogs or composite widgets + * + * @class + */ IPA.widget_container = function(spec) { spec = spec || {}; @@ -3626,6 +4312,10 @@ IPA.widget_container = function(spec) { return that; }; +/** + * Widget builder + * @class + */ IPA.widget_builder = function(spec) { spec = spec || {}; @@ -3651,6 +4341,14 @@ IPA.widget_builder = function(spec) { return that; }; +/** + * SSH keys widget + * + * Multivalued widget with SSH key widget instead of text widget. + * + * @class + * @extends IPA.multivalued_widget + */ IPA.sshkeys_widget = function(spec) { spec = spec || {}; @@ -3677,6 +4375,12 @@ IPA.sshkeys_widget = function(spec) { return that; }; +/** + * SSH key widget + * + * @class + * @extends IPA.input_widget + */ IPA.sshkey_widget = function(spec) { spec = spec || {}; @@ -3859,6 +4563,14 @@ IPA.sshkey_widget = function(spec) { return that; }; +/** + * Action panel + * + * - usable in sections + * + * @class + * @extends IPA.widget + */ IPA.action_panel = function(spec) { spec = spec || {}; @@ -3983,6 +4695,16 @@ IPA.action_panel = function(spec) { return that; }; +/** + * Value map widget + * + * Read-only widget which shows different string based on current value. + * + * Basically there is a map between values(keys) and strings (displayed values). + * + * @class + * @extends IPA.input_widget + */ IPA.value_map_widget = function(spec) { spec = spec || {}; @@ -4034,6 +4756,11 @@ IPA.value_map_widget = function(spec) { return that; }; +/** + * pre_op operations for widgets + * - sets facet and entity if present in context + * @member widget + */ exp.pre_op = function(spec, context) { if (context.facet) spec.facet = context.facet; @@ -4043,6 +4770,7 @@ exp.pre_op = function(spec, context) { /** * Enables widget nesting + * @member widget */ exp.post_op = function(obj, spec, context) { @@ -4056,7 +4784,11 @@ exp.post_op = function(obj, spec, context) { return obj; }; -// Widget builder and registry +/** + * Widget builder + * - instantiated in global builder registry for type: 'widget' + * @member widget + */ exp.builder = builder.get('widget'); exp.builder.factory = IPA.text_widget; exp.builder.string_mode = 'property'; @@ -4066,12 +4798,19 @@ exp.builder.post_ops.push(exp.post_op); reg.set('widget', exp.builder.registry); -// Formatter builder and registry +/** + * Formatter builder + * - added as builder for 'formatter' registry + * @member widget + */ exp.formatter_builder = builder.get('formatter'); exp.formatter_builder.factory = IPA.formatter; reg.set('formatter', exp.formatter_builder.registry); - +/** + * Register widgets and formatters to registries + * @member widget + */ exp.register = function() { var w = reg.widget; var f = reg.formatter; diff --git a/install/ui/src/freeipa/widgets/App.js b/install/ui/src/freeipa/widgets/App.js index abec9754e..569cecef9 100644 --- a/install/ui/src/freeipa/widgets/App.js +++ b/install/ui/src/freeipa/widgets/App.js @@ -42,8 +42,7 @@ define(['dojo/_base/declare', * This class serves as top level widget. It creates basic UI: controls * rendering of header, footer and placeholder for facets. * - * @name freeipa.widgets.app - * @class + * @class widgets.App */ var app = declare([Stateful, Evented], { diff --git a/install/ui/src/freeipa/widgets/Menu.js b/install/ui/src/freeipa/widgets/Menu.js index 61178b046..1e4c08211 100644 --- a/install/ui/src/freeipa/widgets/Menu.js +++ b/install/ui/src/freeipa/widgets/Menu.js @@ -35,38 +35,39 @@ define(['dojo/_base/declare', return declare([Evented], { /** - * @name freeipa.widget.menu - * @class - * * Creates UI for freeipa.navigation.menu. Provides an event when * a menu items is selected. * - * event: item-select(menu_item) + * @class widgets.Menu + */ + + /** + * @event item-select(menu_item) */ /** * Object store of menu items * @protected - * @type freeipa.navigation.menu + * @property {navigation.Menu} */ menu: null, /** * domNode of this widget. FIXME: move to superclass (none yet) - * @type Node + * @property {HTMLElement} */ domNode: null, /** * Turns off update on data change - * @type Boolen + * @property {boolean} */ ignore_changes: false, /** * Css class for nodes containing a submenu of certain level_class - * @type String + * @property {string} */ level_class: 'menu-level', @@ -92,9 +93,9 @@ define(['dojo/_base/declare', * Top level items are rendered if menu_items is null * * @protected - * @param {menu_item|null} menu_item - * @param {Node} node - * @param {Number} level + * @param {navigation.MenuItem|null} menu_item + * @param {HTMLElement} node + * @param {number} level */ _render_children: function (menu_item, node, level) { @@ -150,8 +151,8 @@ define(['dojo/_base/declare', * menu_item's state. * * @protected - * @param {menu_item|string} menu_item - * @param {Node} [li_node] + * @param {navigation.MenuItem|string} menu_item + * @param {HTMLElement} [li_node] */ _update_item: function(menu_item, li_node) { @@ -182,7 +183,7 @@ define(['dojo/_base/declare', /** * Displays only supplied menu items. - * @param {menu_item[]} menu_items Items to show + * @param {navigation.MenuItem[]} menu_items Items to show */ select: function(menu_items) { @@ -215,9 +216,9 @@ define(['dojo/_base/declare', * Handles changes in this.menu object. * * @protected - * @param {menu_item} object - * @param {Number} removedFrom - * @param {Number} insertedInto + * @param {navigation.MenuItem} object + * @param {number} removedFrom + * @param {number} insertedInto */ _items_changed: function(object, removedFrom, insertedInto) { @@ -234,7 +235,7 @@ define(['dojo/_base/declare', /** * Sets this.menu and starts to watch its changes - * @param {freeipa.navigation.menu} menu + * @param {navigation.Menu} menu */ set_menu: function(menu) { this.menu = menu; @@ -249,6 +250,8 @@ define(['dojo/_base/declare', /** * Internal handler for clicking on menu item. * Raises item-select event. + * @protected + * @param {navigation.MenuItem} menu_items */ _item_clicked: function(menu_item) { this.emit('item-select', menu_item); @@ -259,7 +262,7 @@ define(['dojo/_base/declare', * * Intended for overriding. * - * @param {menu_item} menu_item + * @param {navigation.MenuItem} menu_item * @param {Event} event */ item_clicked: function(menu_item/*, event*/) { |