mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
webui: field and widget binding refactoring
This is a Web UI wide change. Fields and Widgets binding was refactored to enable proper two-way binding between them. This should allow to have one source of truth (field) for multiple consumers - widgets or something else. One of the goal is to have fields and widget implementations independent on each other. So that one could use a widget without field or use one field for multiple widgets, etc.. Basically a fields logic was split into separate components: - adapters - parsers & formatters - binder Adapters - extract data from data source (FreeIPA RPC command result) - prepares them for commands. Parsers - parse extracted data to format expected by field - parse widget value to format expected by field Formatters - format field value to format suitable for widgets - format field value to format suitable for adapter Binder - is a communication bridge between field and widget - listens to field's and widget's events and call appropriate methods Some side benefits: - better validation reporting in multivalued widget Reviewed-By: Adam Misnyovszki <amisnyov@redhat.com>
This commit is contained in:
@@ -74,8 +74,8 @@
|
||||
"reg",
|
||||
"details.details_builder",
|
||||
"details.section_builder",
|
||||
"IPA.field_builder",
|
||||
"IPA.widget_builder"
|
||||
"field.field_builder",
|
||||
"widget.widget_builder"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -96,8 +96,10 @@
|
||||
"IPA.bulk_associator",
|
||||
"IPA.association_config",
|
||||
"spec_util",
|
||||
"_base.debug",
|
||||
"_base.Spec_mod",
|
||||
"datetime"
|
||||
"datetime",
|
||||
"util"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -115,7 +117,7 @@
|
||||
"facet.FacetState",
|
||||
"facet.action_holder",
|
||||
"details.facet_policies",
|
||||
"IPA.field_container",
|
||||
"field.field_container",
|
||||
"IPA.widget_container",
|
||||
"details.update_info",
|
||||
"details.command_info",
|
||||
@@ -179,10 +181,22 @@
|
||||
{
|
||||
"name": "Fields",
|
||||
"classes": [
|
||||
"IPA.field",
|
||||
"field.field",
|
||||
"*_field"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Binders",
|
||||
"classes": [
|
||||
"*Binder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Adapters",
|
||||
"classes": [
|
||||
"*Adapter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Formatters",
|
||||
"classes": [
|
||||
@@ -193,7 +207,7 @@
|
||||
{
|
||||
"name": "Validators",
|
||||
"classes": [
|
||||
"IPA.validator",
|
||||
"field.validator",
|
||||
"*_validator"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -184,3 +184,25 @@ input[type="radio"]:checked + label:before {
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
// do not show error hint for valid value in multivalued widget if some
|
||||
// other value is invalid
|
||||
.control-group.error .valid .help-inline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// only type text for now - it should be replaced when we adopt Bootstrap 3
|
||||
// based RCUE (patternfly)
|
||||
.control-group.error .valid input[type=text] {
|
||||
|
||||
border-color: #62afdb !important;
|
||||
|
||||
&:focus {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
|
||||
}
|
||||
}
|
||||
|
||||
336
install/ui/src/freeipa/FieldBinder.js
Normal file
336
install/ui/src/freeipa/FieldBinder.js
Normal file
@@ -0,0 +1,336 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
define(['dojo/_base/declare',
|
||||
'dojo/_base/lang',
|
||||
'dojo/on',
|
||||
'./util'
|
||||
],
|
||||
function(declare, lang, on, util) {
|
||||
|
||||
/**
|
||||
* Field binder
|
||||
*
|
||||
* Binds input widget with field - defines standard communication logic
|
||||
* between widget and a field.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* var binder = new FieldBinder(widget, field).bind();
|
||||
*
|
||||
* // or
|
||||
* var binder = new FieldBinder({
|
||||
* field: field,
|
||||
* widget: widget
|
||||
* });
|
||||
* binder.bind()
|
||||
*
|
||||
* @class FieldBinder
|
||||
*/
|
||||
var FieldBinder = declare([], {
|
||||
|
||||
/**
|
||||
* Field
|
||||
* @property {IPA.field}
|
||||
*/
|
||||
field: null,
|
||||
|
||||
/**
|
||||
* Widget
|
||||
* @property {IPA.input_widget}
|
||||
*/
|
||||
widget: null,
|
||||
|
||||
/**
|
||||
* Binder is enabled
|
||||
*
|
||||
* Handlers are not be called when set to false.
|
||||
*
|
||||
* @property {boolean}
|
||||
*/
|
||||
enabled: true,
|
||||
|
||||
/**
|
||||
* Handlers
|
||||
* @protected
|
||||
* @property {Function[]}
|
||||
*/
|
||||
handlers: null,
|
||||
|
||||
/**
|
||||
* Value update is in progress
|
||||
*
|
||||
* When set, binder should not react to field's nor widget's value-change
|
||||
* event.
|
||||
*
|
||||
* @property {boolean}
|
||||
*/
|
||||
updating: false,
|
||||
|
||||
/**
|
||||
* Bind widget with field
|
||||
*
|
||||
* Listens for field's:
|
||||
*
|
||||
* - enable-change
|
||||
* - valid-change
|
||||
* - value-change
|
||||
* - dirty-change
|
||||
* - require-change
|
||||
* - writable-change
|
||||
* - readonly-change
|
||||
* - reset
|
||||
*
|
||||
* Listens for widget's:
|
||||
*
|
||||
* - value-change
|
||||
* - undo-click
|
||||
*
|
||||
* @param {boolean} hard
|
||||
* Hard binding. Sets `field.widget` to `this.widget`.
|
||||
* This option is for backward compatibility.
|
||||
*/
|
||||
bind: function(hard) {
|
||||
|
||||
var field = this.field;
|
||||
var widget = this.widget;
|
||||
|
||||
if (hard) field.widget = widget;
|
||||
|
||||
this.handle(field, 'enable-change', this.on_field_enable_change);
|
||||
this.handle(field, 'valid-change', this.on_field_valid_change);
|
||||
this.handle(field, 'value-change', this.on_field_value_change);
|
||||
this.handle(field, 'dirty-change', this.on_field_dirty_change);
|
||||
this.handle(field, 'require-change', this.on_field_require_change);
|
||||
this.handle(field, 'writable-change', this.on_field_writable_change);
|
||||
this.handle(field, 'readonly-change', this.on_field_readonly_change);
|
||||
this.handle(field, 'reset', this.on_field_reset);
|
||||
|
||||
this.handle(widget, 'value-change', this.on_widget_value_change);
|
||||
this.handle(widget, 'undo-click', this.on_widget_undo_click);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbind all handlers
|
||||
*/
|
||||
unbind: function() {
|
||||
|
||||
var handler;
|
||||
while ((handler = this.handlers.pop())) {
|
||||
handler.remove();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and registers the handler.
|
||||
* Handler will be called in binder context and only if
|
||||
* `this.enabled === true`.
|
||||
*
|
||||
* Do not use `on(target, type, handler)` directly.
|
||||
*
|
||||
* @param {Function} handler
|
||||
* @return {Function} context bound handler
|
||||
* @protected
|
||||
*/
|
||||
handle: function(target, type, handler) {
|
||||
|
||||
var _this = this;
|
||||
|
||||
var hndlr = function() {
|
||||
if (_this.enabled !== true) return;
|
||||
else {
|
||||
handler.apply(_this, Array.prototype.slice.call(arguments, 0));
|
||||
}
|
||||
};
|
||||
|
||||
var reg_hndl = on(target, type, hndlr);
|
||||
this.handlers.push(reg_hndl);
|
||||
|
||||
return hndlr;
|
||||
},
|
||||
|
||||
/**
|
||||
* Field enable change handler
|
||||
*
|
||||
* Reflect enabled state to widget
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
on_field_enable_change: function(event) {
|
||||
this.widget.set_enabled(event.enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* Field valid change handler
|
||||
* @protected
|
||||
*/
|
||||
on_field_valid_change: function(event) {
|
||||
this.widget.set_valid(event.result);
|
||||
},
|
||||
|
||||
/**
|
||||
* Field dirty change handler
|
||||
*
|
||||
* Controls showing of widget's undo button
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
on_field_dirty_change: function(event) {
|
||||
|
||||
if (!this.field.undo) return;
|
||||
if (event.dirty) {
|
||||
this.widget.show_undo();
|
||||
} else {
|
||||
this.widget.hide_undo();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Field require change handler
|
||||
*
|
||||
* Updates widget's require state
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
on_field_require_change: function(event) {
|
||||
|
||||
this.widget.set_required(event.required);
|
||||
},
|
||||
|
||||
/**
|
||||
* Field require change handler
|
||||
*
|
||||
* Updates widget's require state
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
on_field_writable_change: function(event) {
|
||||
|
||||
this.widget.set_writable(event.writable);
|
||||
},
|
||||
|
||||
/**
|
||||
* Field require change handler
|
||||
*
|
||||
* Updates widget's require state
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
on_field_readonly_change: function(event) {
|
||||
|
||||
this.widget.set_read_only(event.read_only);
|
||||
},
|
||||
|
||||
/**
|
||||
* Field reset handler
|
||||
*
|
||||
* @param {Object} event
|
||||
* @protected
|
||||
*/
|
||||
on_field_reset: function(event) {
|
||||
this.copy_properties();
|
||||
},
|
||||
|
||||
/**
|
||||
* Field value change handler
|
||||
* @protected
|
||||
*/
|
||||
on_field_value_change: function(event) {
|
||||
|
||||
if (this.updating) return;
|
||||
|
||||
var format_result = util.format(this.field.ui_formatter, event.value);
|
||||
if (format_result.ok) {
|
||||
this.updating = true;
|
||||
this.widget.update(format_result.value);
|
||||
this.updating = false;
|
||||
} else {
|
||||
// this should not happen in ideal world
|
||||
window.console.warn('field format error: '+this.field.name);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget value change handler
|
||||
* @protected
|
||||
*/
|
||||
on_widget_value_change: function(event) {
|
||||
|
||||
if (this.updating) return;
|
||||
|
||||
var val = this.widget.save();
|
||||
var format_result = util.parse(this.field.ui_parser, val);
|
||||
if (format_result.ok) {
|
||||
this.updating = true;
|
||||
this.field.set_value(format_result.value);
|
||||
this.updating = false;
|
||||
} else {
|
||||
this.field.set_valid(format_result);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget undo click handler
|
||||
* @protected
|
||||
*/
|
||||
on_widget_undo_click: function(event) {
|
||||
|
||||
this.field.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Copies `label`, `tooltip`, `measurement_unit`, `undo`, `writable`,
|
||||
* `read_only` from field to widget
|
||||
*/
|
||||
copy_properties: function() {
|
||||
|
||||
var field = this.field;
|
||||
var widget = this.widget;
|
||||
|
||||
if (field.label) widget.label = field.label;
|
||||
if (field.tooltip) widget.tooltip = field.tooltip;
|
||||
if (field.measurement_unit) widget.measurement_unit = field.measurement_unit;
|
||||
widget.undo = field.undo;
|
||||
widget.set_writable(field.writable);
|
||||
widget.set_read_only(field.read_only);
|
||||
widget.set_required(field.is_required());
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
constructor: function(arg1, arg2) {
|
||||
|
||||
this.handlers = [];
|
||||
|
||||
if (arg2) {
|
||||
this.field = arg1;
|
||||
this.widget = arg2;
|
||||
} else {
|
||||
arg1 = arg1 || {};
|
||||
this.field = arg1.field;
|
||||
this.widget = arg1.widget;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return FieldBinder;
|
||||
});
|
||||
@@ -366,7 +366,7 @@ define(['dojo/_base/declare',
|
||||
var temp = lang.clone(preop);
|
||||
this.spec_mod.mod(spec, temp);
|
||||
this.spec_mod.del_rules(temp);
|
||||
lang.mixin(spec, preop);
|
||||
lang.mixin(spec, temp);
|
||||
}
|
||||
}
|
||||
return spec;
|
||||
|
||||
@@ -44,7 +44,11 @@
|
||||
* @class _base.Provider
|
||||
*
|
||||
*/
|
||||
define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) {
|
||||
define([
|
||||
'dojo/_base/declare',
|
||||
'dojo/_base/lang',
|
||||
'./debug'],
|
||||
function(declare, lang, debug) {
|
||||
|
||||
var undefined;
|
||||
var Provider = declare(null, {
|
||||
@@ -177,7 +181,7 @@ define(['dojo/_base/declare','dojo/_base/lang'], function(declare, lang) {
|
||||
}
|
||||
|
||||
var ret = value || alternate;
|
||||
if (!ret && key) {
|
||||
if (!ret && key && debug.provider_missing_value) {
|
||||
window.console.log('No value for:'+key);
|
||||
}
|
||||
|
||||
|
||||
41
install/ui/src/freeipa/_base/debug.js
Normal file
41
install/ui/src/freeipa/_base/debug.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/* Authors:
|
||||
* Petr Vobornik <pvoborni@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 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/>.
|
||||
*/
|
||||
define([], function() {
|
||||
|
||||
/**
|
||||
* Debug module
|
||||
*
|
||||
* One can set flags to enable console output of various messages.
|
||||
*
|
||||
* """
|
||||
* var debug = require('freeipa._base.debug');
|
||||
* debug.provider_missing_value = true;
|
||||
* """
|
||||
*
|
||||
* Currently used flags
|
||||
*
|
||||
* - provider_missing_value
|
||||
*
|
||||
* @class _base.debug
|
||||
*/
|
||||
return {
|
||||
provider_missing_value: false
|
||||
};
|
||||
});
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'dojo/on',
|
||||
'./metadata',
|
||||
'./ipa',
|
||||
'./jquery',
|
||||
@@ -30,7 +31,7 @@ define([
|
||||
'./search',
|
||||
'./association',
|
||||
'./entity'],
|
||||
function(metadata_provider, IPA, $, phases, reg, text) {
|
||||
function(on, metadata_provider, IPA, $, phases, reg, text) {
|
||||
|
||||
/**
|
||||
* Widgets, entities and fields related to Access Control that means
|
||||
@@ -577,6 +578,7 @@ aci.attributes_widget = function(spec) {
|
||||
$('.aci-attribute', that.table).
|
||||
prop('checked', $(this).prop('checked'));
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('value-change', { source: that });
|
||||
}
|
||||
}, th);
|
||||
|
||||
@@ -615,6 +617,7 @@ aci.attributes_widget = function(spec) {
|
||||
'class': 'aci-attribute',
|
||||
change: function() {
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('value-change', { source: that });
|
||||
}
|
||||
}, td);
|
||||
td = $('<td/>').appendTo(tr);
|
||||
@@ -827,15 +830,9 @@ aci.permission_target_policy = function (spec) {
|
||||
that.init = function() {
|
||||
|
||||
that.permission_target = that.container.widgets.get_widget(that.widget_name);
|
||||
var type_select = that.permission_target.widgets.get_widget('type');
|
||||
var type_f = that.container.fields.get_field('type');
|
||||
|
||||
type_select.value_changed.attach(function() {
|
||||
that.apply_type();
|
||||
});
|
||||
|
||||
type_select.undo_clicked.attach(function() {
|
||||
that.apply_type();
|
||||
});
|
||||
on(type_f, 'value-change', that.apply_type);
|
||||
};
|
||||
|
||||
that.apply_type = function () {
|
||||
@@ -861,9 +858,7 @@ aci.permission_target_policy = function (spec) {
|
||||
// permission plugin resets ipapermlocation to basedn when
|
||||
// type is unset. -> use it as pristine value so undo will
|
||||
// work correctly.
|
||||
var loc = [IPA.env.basedn];
|
||||
loc_w.update(loc);
|
||||
loc_f.values = loc;
|
||||
loc_f.set_value([IPA.env.basedn], true);
|
||||
} else {
|
||||
attrs = attr_multi.save();
|
||||
attr_table.update(attrs);
|
||||
@@ -1077,9 +1072,9 @@ aci.register = function() {
|
||||
e.register({ type: 'delegation', spec: aci.delegation_entity_spec });
|
||||
|
||||
w.register('attributes', aci.attributes_widget);
|
||||
f.register('attributes', IPA.checkboxes_field);
|
||||
f.register('attributes', IPA.field);
|
||||
w.register('rights', aci.rights_widget);
|
||||
f.register('rights', IPA.checkboxes_field);
|
||||
f.register('rights', IPA.field);
|
||||
w.register('permission_target', aci.permission_target_widget);
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'dojo/_base/declare',
|
||||
'./field',
|
||||
'./metadata',
|
||||
'./ipa',
|
||||
'./jquery',
|
||||
@@ -31,7 +33,8 @@ define([
|
||||
'./search',
|
||||
'./association',
|
||||
'./entity'],
|
||||
function(metadata_provider, IPA, $, navigation, phases, reg, rpc, text) {
|
||||
function(declare, field_mod, metadata_provider, IPA, $, navigation,
|
||||
phases, reg, rpc, text) {
|
||||
|
||||
var exp = IPA.automember = {};
|
||||
|
||||
@@ -445,29 +448,27 @@ IPA.automember.parse_condition_regex = function(regex) {
|
||||
IPA.automember.condition_field = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
spec.adapter = spec.adapter || IPA.automember.condition_adapter;
|
||||
var that = IPA.field(spec);
|
||||
|
||||
that.attr_name = spec.attribute || that.name;
|
||||
|
||||
that.load = function(record) {
|
||||
|
||||
var regexes = record[that.attr_name];
|
||||
that.values = [];
|
||||
|
||||
if (regexes) {
|
||||
for (var i=0, j=0; i<regexes.length; i++) {
|
||||
var condition = IPA.automember.parse_condition_regex(regexes[i]);
|
||||
that.values.push(condition);
|
||||
}
|
||||
}
|
||||
|
||||
that.load_writable(record);
|
||||
that.reset();
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.automember.condition_adapter = declare([field_mod.Adapter], {
|
||||
|
||||
load: function(record) {
|
||||
var regexes = this.inherited(arguments);
|
||||
var values = [];
|
||||
if (regexes) {
|
||||
for (var i=0, j=0; i<regexes.length; i++) {
|
||||
if (regexes[i] === '') continue;
|
||||
var condition = IPA.automember.parse_condition_regex(regexes[i]);
|
||||
values.push(condition);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
});
|
||||
|
||||
IPA.automember.condition_widget = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
@@ -523,6 +523,8 @@ IPA.cert.load_policy = function(spec) {
|
||||
method: 'show',
|
||||
args: [serial_number],
|
||||
on_success: function(data, text_status, xhr) {
|
||||
// copy it so consumers can notice the difference
|
||||
that.container.certificate = lang.clone(that.container.certificate);
|
||||
var cert = that.container.certificate;
|
||||
cert.revocation_reason = data.result.result.revocation_reason;
|
||||
that.notify_loaded();
|
||||
@@ -914,12 +916,11 @@ IPA.cert.status_field = function(spec) {
|
||||
|
||||
that.load = function(result) {
|
||||
that.register_listener();
|
||||
that.reset();
|
||||
that.field_load(result);
|
||||
};
|
||||
|
||||
that.set_certificate = function(certificate) {
|
||||
that.values = certificate;
|
||||
that.reset();
|
||||
that.set_value(certificate);
|
||||
};
|
||||
|
||||
that.register_listener = function() {
|
||||
|
||||
@@ -590,6 +590,15 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) {
|
||||
*/
|
||||
that.dirty_changed = IPA.observer();
|
||||
|
||||
/**
|
||||
* Get field
|
||||
* @param {string} name Field name
|
||||
* @returns {IPA.field}
|
||||
*/
|
||||
that.get_field = function(name) {
|
||||
return that.fields.get_field(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -685,7 +694,7 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) {
|
||||
that.is_dirty = function() {
|
||||
var fields = that.fields.get_fields();
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
if (fields[i].enabled && fields[i].is_dirty()) {
|
||||
if (fields[i].enabled && fields[i].dirty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -741,7 +750,7 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) {
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
var field = fields[i];
|
||||
|
||||
if (!field.enabled || only_dirty && !field.is_dirty()) continue;
|
||||
if (!field.enabled || only_dirty && !field.dirty) continue;
|
||||
|
||||
var values = field.save();
|
||||
if (require_value && !values) continue;
|
||||
|
||||
@@ -204,6 +204,15 @@ IPA.dialog = function(spec) {
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get field
|
||||
* @param {string} name Field name
|
||||
* @returns {IPA.field}
|
||||
*/
|
||||
that.get_field = function(name) {
|
||||
return that.fields.get_field(name);
|
||||
};
|
||||
|
||||
/** Validate dialog fields */
|
||||
that.validate = function() {
|
||||
var valid = true;
|
||||
|
||||
@@ -21,20 +21,24 @@
|
||||
|
||||
|
||||
define([
|
||||
'dojo/_base/declare',
|
||||
'./ipa',
|
||||
'./jquery',
|
||||
'./net',
|
||||
'./field',
|
||||
'./navigation',
|
||||
'./menu',
|
||||
'./phases',
|
||||
'./reg',
|
||||
'./rpc',
|
||||
'./util',
|
||||
'./text',
|
||||
'./details',
|
||||
'./search',
|
||||
'./association',
|
||||
'./entity'],
|
||||
function(IPA, $, NET, navigation, menu, phases, reg, rpc, text) {
|
||||
function(declare, IPA, $, NET, field_mod, navigation, menu, phases,
|
||||
reg, rpc, util, text) {
|
||||
|
||||
var exp = IPA.dns = {
|
||||
zone_permission_name: 'Manage DNS zone ${dnszone}'
|
||||
@@ -1434,8 +1438,8 @@ IPA.dns.record_prepare_details_for_type = function(type, fields, container) {
|
||||
*/
|
||||
|
||||
|
||||
IPA.dnsrecord_host_link_field = function(spec) {
|
||||
var that = IPA.link_field(spec);
|
||||
IPA.dnsrecord_host_link_widget = function(spec) {
|
||||
var that = IPA.link_widget(spec);
|
||||
that.other_pkeys = function() {
|
||||
var pkey = that.facet.get_pkeys();
|
||||
return [pkey[1]+'.'+pkey[0]];
|
||||
@@ -1587,12 +1591,20 @@ IPA.dnsrecord_adder_dialog_type_policy = function(spec) {
|
||||
IPA.dns.record_type_table_field = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
spec.adapter = spec.adapter || IPA.dns.record_type_adapter;
|
||||
|
||||
var that = IPA.field(spec);
|
||||
|
||||
that.dnstype = spec.dnstype;
|
||||
|
||||
that.load = function(record) {
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.dns.record_type_adapter = declare([field_mod.Adapter], {
|
||||
|
||||
separator: ';',
|
||||
|
||||
load: function(record) {
|
||||
|
||||
var data = {};
|
||||
|
||||
@@ -1602,22 +1614,16 @@ IPA.dns.record_type_table_field = function(spec) {
|
||||
for (var i=0, j=0; i<record.dnsrecords.length; i++) {
|
||||
|
||||
var dnsrecord = record.dnsrecords[i];
|
||||
if(dnsrecord.dnstype === that.dnstype) {
|
||||
if(dnsrecord.dnstype === this.context.dnstype) {
|
||||
|
||||
dnsrecord.position = j;
|
||||
j++;
|
||||
data.dnsrecords.push(dnsrecord);
|
||||
}
|
||||
}
|
||||
|
||||
that.values = data;
|
||||
|
||||
that.load_writable(record);
|
||||
that.reset();
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
IPA.dns.record_type_table_widget = function(spec) {
|
||||
|
||||
@@ -2040,70 +2046,35 @@ IPA.dns.record_type_table_widget = function(spec) {
|
||||
IPA.dns.netaddr_field = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.multivalued_field(spec);
|
||||
|
||||
that.load = function(record) {
|
||||
|
||||
that.record = record;
|
||||
|
||||
that.values = that.get_value(record, that.name);
|
||||
that.values = that.values[0].split(';');
|
||||
|
||||
that.load_writable(record);
|
||||
|
||||
that.reset();
|
||||
};
|
||||
|
||||
that.test_dirty = function() {
|
||||
|
||||
if (that.read_only) return false;
|
||||
|
||||
var values = that.field_save();
|
||||
|
||||
//check for empty value: null, [''], '', []
|
||||
var orig_empty = IPA.is_empty(that.values);
|
||||
var new_empty= IPA.is_empty(values);
|
||||
if (orig_empty && new_empty) return false;
|
||||
if (orig_empty != new_empty) return true;
|
||||
|
||||
//strict equality - checks object's ref equality, numbers, strings
|
||||
if (values === that.values) return false;
|
||||
|
||||
//compare values in array
|
||||
if (values.length !== that.values.length) return true;
|
||||
|
||||
for (var i=0; i<values.length; i++) {
|
||||
if (values[i] != that.values[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return that.widget.test_dirty();
|
||||
};
|
||||
|
||||
that.save = function(record) {
|
||||
|
||||
var values = that.field_save();
|
||||
var new_val = values.join(';');
|
||||
|
||||
if (record) {
|
||||
record[that.name] = new_val;
|
||||
}
|
||||
|
||||
return [new_val];
|
||||
};
|
||||
|
||||
that.validate = function() {
|
||||
|
||||
var values = that.field_save();
|
||||
|
||||
return that.validate_core(values);
|
||||
};
|
||||
|
||||
spec.adapter = IPA.dns.netaddr_adapter;
|
||||
var that = IPA.field(spec);
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.dns.netaddr_adapter = declare([field_mod.Adapter], {
|
||||
|
||||
separator: ';',
|
||||
|
||||
load: function(record) {
|
||||
var value = this.inherited(arguments)[0];
|
||||
if (value) {
|
||||
if (value[value.length-1] === this.separator) {
|
||||
value = value.substring(0, value.length-1);
|
||||
}
|
||||
value = value.split(this.separator);
|
||||
}
|
||||
value = util.normalize_value(value);
|
||||
return value;
|
||||
},
|
||||
|
||||
save: function(value, record) {
|
||||
if (value[0]) {
|
||||
value = [value.join(this.separator)];
|
||||
}
|
||||
return this.inherited(arguments, [value, record]);
|
||||
}
|
||||
});
|
||||
|
||||
IPA.dns.record_modify_column = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
@@ -2519,8 +2490,8 @@ exp.register = function() {
|
||||
w.register('dnszone_name', IPA.dnszone_name_widget);
|
||||
w.register('force_dnszone_add_checkbox', IPA.force_dnszone_add_checkbox_widget);
|
||||
f.register('force_dnszone_add_checkbox', IPA.checkbox_field);
|
||||
w.register('dnsrecord_host_link', IPA.link_widget);
|
||||
f.register('dnsrecord_host_link', IPA.dnsrecord_host_link_field);
|
||||
w.register('dnsrecord_host_link', IPA.dnsrecord_host_link_widget);
|
||||
f.register('dnsrecord_host_link', IPA.field);
|
||||
w.register('dnsrecord_type', IPA.dnsrecord_type_widget);
|
||||
f.register('dnsrecord_type', IPA.dnsrecord_type_field);
|
||||
w.register('dnsrecord_type_table', IPA.dns.record_type_table_widget);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -430,7 +430,7 @@ IPA.host_fqdn_field = function(spec) {
|
||||
};
|
||||
|
||||
that.child_value_changed = function() {
|
||||
that.validate();
|
||||
that.set_value(that.widget.save());
|
||||
};
|
||||
|
||||
return that;
|
||||
@@ -523,8 +523,9 @@ IPA.dnszone_select_widget = function(spec) {
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.host_dnsrecord_entity_link_field = function(spec){
|
||||
var that = IPA.link_field(spec);
|
||||
IPA.host_dnsrecord_entity_link_widget = function(spec) {
|
||||
|
||||
var that = IPA.link_widget(spec);
|
||||
|
||||
that.other_pkeys = function(){
|
||||
var pkey = that.facet.get_pkey();
|
||||
@@ -852,8 +853,8 @@ exp.register = function() {
|
||||
w.register('host_fqdn', IPA.host_fqdn_widget);
|
||||
f.register('dnszone_select', IPA.field);
|
||||
w.register('dnszone_select', IPA.dnszone_select_widget);
|
||||
f.register('host_dnsrecord_entity_link', IPA.host_dnsrecord_entity_link_field);
|
||||
w.register('host_dnsrecord_entity_link', IPA.link_widget);
|
||||
f.register('host_dnsrecord_entity_link', IPA.field);
|
||||
w.register('host_dnsrecord_entity_link', IPA.host_dnsrecord_entity_link_widget);
|
||||
f.register('force_host_add_checkbox', IPA.checkbox_field);
|
||||
w.register('force_host_add_checkbox', IPA.force_host_add_checkbox_widget);
|
||||
f.register('host_password', IPA.field);
|
||||
|
||||
@@ -218,7 +218,7 @@ IPA.rule_association_table_field = function(spec) {
|
||||
//performs delete operation.
|
||||
|
||||
if (!that.widget.enabled) {
|
||||
var values = that.save();
|
||||
var values = that.widget.save();
|
||||
|
||||
if (values.length > 0) { //no need to delete if has no values
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'dojo/_base/declare',
|
||||
'./field',
|
||||
'./ipa',
|
||||
'./jquery',
|
||||
'./phases',
|
||||
@@ -29,7 +31,7 @@ define([
|
||||
'./search',
|
||||
'./association',
|
||||
'./entity'],
|
||||
function(IPA, $, phases, reg, rpc, text) {
|
||||
function(declare, field_mod, IPA, $, phases, reg, rpc, text) {
|
||||
|
||||
var exp =IPA.service = {};
|
||||
|
||||
@@ -66,16 +68,16 @@ return {
|
||||
fields: [
|
||||
'krbprincipalname',
|
||||
{
|
||||
$type: 'service_name',
|
||||
name: 'service',
|
||||
label: '@i18n:objects.service.service',
|
||||
read_only: true
|
||||
read_only: true,
|
||||
adapter: IPA.service_name_adapter
|
||||
},
|
||||
{
|
||||
$type: 'service_host',
|
||||
name: 'host',
|
||||
label: '@i18n:objects.service.host',
|
||||
read_only: true
|
||||
read_only: true,
|
||||
adapter: IPA.service_host_adapter
|
||||
},
|
||||
{
|
||||
name: 'ipakrbauthzdata',
|
||||
@@ -271,45 +273,21 @@ IPA.service_adder_dialog = function(spec) {
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.service_name_field = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.field(spec);
|
||||
|
||||
that.load = function(record) {
|
||||
|
||||
that.field_load(record);
|
||||
|
||||
IPA.service_name_adapter = declare([field_mod.Adapter], {
|
||||
load: function(record) {
|
||||
var krbprincipalname = record.krbprincipalname[0];
|
||||
var value = krbprincipalname.replace(/\/.*$/, '');
|
||||
that.values = [value];
|
||||
|
||||
that.reset();
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
IPA.service_host_field = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.field(spec);
|
||||
|
||||
that.load = function(record) {
|
||||
|
||||
that.field_load(record);
|
||||
return [value];
|
||||
}
|
||||
});
|
||||
|
||||
IPA.service_host_adapter = declare([field_mod.Adapter], {
|
||||
load: function(record) {
|
||||
var krbprincipalname = record.krbprincipalname[0];
|
||||
var value = krbprincipalname.replace(/^.*\//, '').replace(/@.*$/, '');
|
||||
that.values = [value];
|
||||
|
||||
that.reset();
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
return [value];
|
||||
}
|
||||
});
|
||||
|
||||
IPA.service_provisioning_status_widget = function (spec) {
|
||||
|
||||
@@ -512,10 +490,6 @@ phases.on('registration', function() {
|
||||
|
||||
e.register({type: 'service', spec: exp.entity_spec});
|
||||
|
||||
f.register('service_name', IPA.service_name_field);
|
||||
w.register('service_name', IPA.text_widget);
|
||||
f.register('service_host', IPA.service_host_field);
|
||||
w.register('service_host', IPA.text_widget);
|
||||
f.register('service_provisioning_status', IPA.field);
|
||||
w.register('service_provisioning_status', IPA.service_provisioning_status_widget);
|
||||
a.register('service_unprovision', IPA.service.unprovision_action);
|
||||
|
||||
@@ -133,10 +133,10 @@ return {
|
||||
metadata: '@mo-param:user:userpassword'
|
||||
},
|
||||
{
|
||||
$type: 'datetime',
|
||||
name: 'krbpasswordexpiration',
|
||||
label: '@i18n:objects.user.krbpasswordexpiration',
|
||||
read_only: true,
|
||||
formatter: 'datetime'
|
||||
read_only: true
|
||||
},
|
||||
'uidnumber',
|
||||
'gidnumber',
|
||||
@@ -473,7 +473,7 @@ IPA.user_adder_dialog = function(spec) {
|
||||
var password2 = field2.save()[0];
|
||||
|
||||
if (password1 !== password2) {
|
||||
field2.show_error(text.get('@i18n:password.password_must_match'));
|
||||
field2.set_valid({ valid: false, message: text.get('@i18n:password.password_must_match') });
|
||||
valid = false;
|
||||
}
|
||||
|
||||
|
||||
338
install/ui/src/freeipa/util.js
Normal file
338
install/ui/src/freeipa/util.js
Normal file
@@ -0,0 +1,338 @@
|
||||
/* Authors:
|
||||
* Petr Vobornik <pvoborni@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 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/>.
|
||||
*/
|
||||
|
||||
define([
|
||||
'dojo/_base/lang',
|
||||
'./text'
|
||||
],
|
||||
function(lang, text) {
|
||||
|
||||
function equals_obj_def(a, b, options) {
|
||||
var same = true;
|
||||
var checked = {};
|
||||
|
||||
var check_same = function(a, b, skip) {
|
||||
|
||||
var same = true;
|
||||
skip = skip || {};
|
||||
|
||||
for (var key in a) {
|
||||
if (a.hasOwnProperty(key) && !(key in skip)) {
|
||||
|
||||
var va = a[key];
|
||||
var vb = b[key];
|
||||
|
||||
if (!equals(va, vb, options)) {
|
||||
same = false;
|
||||
skip[a] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return same;
|
||||
};
|
||||
|
||||
same = check_same(a,b, checked);
|
||||
same = same && check_same(b,a, checked);
|
||||
return same;
|
||||
}
|
||||
|
||||
function equals_obj(a, b, options) {
|
||||
|
||||
if (options.comparator) {
|
||||
return options.comparator(a, b, options);
|
||||
} else {
|
||||
return equals_obj_def(a, b, options);
|
||||
}
|
||||
}
|
||||
|
||||
function equals_array(a1, b1, options) {
|
||||
|
||||
var a = a1,
|
||||
b = b1;
|
||||
|
||||
if (!a || !b) return false;
|
||||
|
||||
if (a1.length !== b1.length) return false;
|
||||
|
||||
if (options.unordered) {
|
||||
a = a1.slice(0);
|
||||
b = b1.slice(0);
|
||||
a.sort();
|
||||
b.sort();
|
||||
}
|
||||
|
||||
for (var i=0; i<a.length; i++) {
|
||||
if (!equals(a[i], b[i], options)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function equals(a, b, options) {
|
||||
var a_t = typeof a;
|
||||
var b_t = typeof b;
|
||||
options = options || {};
|
||||
|
||||
if (a_t !== b_t) return false;
|
||||
if (a === b) return true;
|
||||
|
||||
if (['string', 'number', 'function', 'boolean',
|
||||
'undefined'].indexOf(a_t) > -1) {
|
||||
return false;
|
||||
} else if (a === null || b === null) {
|
||||
return false;
|
||||
} else if (lang.isArray(a)) {
|
||||
return equals_array(a, b, options);
|
||||
} else if (a instanceof Date) {
|
||||
return a.getTime() === b.getTime();
|
||||
} else { // objects
|
||||
return equals_obj(a, b, options);
|
||||
}
|
||||
}
|
||||
|
||||
function is_empty(value) {
|
||||
var empty = false;
|
||||
|
||||
if (!value) empty = true;
|
||||
|
||||
if (lang.isArray(value)) {
|
||||
empty = empty || value.length === 0 ||
|
||||
(value.length === 1) && (value[0] === '');
|
||||
} else if (typeof value === 'object') {
|
||||
var has_p = false;
|
||||
for (var p in value) {
|
||||
if (value.hasOwnProperty(p)) {
|
||||
has_p = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
empty = !has_p;
|
||||
} else if (value === '') empty = true;
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
function dirty(value, pristine, options) {
|
||||
|
||||
// check for empty value: null, [''], '', []
|
||||
var orig_empty = is_empty(pristine);
|
||||
var new_empty= is_empty(value);
|
||||
if (orig_empty && new_empty) return false;
|
||||
if (orig_empty != new_empty) return true;
|
||||
|
||||
// strict equality - checks object's ref equality, numbers, strings
|
||||
if (value === pristine) return false;
|
||||
|
||||
return !equals(value, pristine, options);
|
||||
}
|
||||
|
||||
function format_single(formatter, value, error_text, method) {
|
||||
var val = value,
|
||||
ok = true,
|
||||
msg = null;
|
||||
try {
|
||||
if (method === 'format') {
|
||||
val = formatter.format(val);
|
||||
} else {
|
||||
val = formatter.parse(val);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.reason !== method) throw e;
|
||||
ok = false;
|
||||
value = e.value;
|
||||
msg = e.message || error_text;
|
||||
}
|
||||
return {
|
||||
ok: ok,
|
||||
value: val,
|
||||
message: msg
|
||||
};
|
||||
}
|
||||
|
||||
function format_core(formatter, value, error_text, method) {
|
||||
|
||||
if (!formatter) return { ok: true, value: value };
|
||||
if (lang.isArray(value)) {
|
||||
var res = {
|
||||
ok: true,
|
||||
value: [],
|
||||
messages: []
|
||||
};
|
||||
for (var i=0, l=value.length; i<l; i++) {
|
||||
var single_res = format_single(formatter, value[i], error_text, method);
|
||||
res.ok = res.ok && single_res.ok;
|
||||
res.value[i] =single_res.value;
|
||||
res.messages[i] = single_res.message;
|
||||
if (!res.ok) {
|
||||
if (l > 1) res.message = error_text;
|
||||
else res.message = res.messages[0];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return format_single(formatter, value, error_text, method);
|
||||
}
|
||||
}
|
||||
|
||||
function format(formatter, value, error_text) {
|
||||
|
||||
var err = error_text || text.get('@i18n:widget.validation.format');
|
||||
return format_core(formatter, value, err, 'format');
|
||||
}
|
||||
|
||||
function parse(formatter, value, error_text) {
|
||||
|
||||
var err = error_text || text.get('@i18n:widget.validation.parse');
|
||||
return format_core(formatter, value, err, 'parse');
|
||||
}
|
||||
|
||||
function normalize_value(value) {
|
||||
|
||||
if (!(value instanceof Array)) {
|
||||
value = value !== undefined ? [value] : [];
|
||||
}
|
||||
if (!value.length) {
|
||||
value = [''];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function emit_delayed(target, type, event, delay) {
|
||||
|
||||
delay = delay || 0;
|
||||
window.setTimeout(function() {
|
||||
target.emit(type, event);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module with utility functions
|
||||
* @class
|
||||
* @singleton
|
||||
*/
|
||||
var util = {
|
||||
|
||||
/**
|
||||
* Checks if two variables have equal value
|
||||
*
|
||||
* - `string`, `number`, `function`, `boolean`, `null`,
|
||||
* `undefined` are compared with strict equality
|
||||
* - 'object' and arrays are compared by values
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* - `unordered` - boolean, sort arrays before value comparison. Does
|
||||
* not modify original values.
|
||||
* - `comparator`- function(a,b), returns bool - custom object comparator
|
||||
*
|
||||
* @param {Mixed} a
|
||||
* @param {Mixed} b
|
||||
* @param {String[]} [options]
|
||||
* @return {boolean} `a` and `b` are value-equal
|
||||
*/
|
||||
equals: equals,
|
||||
|
||||
/**
|
||||
* Check if value is empty.
|
||||
*
|
||||
* True when:
|
||||
*
|
||||
* - value is undefined or `null` or `''`
|
||||
* - value is empty Array
|
||||
* - value is Array with an empty string (`''`)
|
||||
* - value is empty Object- `{}`
|
||||
* @param value - value to check
|
||||
* @return {boolean}
|
||||
*/
|
||||
is_empty: is_empty,
|
||||
|
||||
/**
|
||||
* Special kind of negative `equals` where variants of `empty_value` are
|
||||
* considered same.
|
||||
*
|
||||
* @param {Mixed} value New value
|
||||
* @param {Mixed} pristine Pristine value
|
||||
* @param {String[]} [options] control options, same as in `equals`
|
||||
* @return {boolean} `value` and `pristine` differs
|
||||
*/
|
||||
dirty: dirty,
|
||||
|
||||
/**
|
||||
* Format value or values using a formatter
|
||||
*
|
||||
* Output format for single values:
|
||||
*
|
||||
* {
|
||||
* ok: true|false,
|
||||
* value: null | formatted value,
|
||||
* message: null | string
|
||||
* }
|
||||
*
|
||||
* Output form for array:
|
||||
*
|
||||
* {
|
||||
* ok: true|false,
|
||||
* value: array of formatted values,
|
||||
* messages: array of error messages
|
||||
* message: null | string
|
||||
* }
|
||||
*
|
||||
* @param {IPA.formatter} formatter
|
||||
* @param {Mixed} value
|
||||
* @param {string} error Default error message
|
||||
* @return {Object}
|
||||
*/
|
||||
format: format,
|
||||
|
||||
/**
|
||||
* Basically the same as format method, just uses formatter's `parse`
|
||||
* method instead of `format` method.
|
||||
*
|
||||
* @param {IPA.formatter} formatter
|
||||
* @param {Mixed} value
|
||||
* @param {string} error Default error message
|
||||
* @return {Object}
|
||||
*/
|
||||
parse: parse,
|
||||
|
||||
/**
|
||||
* Encapsulates value into array if it's not already an array.
|
||||
*
|
||||
* @param {Mixed} value
|
||||
* @returns {Array} normalized value
|
||||
*/
|
||||
normalize_value: normalize_value,
|
||||
|
||||
/**
|
||||
* Emit delayed event
|
||||
*
|
||||
* Uses timer in order to wait for current processing to finish.
|
||||
*
|
||||
* @param {Evented} object Source object which emits the event
|
||||
* @param {String} type Name of the event to emit
|
||||
* @param {Object} event Event object
|
||||
* @param {Number} [delay=0]
|
||||
*/
|
||||
emit_delayed: emit_delayed
|
||||
};
|
||||
|
||||
return util;
|
||||
});
|
||||
@@ -32,13 +32,15 @@ define(['dojo/_base/array',
|
||||
'./datetime',
|
||||
'./ipa',
|
||||
'./jquery',
|
||||
'./navigation',
|
||||
'./phases',
|
||||
'./reg',
|
||||
'./rpc',
|
||||
'./text'
|
||||
'./text',
|
||||
'./util'
|
||||
],
|
||||
function(array, lang, Evented, has, keys, on, builder, datetime, IPA, $,
|
||||
phases, reg, rpc, text) {
|
||||
function(array, lang, Evented, has, keys, on, builder, datetime,
|
||||
IPA, $, navigation, phases, reg, rpc, text, util) {
|
||||
|
||||
/**
|
||||
* Widget module
|
||||
@@ -127,6 +129,12 @@ IPA.widget = function(spec) {
|
||||
*/
|
||||
that.enabled = spec.enabled === undefined ? true : spec.enabled;
|
||||
|
||||
/**
|
||||
* Enables showing of validation errors
|
||||
* @property {boolean}
|
||||
*/
|
||||
that.show_errors = spec.show_errors === undefined ? true : spec.show_errors;
|
||||
|
||||
/**
|
||||
* Create HTML representation of a widget.
|
||||
* @method
|
||||
@@ -190,6 +198,24 @@ IPA.widget = function(spec) {
|
||||
return child;
|
||||
};
|
||||
|
||||
that.add_class = function(cls) {
|
||||
if (that.container) {
|
||||
that.container.addClass(cls);
|
||||
}
|
||||
};
|
||||
|
||||
that.remove_class = function(cls) {
|
||||
if (that.container) {
|
||||
that.container.removeClass(cls);
|
||||
}
|
||||
};
|
||||
|
||||
that.toggle_class = function(cls, flag) {
|
||||
if (that.container) {
|
||||
that.container.toggleClass(cls, flag);
|
||||
}
|
||||
};
|
||||
|
||||
that.widget_create = that.create;
|
||||
that.widget_set_enabled = that.set_enabled;
|
||||
|
||||
@@ -259,6 +285,7 @@ IPA.input_widget = function(spec) {
|
||||
* Value changed event.
|
||||
*
|
||||
* Raised when user modifies data by hand.
|
||||
* @deprecated
|
||||
*
|
||||
* @event
|
||||
*/
|
||||
@@ -266,6 +293,7 @@ IPA.input_widget = function(spec) {
|
||||
|
||||
/**
|
||||
* Undo clicked event.
|
||||
* @deprecated
|
||||
*
|
||||
* @event
|
||||
*/
|
||||
@@ -273,6 +301,7 @@ IPA.input_widget = function(spec) {
|
||||
|
||||
/**
|
||||
* Updated event.
|
||||
* @deprecated
|
||||
*
|
||||
* Raised when widget content gets updated - raised by
|
||||
* {@link IPA.input_widget#update} method.
|
||||
@@ -326,6 +355,13 @@ IPA.input_widget = function(spec) {
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Alias of save
|
||||
*/
|
||||
that.get_value = function() {
|
||||
return that.save();
|
||||
};
|
||||
|
||||
/**
|
||||
* This function creates an undo link in the container.
|
||||
* On_undo is a link click callback. It can be specified to custom
|
||||
@@ -383,27 +419,60 @@ IPA.input_widget = function(spec) {
|
||||
* @return {jQuery} error link jQuery reference
|
||||
*/
|
||||
that.get_error_link = function() {
|
||||
return $('span[name="error_link"]', that.container);
|
||||
return $('span[name="error_link"]', that.container).eq(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set's validity of widget's value. Usually checked by outside logic.
|
||||
* @param {Object} result Validation result as defined in IPA.validator
|
||||
*/
|
||||
that.set_valid = function(result) {
|
||||
|
||||
var old = that.valid;
|
||||
that.valid = result.valid;
|
||||
|
||||
that.toggle_class('valid', that.valid);
|
||||
if (!that.valid) {
|
||||
that.show_error(result.message);
|
||||
} else {
|
||||
that.hide_error();
|
||||
}
|
||||
if (old !== that.valid) {
|
||||
that.emit("valid-change", {
|
||||
source: that,
|
||||
valid: that.valid,
|
||||
result: result
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show error message
|
||||
* @protected
|
||||
* @param {string} message
|
||||
* @fires error-show
|
||||
* @param {Object} error
|
||||
*/
|
||||
that.show_error = function(message) {
|
||||
var error_link = that.get_error_link();
|
||||
error_link.html(message);
|
||||
error_link.css('display', '');
|
||||
that.emit('error-show', { source: that, error: message });
|
||||
if (that.show_errors) {
|
||||
var error_link = that.get_error_link();
|
||||
error_link.html(message);
|
||||
error_link.css('display', '');
|
||||
}
|
||||
that.emit('error-show', {
|
||||
source: that,
|
||||
error: message,
|
||||
displayed: that.show_errors
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide error message
|
||||
* @protected
|
||||
* @fires error-hide
|
||||
*/
|
||||
that.hide_error = function() {
|
||||
var error_link = that.get_error_link();
|
||||
error_link.html('');
|
||||
error_link.css('display', 'none');
|
||||
that.emit('error-hide', { source: that });
|
||||
};
|
||||
@@ -457,6 +526,38 @@ IPA.input_widget = function(spec) {
|
||||
return !that.read_only && !!that.writable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set writable
|
||||
* @fires writable-change
|
||||
* @param {boolean} writable
|
||||
*/
|
||||
that.set_writable = function(writable) {
|
||||
|
||||
var changed = writable !== that.writable;
|
||||
|
||||
that.writable = writable;
|
||||
|
||||
if (changed) {
|
||||
that.emit('writable-change', { source: that, writable: writable });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set read only
|
||||
* @fires readonly-change
|
||||
* @param {boolean} writable
|
||||
*/
|
||||
that.set_read_only = function(read_only) {
|
||||
|
||||
var changed = read_only !== that.read_only;
|
||||
|
||||
that.read_only = read_only;
|
||||
|
||||
if (changed) {
|
||||
that.emit('readonly-change', { source: that, read_only: read_only });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Focus input element
|
||||
* @abstract
|
||||
@@ -499,6 +600,7 @@ IPA.input_widget = function(spec) {
|
||||
// methods that should be invoked by subclasses
|
||||
that.widget_hide_error = that.hide_error;
|
||||
that.widget_show_error = that.show_error;
|
||||
that.widget_set_valid = that.set_valid;
|
||||
|
||||
return that;
|
||||
};
|
||||
@@ -680,7 +782,7 @@ IPA.multivalued_widget = function(spec) {
|
||||
that.child_spec = spec.child_spec;
|
||||
that.size = spec.size || 30;
|
||||
that.undo_control;
|
||||
that.initialized = false;
|
||||
that.initialized = true;
|
||||
|
||||
that.rows = [];
|
||||
|
||||
@@ -693,24 +795,16 @@ IPA.multivalued_widget = function(spec) {
|
||||
row.remove_link.show();
|
||||
}
|
||||
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('child-value-change', { source: that, row: row });
|
||||
that.emit('value-change', { source: that });
|
||||
that.emit('child-value-change', { source: that, row: row });
|
||||
};
|
||||
|
||||
that.on_child_undo_clicked = function(row) {
|
||||
if (row.is_new) {
|
||||
that.remove_row(row);
|
||||
} else {
|
||||
//reset
|
||||
row.widget.update(row.original_values);
|
||||
row.widget.set_deleted(false);
|
||||
row.deleted = false;
|
||||
row.remove_link.show();
|
||||
that.reset_row(row);
|
||||
}
|
||||
|
||||
row.widget.hide_undo();
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('child-undo-click', { source: that, row: row });
|
||||
};
|
||||
|
||||
@@ -745,20 +839,45 @@ IPA.multivalued_widget = function(spec) {
|
||||
}
|
||||
};
|
||||
|
||||
that.show_child_error = function(index, error) {
|
||||
that.set_valid = function (result) {
|
||||
|
||||
that.rows[index].widget.show_error(error);
|
||||
};
|
||||
var old = that.valid;
|
||||
that.valid = result.valid;
|
||||
|
||||
that.get_saved_value_row_index = function(index) {
|
||||
if (!result.valid && result.errors) {
|
||||
var offset = 0;
|
||||
for (var i=0; i<that.rows.length; i++) {
|
||||
|
||||
for (var i=0; i<that.rows.length;i++) {
|
||||
var val_result = null;
|
||||
if (that.rows[i].deleted) {
|
||||
offset++;
|
||||
val_result = { valid: true };
|
||||
} else {
|
||||
val_result = result.results[i-offset];
|
||||
}
|
||||
var widget = that.rows[i].widget;
|
||||
if (val_result) widget.set_valid(val_result);
|
||||
}
|
||||
|
||||
if(that.rows[i].deleted) index++;
|
||||
if(i === index) return i;
|
||||
if (that.rows.length > 0) {
|
||||
var error_link = that.get_error_link();
|
||||
error_link.css('display', 'none');
|
||||
error_link.html('');
|
||||
} else {
|
||||
that.show_error(result.message);
|
||||
}
|
||||
|
||||
} else {
|
||||
that.hide_error();
|
||||
}
|
||||
|
||||
return -1; //error state
|
||||
if (old !== that.valid) {
|
||||
that.emit("valid-change", {
|
||||
source: that,
|
||||
valid: that.valid,
|
||||
result: result
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
that.save = function() {
|
||||
@@ -829,10 +948,10 @@ IPA.multivalued_widget = function(spec) {
|
||||
row.original_values = values;
|
||||
row.widget.update(values);
|
||||
|
||||
row.widget.value_changed.attach(function() {
|
||||
on(row.widget, 'value-change', function() {
|
||||
that.on_child_value_changed(row);
|
||||
});
|
||||
row.widget.undo_clicked.attach(function() {
|
||||
on(row.widget, 'undo-click', function() {
|
||||
that.on_child_undo_clicked(row);
|
||||
});
|
||||
on(row.widget, 'error-show', function() {
|
||||
@@ -847,8 +966,6 @@ IPA.multivalued_widget = function(spec) {
|
||||
html: text.get('@i18n:buttons.remove'),
|
||||
click: function () {
|
||||
that.remove_row(row);
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('value-change', { source: that });
|
||||
return false;
|
||||
}
|
||||
}).appendTo(row.container);
|
||||
@@ -902,6 +1019,17 @@ IPA.multivalued_widget = function(spec) {
|
||||
}).appendTo(container);
|
||||
};
|
||||
|
||||
that.reset_row = function(row) {
|
||||
row.widget.update(row.original_values);
|
||||
row.widget.set_deleted(false);
|
||||
row.deleted = false;
|
||||
row.remove_link.show();
|
||||
row.widget.hide_undo();
|
||||
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('value-change', { source: that });
|
||||
};
|
||||
|
||||
that.remove_row = function(row) {
|
||||
if (row.is_new) {
|
||||
row.container.remove();
|
||||
@@ -912,6 +1040,8 @@ IPA.multivalued_widget = function(spec) {
|
||||
row.remove_link.hide();
|
||||
row.widget.show_undo();
|
||||
}
|
||||
that.value_changed.notify([], that);
|
||||
that.emit('value-change', { source: that });
|
||||
};
|
||||
|
||||
that.remove_rows = function() {
|
||||
@@ -929,30 +1059,14 @@ IPA.multivalued_widget = function(spec) {
|
||||
|
||||
if (row.deleted || row.is_new) return true;
|
||||
|
||||
var values = row.widget.save();
|
||||
if (!values) return false;
|
||||
var value = row.widget.save();
|
||||
|
||||
if (row.original_values.length !== values.length) return true;
|
||||
|
||||
for (var i=0; i<values.length; i++) {
|
||||
if (values[i] !== row.original_values[i]) {
|
||||
return true;
|
||||
}
|
||||
if (util.dirty(value, row.original_values, { unordered: true })) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
that.test_dirty = function() {
|
||||
var dirty = false;
|
||||
|
||||
for(var i=0; i < that.rows.length; i++) {
|
||||
dirty = dirty || that.test_dirty_row(that.rows[i]);
|
||||
}
|
||||
|
||||
return dirty;
|
||||
};
|
||||
|
||||
that.update = function(values, index) {
|
||||
|
||||
var value;
|
||||
@@ -1693,10 +1807,15 @@ IPA.select_widget = function(spec) {
|
||||
};
|
||||
|
||||
that.update = function(values) {
|
||||
var old = that.save()[0];
|
||||
var value = values[0];
|
||||
var option = $('option[value="'+value+'"]', that.select);
|
||||
if (!option.length) return;
|
||||
option.prop('selected', true);
|
||||
if (option.length) {
|
||||
option.prop('selected', true);
|
||||
} else {
|
||||
// default was selected instead of supplied value, hence notify
|
||||
util.emit_delayed(that,'value-change', { source: that });
|
||||
}
|
||||
that.updated.notify([], that);
|
||||
that.emit('update', { source: that });
|
||||
};
|
||||
@@ -1869,7 +1988,8 @@ IPA.boolean_formatter = function(spec) {
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.formatter(spec);
|
||||
|
||||
/** Parse error */
|
||||
that.parse_error = text.get(spec.parse_error || 'Boolean value expected');
|
||||
/** Formatted value for true */
|
||||
that.true_value = text.get(spec.true_value || '@i18n:true');
|
||||
/** Formatted value for false */
|
||||
@@ -1881,9 +2001,9 @@ IPA.boolean_formatter = function(spec) {
|
||||
/**
|
||||
* Result of parse of `undefined` or `null` value will be `empty_value`
|
||||
* if set.
|
||||
* @property {boolean|undefined}
|
||||
* @property {boolean}
|
||||
*/
|
||||
that.empty_value = spec.empty_value;
|
||||
that.empty_value = spec.empty_value !== undefined ? spec.empty_value : false;
|
||||
|
||||
/**
|
||||
* Convert string boolean value into real boolean value, or keep
|
||||
@@ -1894,9 +2014,8 @@ IPA.boolean_formatter = function(spec) {
|
||||
*/
|
||||
that.parse = function(value) {
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
if (that.empty_value !== undefined) value = that.empty_value;
|
||||
else return '';
|
||||
if (util.is_empty(value)) {
|
||||
value = that.empty_value;
|
||||
}
|
||||
|
||||
if (value instanceof Array) {
|
||||
@@ -1910,11 +2029,17 @@ IPA.boolean_formatter = function(spec) {
|
||||
value = true;
|
||||
} else if (value === 'false') {
|
||||
value = false;
|
||||
} // leave other values unchanged
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
if (that.invert_value) value = !value;
|
||||
} else {
|
||||
throw {
|
||||
reason: 'parse',
|
||||
value: that.empty_value,
|
||||
message: that.parse_error
|
||||
};
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -1987,13 +2112,32 @@ IPA.datetime_formatter = function(spec) {
|
||||
|
||||
var that = IPA.formatter(spec);
|
||||
that.template = spec.template;
|
||||
that.parse_error = text.get(spec.parse_error || '@i18n:widget.validation.datetime');
|
||||
|
||||
that.parse = function(value) {
|
||||
if (value === '') return null;
|
||||
var date = datetime.parse(value);
|
||||
if (!date) {
|
||||
throw {
|
||||
reason: 'parse',
|
||||
value: null,
|
||||
message: that.parse_error
|
||||
};
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
that.format = function(value) {
|
||||
|
||||
if (!value) return '';
|
||||
var date = datetime.parse(value);
|
||||
if (!date) return value;
|
||||
var str = datetime.format(date, that.template);
|
||||
if (!(value instanceof Date)) {
|
||||
throw {
|
||||
reason: 'format',
|
||||
value: '',
|
||||
message: 'Input value is not of Date type'
|
||||
};
|
||||
}
|
||||
var str = datetime.format(value, that.template);
|
||||
return str;
|
||||
};
|
||||
return that;
|
||||
@@ -2659,12 +2803,6 @@ IPA.table_widget = function (spec) {
|
||||
return rows;
|
||||
};
|
||||
|
||||
that.show_error = function(message) {
|
||||
var error_link = that.get_error_link();
|
||||
error_link.html(message);
|
||||
error_link.css('display', '');
|
||||
};
|
||||
|
||||
that.set_enabled = function(enabled) {
|
||||
that.widget_set_enabled(enabled);
|
||||
|
||||
@@ -3599,13 +3737,26 @@ IPA.entity_select_widget = function(spec) {
|
||||
IPA.link_widget = function(spec) {
|
||||
var that = IPA.input_widget(spec);
|
||||
|
||||
that.is_link = spec.is_link || false;
|
||||
/**
|
||||
* Entity a link points to
|
||||
* @property {entity.entity}
|
||||
*/
|
||||
that.other_entity = IPA.get_entity(spec.other_entity);
|
||||
|
||||
/**
|
||||
* Raised when link is clicked
|
||||
* @event
|
||||
* Function which should return primary keys of link target in case of
|
||||
* link points to an entity.
|
||||
* @property {Function}
|
||||
*/
|
||||
that.link_clicked = IPA.observer();
|
||||
that.other_pkeys = spec.other_pkeys || other_pkeys;
|
||||
|
||||
that.is_link = spec.is_link || false;
|
||||
|
||||
that.value = [];
|
||||
|
||||
function other_pkeys () {
|
||||
return that.facet.get_pkeys();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
that.create = function(container) {
|
||||
@@ -3617,8 +3768,7 @@ IPA.link_widget = function(spec) {
|
||||
html: '',
|
||||
'class': 'link-btn',
|
||||
click: function() {
|
||||
that.link_clicked.notify([], that);
|
||||
that.emit('link-click', { source: that });
|
||||
that.on_link_clicked();
|
||||
return false;
|
||||
}
|
||||
}).appendTo(container);
|
||||
@@ -3628,11 +3778,19 @@ IPA.link_widget = function(spec) {
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
that.update = function (values){
|
||||
that.update = function(values) {
|
||||
|
||||
if (values || values.length > 0) {
|
||||
that.nonlink.text(values[0]);
|
||||
that.link.text(values[0]);
|
||||
that.value = util.normalize_value(values)[0] || '';
|
||||
that.link.html(that.value);
|
||||
that.nonlink.html(that.value);
|
||||
|
||||
that.check_entity_link();
|
||||
that.updated.notify([], that);
|
||||
that.emit('update', { source: that });
|
||||
};
|
||||
|
||||
that.update_link = function() {
|
||||
if (that.value) {
|
||||
if(that.is_link) {
|
||||
that.link.css('display','');
|
||||
that.nonlink.css('display','none');
|
||||
@@ -3641,13 +3799,54 @@ IPA.link_widget = function(spec) {
|
||||
that.nonlink.css('display','');
|
||||
}
|
||||
} else {
|
||||
that.link.html('');
|
||||
that.nonlink.html('');
|
||||
that.link.css('display','none');
|
||||
that.nonlink.css('display','none');
|
||||
}
|
||||
that.updated.notify([], that);
|
||||
that.emit('update', { source: that });
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for widget `link_click` event
|
||||
*/
|
||||
that.on_link_clicked = function() {
|
||||
|
||||
navigation.show_entity(
|
||||
that.other_entity.name,
|
||||
'default',
|
||||
that.other_pkeys());
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if entity exists
|
||||
*
|
||||
* - only if link points to an entity
|
||||
* - updates link visibility accordingly
|
||||
*/
|
||||
that.check_entity_link = function() {
|
||||
|
||||
//In some cases other entity may not be present.
|
||||
//For example when DNS is not configured.
|
||||
if (!that.other_entity) {
|
||||
that.is_link = false;
|
||||
return;
|
||||
}
|
||||
|
||||
rpc.command({
|
||||
entity: that.other_entity.name,
|
||||
method: 'show',
|
||||
args: that.other_pkeys(),
|
||||
options: {},
|
||||
retry: false,
|
||||
on_success: function(data) {
|
||||
that.is_link = data.result && data.result.result;
|
||||
that.update_link();
|
||||
},
|
||||
on_error: function() {
|
||||
that.is_link = false;
|
||||
that.update_link();
|
||||
}
|
||||
}).execute();
|
||||
|
||||
that.update_link();
|
||||
};
|
||||
|
||||
/** @inheritDoc */
|
||||
@@ -4119,7 +4318,7 @@ exp.fluid_layout = IPA.fluid_layout = function(spec) {
|
||||
text: label_text
|
||||
}).appendTo(label_cont);
|
||||
|
||||
var input = widget.get_input();
|
||||
var input = widget.get_input && widget.get_input();
|
||||
|
||||
if (input && input.length) input = input[0];
|
||||
|
||||
@@ -4150,6 +4349,8 @@ exp.fluid_layout = IPA.fluid_layout = function(spec) {
|
||||
that.register_state_handlers = function(widget) {
|
||||
on(widget, 'require-change', that.on_require_change);
|
||||
on(widget, 'enabled-change', that.on_enabled_change);
|
||||
on(widget, 'readonly-change', that.on_require_change);
|
||||
on(widget, 'writable-change', that.on_require_change);
|
||||
on(widget, 'error-show', that.on_error_show);
|
||||
on(widget, 'error-hide', that.on_error_hide);
|
||||
};
|
||||
@@ -4165,7 +4366,7 @@ exp.fluid_layout = IPA.fluid_layout = function(spec) {
|
||||
|
||||
var row = that._get_row(event);
|
||||
if (!row) return;
|
||||
row.toggleClass('required', !!event.required);
|
||||
row.toggleClass('required', !!event.required && event.source.is_writable());
|
||||
};
|
||||
|
||||
that.on_error_show = function(event) {
|
||||
@@ -4184,7 +4385,7 @@ exp.fluid_layout = IPA.fluid_layout = function(spec) {
|
||||
|
||||
that.update_state = function(row, widget) {
|
||||
row.toggleClass('disabled', !widget.enabled);
|
||||
row.toggleClass('required', !!widget.required);
|
||||
row.toggleClass('required', !!widget.required && widget.is_writable());
|
||||
};
|
||||
|
||||
that._get_row = function(event) {
|
||||
@@ -4685,9 +4886,10 @@ IPA.widget_container = function(spec) {
|
||||
|
||||
/**
|
||||
* Widget builder
|
||||
* @class
|
||||
* @class widget.widget_builder
|
||||
* @alternateClassName IPA.widget_builder
|
||||
*/
|
||||
IPA.widget_builder = function(spec) {
|
||||
exp.widget_builder = IPA.widget_builder = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
@@ -4792,9 +4994,9 @@ IPA.sshkey_widget = function(spec) {
|
||||
that.create_error_link(container);
|
||||
};
|
||||
|
||||
that.update = function(values) {
|
||||
that.update = function(value) {
|
||||
|
||||
var key = values && values.length ? values[0] : null;
|
||||
var key = value[0];
|
||||
|
||||
if (!key || key === '') {
|
||||
key = {};
|
||||
@@ -4821,9 +5023,7 @@ IPA.sshkey_widget = function(spec) {
|
||||
};
|
||||
|
||||
that.save = function() {
|
||||
var value = that.key.key;
|
||||
value = value ? [value] : [''];
|
||||
return value;
|
||||
return that.key;
|
||||
};
|
||||
|
||||
that.update_link = function() {
|
||||
|
||||
@@ -239,7 +239,17 @@ test("Testing type target.", function() {
|
||||
|
||||
same(target_widget.target, 'type', 'type selected');
|
||||
|
||||
$("input[type=checkbox]").attr("checked",true);
|
||||
var attrs_w = target_widget.widgets.get_widget('attrs');
|
||||
var options = attrs_w.options;
|
||||
ok(options.length > 0, "Attrs has some options");
|
||||
// check them all
|
||||
var values = [];
|
||||
for (var i=0,l=options.length; i<l; i++) {
|
||||
values.push(options[i].value);
|
||||
}
|
||||
attrs_w.update(values);
|
||||
attrs_w.emit('value-change', { source: attrs_w });
|
||||
|
||||
var record = {};
|
||||
target_facet.save(record);
|
||||
|
||||
@@ -250,8 +260,7 @@ test("Testing type target.", function() {
|
||||
'ipapermtarget', 'memberof', 'attrs'],
|
||||
'type and attrs rows visible');
|
||||
|
||||
ok((record.attrs.length > 10),
|
||||
"response length shows some attrs set");
|
||||
same(record.attrs.length, options.length, "response contains all checked attrs");
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -560,6 +560,7 @@
|
||||
"error": "Text does not match field pattern",
|
||||
"datetime": "Must be an UTC date/time value (e.g., \"2014-01-20 17:58:01Z\")",
|
||||
"decimal": "Must be a decimal number",
|
||||
"format": "Format error",
|
||||
"integer": "Must be an integer",
|
||||
"ip_address": "Not a valid IP address",
|
||||
"ip_v4_address": "Not a valid IPv4 address",
|
||||
@@ -567,6 +568,7 @@
|
||||
"max_value": "Maximum value is ${value}",
|
||||
"min_value": "Minimum value is ${value}",
|
||||
"net_address": "Not a valid network address",
|
||||
"parse": "Parse error",
|
||||
"port": "'${port}' is not a valid port",
|
||||
"required": "Required field",
|
||||
"unsupported": "Unsupported value"
|
||||
|
||||
@@ -24,12 +24,12 @@ define([
|
||||
'freeipa/jquery',
|
||||
'freeipa/details',
|
||||
'freeipa/facet',
|
||||
'freeipa/field',
|
||||
'freeipa/reg',
|
||||
'freeipa/rpc',
|
||||
'freeipa/entity',
|
||||
'freeipa/field',
|
||||
'freeipa/widget'],
|
||||
function(md, IPA, $, mod_details, mod_facet, reg, rpc) {
|
||||
function(md, IPA, $, mod_details, mod_facet, mod_field, reg, rpc) {
|
||||
return function() {
|
||||
|
||||
var details_container;
|
||||
@@ -41,6 +41,7 @@ module('details', {
|
||||
|
||||
mod_facet.register();
|
||||
mod_details.register();
|
||||
mod_field.register();
|
||||
|
||||
IPA.init({
|
||||
url: 'data',
|
||||
@@ -255,7 +256,10 @@ test("Testing details lifecycle: create, load.", function(){
|
||||
ok (load_called, 'load manager called');
|
||||
|
||||
var field = facet.fields.get_field('test');
|
||||
field.set_dirty(true);
|
||||
field.set_value("foo");
|
||||
var widget = facet.widgets.get_widget('contact.test');
|
||||
// simulate user change
|
||||
widget.emit('value-change', { source: widget, value: "foo" });
|
||||
|
||||
facet.update(
|
||||
function(){update_success_called = true;},
|
||||
|
||||
@@ -22,9 +22,10 @@ define([
|
||||
'freeipa/ipa',
|
||||
'freeipa/jquery',
|
||||
'freeipa/datetime',
|
||||
'freeipa/util',
|
||||
'freeipa/field',
|
||||
'freeipa/widget'],
|
||||
function(IPA, $, datetime) { return function() {
|
||||
function(IPA, $, datetime, util) { return function() {
|
||||
|
||||
var old;
|
||||
|
||||
@@ -143,6 +144,23 @@ test('Testing IPA.defined', function() {
|
||||
same(IPA.defined(null), false, 'null');
|
||||
});
|
||||
|
||||
test('Testing util.equals', function() {
|
||||
|
||||
ok(util.equals([], []), 'Empty Arrays');
|
||||
ok(util.equals([1, "a", false, true], [1, "a", false, true]), 'Arrays');
|
||||
ok(util.equals(true, true), 'Boolean: true');
|
||||
ok(util.equals(false, false), 'Boolean: false');
|
||||
ok(!util.equals(true, false), 'Negative: boolean');
|
||||
ok(!util.equals(false, true), 'Negative: boolean');
|
||||
ok(util.equals("abc", "abc"), 'Positive: strings');
|
||||
ok(!util.equals("abc", "aBC"), 'Negative: string casing');
|
||||
ok(util.equals(1, 1), 'Positive: number');
|
||||
ok(util.equals(1.0, 1), 'Positive: number');
|
||||
ok(util.equals(2.2, 2.2), 'Positive: number');
|
||||
|
||||
ok(!util.equals([], [""]), 'Negative: empty array');
|
||||
});
|
||||
|
||||
test('Testing datetime', function() {
|
||||
|
||||
var valid = [
|
||||
|
||||
@@ -18,9 +18,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define(['freeipa/ipa', 'freeipa/jquery', 'freeipa/field', 'freeipa/widget',
|
||||
'freeipa/entity'],
|
||||
function(IPA, $) { return function() {
|
||||
define([
|
||||
'dojo/on',
|
||||
'freeipa/ipa',
|
||||
'freeipa/jquery',
|
||||
'freeipa/group',
|
||||
'freeipa/field',
|
||||
'freeipa/widget',
|
||||
'freeipa/entity'
|
||||
], function(on, IPA, $, group) { return function() {
|
||||
|
||||
var widget_container;
|
||||
var widget;
|
||||
@@ -43,6 +49,7 @@ module('widget',{
|
||||
factory = null;
|
||||
spec = null;
|
||||
|
||||
group.register();
|
||||
|
||||
},
|
||||
teardown: function() {
|
||||
@@ -119,23 +126,28 @@ function text_tests(widget,input){
|
||||
function multivalued_text_tests(widget) {
|
||||
|
||||
var values = ['val1', 'val2', 'val3'];
|
||||
var changed = false;
|
||||
function on_change (event) {
|
||||
changed = true;
|
||||
}
|
||||
on(widget, 'value-change', on_change);
|
||||
|
||||
widget.update(values);
|
||||
|
||||
same(widget.save(), values, "All values loaded");
|
||||
same(widget.test_dirty(), false, "Field initially clean");
|
||||
|
||||
values = ['val1', 'val2', 'val3', 'val4'];
|
||||
widget.add_row(['val4']);
|
||||
|
||||
same(widget.save(), values, "Value added");
|
||||
same(widget.test_dirty(), true, "Field is dirty");
|
||||
ok(changed, "Value changed");
|
||||
changed = false;
|
||||
|
||||
values = ['val1', 'val3', 'val4'];
|
||||
widget.remove_row(widget.rows[1]);
|
||||
|
||||
same(widget.save(), values, "Value removed");
|
||||
same(widget.test_dirty(), true, "Field is dirty");
|
||||
ok(changed, "Value changed");
|
||||
changed = false;
|
||||
}
|
||||
|
||||
test("IPA.table_widget" ,function(){
|
||||
@@ -294,7 +306,10 @@ test("IPA.entity_link_widget" ,function(){
|
||||
factory = IPA.link_widget;
|
||||
spec = {
|
||||
name: 'gidnumber',
|
||||
other_entity:'group'
|
||||
other_entity:'group',
|
||||
other_pkeys: function() {
|
||||
return ['kfrog'];
|
||||
}
|
||||
};
|
||||
base_widget_test(widget,'user','test_value');
|
||||
|
||||
@@ -304,8 +319,6 @@ test("IPA.entity_link_widget" ,function(){
|
||||
}
|
||||
};
|
||||
|
||||
var mock_record = { uid: ['kfrog'], gidnumber: ['123456']};
|
||||
|
||||
widget.entity = mock_entity;
|
||||
widget.create(widget_container);
|
||||
|
||||
@@ -315,7 +328,7 @@ test("IPA.entity_link_widget" ,function(){
|
||||
ok(nonlink.length > 1);
|
||||
ok(link.length > 1);
|
||||
|
||||
widget.is_link = true; //setting is_link is responsibility of field
|
||||
var mock_record = { gidnumber: ['123456']};
|
||||
widget.update(mock_record.gidnumber);
|
||||
|
||||
link = widget_container.find('a:contains("123456")');
|
||||
|
||||
@@ -696,6 +696,7 @@ class i18n_messages(Command):
|
||||
"error": _("Text does not match field pattern"),
|
||||
"datetime": _("Must be an UTC date/time value (e.g., \"2014-01-20 17:58:01Z\")"),
|
||||
"decimal": _("Must be a decimal number"),
|
||||
"format": _("Format error"),
|
||||
"integer": _("Must be an integer"),
|
||||
"ip_address": _('Not a valid IP address'),
|
||||
"ip_v4_address": _('Not a valid IPv4 address'),
|
||||
@@ -703,6 +704,7 @@ class i18n_messages(Command):
|
||||
"max_value": _("Maximum value is ${value}"),
|
||||
"min_value": _("Minimum value is ${value}"),
|
||||
"net_address": _("Not a valid network address"),
|
||||
"parse": _("Parse error"),
|
||||
"port": _("'${port}' is not a valid port"),
|
||||
"required": _("Required field"),
|
||||
"unsupported": _("Unsupported value"),
|
||||
|
||||
Reference in New Issue
Block a user