https://fedorahosted.org/freeipa/ticket/4554
[PATCH] 784 webui: refactor combobox search to support search providers
Comboboxe's search logic was moved into a separate object - a search
provider. This change makes Entity Select a regular combobox with a
special provider.
new JointCBSearchProvider allows to combine results of two providers
into a single result.
[PATCH] 785 webui: offer suffixes of trusted domains for overrides
[PATCH] 786 webui: unable to select single value in CB by enter key
Fix: If editable combobox has one value, the value is selected and
changed by hand, it can't be re-selected by enter key.
--
Petr Vobornik
From 091139c851c18155f3b3d75afafbb202a4b12859 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <[email protected]>
Date: Wed, 12 Nov 2014 14:58:46 +0100
Subject: [PATCH] webui: offer suffixes of trusted domains for overrides
https://fedorahosted.org/freeipa/ticket/4554
---
install/ui/src/freeipa/idviews.js | 86 ++++++++++++++++++++++++++++++++++-----
1 file changed, 76 insertions(+), 10 deletions(-)
diff --git a/install/ui/src/freeipa/idviews.js b/install/ui/src/freeipa/idviews.js
index 823936401653096a0a649282e3b18f573de09804..f85aedebc0b2b50773a183c1b7a7648791ec3bbf 100644
--- a/install/ui/src/freeipa/idviews.js
+++ b/install/ui/src/freeipa/idviews.js
@@ -20,7 +20,10 @@
*/
define([
+ 'dojo/_base/declare',
+ 'dojo/Deferred',
'dojo/on',
+ 'dojo/when',
'./ipa',
'./jquery',
'./menu',
@@ -30,9 +33,11 @@ define([
'./text',
'./details',
'./facet',
+ './widget',
'./search',
'./entity'],
- function(on, IPA, $, menu, phases, reg, rpc, text, mod_details, mod_facet) {
+ function(declare, Deferred, on, when, IPA, $, menu, phases,
+ reg, rpc, text, mod_details, mod_facet, mod_widget) {
/**
* ID Views module
* @class
@@ -274,21 +279,36 @@ return {
],
fields: [
{
- $type: 'entity_select',
+ $type: 'combobox',
label: '@i18n:objects.idoverrideuser.anchor_label',
name: 'ipaanchoruuid',
- other_entity: 'user',
- other_field: 'uid',
editable: true,
- tooltip: '@i18n:objects.idoverrideuser.anchor_tooltip'
+ tooltip: '@i18n:objects.idoverrideuser.anchor_tooltip',
+ search_provider: {
+ $ctor: mod_widget.JointCBSearchProvider,
+ providers: [
+ {
+ $ctor: idviews.TrustSuffixCBSearchProvider
+ },
+ {
+ $ctor: mod_widget.EntityCBSearchProvider,
+ entity: 'user'
+ }
+ ]
+ }
},
{
+ $type: 'combobox',
label: '@i18n:objects.idoverrideuser.anchor_label',
name: 'ipaanchoruuid_default',
param: 'ipaanchoruuid',
tooltip: '@i18n:objects.idoverrideuser.anchor_tooltip_ad',
visible: false,
- enabled: false
+ enabled: false,
+ editable: true,
+ search_provider: {
+ $ctor: idviews.TrustSuffixCBSearchProvider
+ }
},
'uid',
'gecos',
@@ -361,18 +381,33 @@ return {
$type: 'entity_select',
label: '@i18n:objects.idoverridegroup.anchor_label',
name: 'ipaanchoruuid',
- other_entity: 'group',
- other_field: 'cn',
editable: true,
- tooltip: '@i18n:objects.idoverridegroup.anchor_tooltip'
+ tooltip: '@i18n:objects.idoverridegroup.anchor_tooltip',
+ search_provider: {
+ $ctor: mod_widget.JointCBSearchProvider,
+ providers: [
+ {
+ $ctor: idviews.TrustSuffixCBSearchProvider
+ },
+ {
+ $ctor: mod_widget.EntityCBSearchProvider,
+ entity: 'group'
+ }
+ ]
+ }
},
{
+ $type: 'combobox',
label: '@i18n:objects.idoverridegroup.anchor_label',
name: 'ipaanchoruuid_default',
param: 'ipaanchoruuid',
tooltip: '@i18n:objects.idoverridegroup.anchor_tooltip_ad',
visible: false,
- enabled: false
+ enabled: false,
+ editable: true,
+ search_provider: {
+ $ctor: idviews.TrustSuffixCBSearchProvider
+ }
},
'cn',
'gidnumber',
@@ -455,6 +490,37 @@ idviews.idoverride_adder_policy = function (spec) {
};
/**
+ * Search provider which returns trusted domains in form of suffixes
+ * @class
+ * @extends widget.EntityCBSearchProvider
+ */
+idviews.TrustSuffixCBSearchProvider = declare([mod_widget.EntityCBSearchProvider], {
+
+ /**
+ * @inheritDoc
+ * @protected
+ */
+ _search: function(filter) {
+ if (!IPA.trust_enabled) return [];
+ var def = new Deferred();
+ when(this.inherited(arguments), function(results) {
+ for (var i=0, l=results.length; i<l; i++) {
+ results[i] = '@'+results[i];
+ }
+ def.resolve(results);
+ }, function(error) {
+ def.reject(error);
+ });
+ return def;
+ },
+
+ constructor: function(spec) {
+ this.entity = this.entity || reg.entity.get('trust');
+ this.param = this.param || 'cn';
+ }
+});
+
+/**
* Apply Id view on hosts on hostgroup action base class
*
* @class idviews.apply_action
--
1.9.3
From feb564d167fc92492b831191eaab7235800c9258 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <[email protected]>
Date: Wed, 12 Nov 2014 15:41:44 +0100
Subject: [PATCH] webui: unable to select single value in CB by enter key
Fix: If editable combobox has one value, the value is selected and changed by hand, it can't be re-selected by enter key.
---
install/ui/src/freeipa/widget.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js
index abfdedfaf4e04feadb80d8d40f23e01770260282..f65dc47a0bb827f811cebb6df5ba2ee6117d7f54 100644
--- a/install/ui/src/freeipa/widget.js
+++ b/install/ui/src/freeipa/widget.js
@@ -4006,6 +4006,7 @@ IPA.combobox_widget = function(spec) {
that.list_on_keyup = function(e) {
if (e.which === keys.ENTER || e.which === keys.SPACE) {
e.stopPropagation();
+ that.list_on_change();
that.close();
IPA.select_range(that.input, 0, 0);
return false;
--
1.9.3
From 92ad60826aa20089295ac9152441cddc84a34b2b Mon Sep 17 00:00:00 2001
From: Petr Vobornik <[email protected]>
Date: Wed, 5 Nov 2014 19:47:06 +0100
Subject: [PATCH] webui: refactor combobox search to support search providers
Comboboxe's search logic was moved into a separate object - a search provider. This change makes Entity Select a regular combobox with a special provider.
new JointCBSearchProvider allows to combine results of two providers into a single result.
---
install/ui/doc/categories.json | 6 +
install/ui/doc/config.json | 2 +-
install/ui/src/freeipa/entity.js | 4 +-
install/ui/src/freeipa/host.js | 33 ++---
install/ui/src/freeipa/trust.js | 38 +++---
install/ui/src/freeipa/widget.js | 262 ++++++++++++++++++++++++++++++---------
6 files changed, 239 insertions(+), 106 deletions(-)
diff --git a/install/ui/doc/categories.json b/install/ui/doc/categories.json
index c84077682eafa42981e8a1c1a2f93c712e6421fd..1e99fb3e55a294be6e1445dcba01174614bedbb6 100644
--- a/install/ui/doc/categories.json
+++ b/install/ui/doc/categories.json
@@ -223,6 +223,12 @@
"field.validator",
"*_validator"
]
+ },
+ {
+ "name": "Combobox search providers",
+ "classes": [
+ "*CBSearchProvider"
+ ]
}
]
},
diff --git a/install/ui/doc/config.json b/install/ui/doc/config.json
index 6a200f9ae673b599e4536a5974ed219bbfe85f0c..a702a80a054683100f2f6435f6eaa7a398ba66a3 100644
--- a/install/ui/doc/config.json
+++ b/install/ui/doc/config.json
@@ -4,7 +4,7 @@
"--guides": "guides.json",
"--css": ["doc.css"],
"--external": ["jQuery", "Store", "QueryResult", "Stateful", "Evented",
- "XMLHttpRequest", "Promise"],
+ "XMLHttpRequest", "Promise", "Deferred"],
"--warnings": ["-link", "-nodoc"],
"--": [
"../src/freeipa/"
diff --git a/install/ui/src/freeipa/entity.js b/install/ui/src/freeipa/entity.js
index eb2d98a558b865b490fc56897f6fb485f87c6c81..171bb2b0d2573f11382f59c718981cbc9a5cfd4b 100644
--- a/install/ui/src/freeipa/entity.js
+++ b/install/ui/src/freeipa/entity.js
@@ -206,7 +206,9 @@ exp.entity = IPA.entity = function(spec) {
that.dialog_build_overrides = {
$pre_ops: [
function (spec, context) {
- spec.entity = context.entity;
+ if (context.entity) {
+ spec.entity = context.entity;
+ }
return spec;
}
],
diff --git a/install/ui/src/freeipa/host.js b/install/ui/src/freeipa/host.js
index 455ff8f50ec58104d4e046ec0fabf2a7e89eeeb2..20b6fede8331030ee78f1d8954cf9f5055b86405 100644
--- a/install/ui/src/freeipa/host.js
+++ b/install/ui/src/freeipa/host.js
@@ -390,13 +390,18 @@ IPA.host_fqdn_widget = function(spec) {
required: true
},
{
- $type: 'dnszone_select',
+ $type: 'entity_select',
name: 'dnszone',
label: '@mo:dnszone.label_singular',
editable: true,
empty_option: false,
required: true,
- searchable: true
+ searchable: true,
+ other_entity: 'dnszone',
+ other_field: 'idnsname',
+ filter_options: {
+ forward_only: true
+ }
}
];
@@ -581,28 +586,6 @@ IPA.host_deleter_dialog = function(spec) {
return that;
};
-IPA.dnszone_select_widget = function(spec) {
-
- spec = spec || {};
- spec.other_entity = 'dnszone';
- spec.other_field = 'idnsname';
-
- var that = IPA.entity_select_widget(spec);
-
- that.create_search_command = function(filter) {
- return rpc.command({
- entity: that.other_entity.name,
- method: 'find',
- args: [filter],
- options: {
- forward_only: true
- }
- });
- };
-
- return that;
-};
-
IPA.host_dnsrecord_entity_link_widget = function(spec) {
var that = IPA.link_widget(spec);
@@ -929,8 +912,6 @@ exp.register = function() {
e.register({type: 'host', spec: exp.entity_spec});
f.register('host_fqdn', IPA.host_fqdn_field);
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.field);
w.register('host_dnsrecord_entity_link', IPA.host_dnsrecord_entity_link_widget);
f.register('force_host_add_checkbox', IPA.checkbox_field);
diff --git a/install/ui/src/freeipa/trust.js b/install/ui/src/freeipa/trust.js
index 51cfefb99fe10010385b528af209ad535e88b673..ba30d0a3b7c830a2ca0c753a44c3330824308965 100644
--- a/install/ui/src/freeipa/trust.js
+++ b/install/ui/src/freeipa/trust.js
@@ -24,11 +24,12 @@ define([
'./menu',
'./phases',
'./reg',
+ './widget',
'./details',
'./search',
'./association',
'./entity'],
- function(IPA, $, menu, phases, reg) {
+ function(IPA, $, menu, phases, reg, mod_widget) {
var exp = IPA.trust = {};
@@ -348,13 +349,23 @@ return {
'ipantflatname',
'ipantdomainguid',
{
- $type: 'trust_fallbackgroup_select',
+ $type: 'combobox',
name: 'ipantfallbackprimarygroup',
- other_entity: 'group',
- other_field: 'cn',
empty_option: false,
- filter_options: {
- posix: true
+ search_provider: {
+ $ctor: mod_widget.JointCBSearchProvider,
+ providers: [
+ {
+ options: ['Default SMB Group']
+ },
+ {
+ $ctor: mod_widget.EntityCBSearchProvider,
+ entity: 'group',
+ filter_options: {
+ posix: true
+ }
+ }
+ ]
}
}
]
@@ -395,18 +406,6 @@ IPA.trust.config_details_facet = function(spec) {
return that;
};
-IPA.trust.fallbackgroup_select_widget = function(spec) {
- var that = IPA.entity_select_widget(spec);
-
- that.set_options = function(options) {
- // always add 'Default SMB Group', it can't be obtained by group-find.
- options.unshift('Default SMB Group');
- that.entity_select_set_options(options);
- };
-
- return that;
-};
-
exp.remove_menu_item = function() {
if (!IPA.trust_enabled) {
menu.remove_item('ipaserver/trusts');
@@ -426,9 +425,6 @@ IPA.trust.register = function() {
e.register({type: 'trust', spec: exp.trust_spec});
e.register({type: 'trustdomain', spec: exp.trustdomain_spec});
e.register({type: 'trustconfig', spec: exp.trustconfig_spec});
-
- w.register('trust_fallbackgroup_select', IPA.trust.fallbackgroup_select_widget);
- f.register('trust_fallbackgroup_select', IPA.field);
};
phases.on('registration', IPA.trust.register);
diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js
index 9240df8ef5402310ec9ceafd0b766def10c8cb48..abfdedfaf4e04feadb80d8d40f23e01770260282 100644
--- a/install/ui/src/freeipa/widget.js
+++ b/install/ui/src/freeipa/widget.js
@@ -23,14 +23,18 @@
define(['dojo/_base/array',
+ 'dojo/_base/declare',
'dojo/_base/lang',
'dojo/dom-construct',
+ 'dojo/Deferred',
'dojo/Evented',
'dojo/has',
'dojo/keys',
'dojo/on',
+ 'dojo/promise/all',
'dojo/string',
'dojo/topic',
+ 'dojo/when',
'./builder',
'./config',
'./datetime',
@@ -45,7 +49,7 @@ define(['dojo/_base/array',
'./util',
'exports'
],
- function(array, lang, construct, Evented, has, keys, on, string, topic, builder, config,
+ function(array, declare, lang, construct, Deferred, Evented, has, keys, on, all, string, topic, when, builder, config,
datetime, entity_mod, IPA, $, navigation, phases, reg, rpc, text, util, exp) {
/**
@@ -3558,6 +3562,187 @@ IPA.attribute_table_widget = function(spec) {
};
/**
+ * Combobox search provider
+ *
+ * Events: 'success', 'error'
+ *
+ * @class widget.CBSearchProvider
+ */
+exp.CBSearchProvider = declare([Evented], {
+
+ /**
+ * Options
+ * @type {Array}
+ */
+ options: [],
+
+ /**
+ * Execute search
+ * @param {string} filter Filter text
+ * @return {Deferred}
+ */
+ search: function(filter) {
+ var def = this._search(filter);
+ var that = this;
+ when(def, function(result){
+ that.emit('success', { source: that, result: result });
+ }, function(err){
+ that.emit('error', { source: that, error: err });
+ });
+ return def;
+ },
+
+ /**
+ * Actual search
+ * @protected
+ * @param {string} filter
+ * @return {Deferred}
+ */
+ _search: function(filter) {
+ return this.options;
+ },
+
+ constructor: function(spec) {
+ if (spec.options) this.options = spec.options;
+ }
+});
+
+/**
+ * Entity Combobox search provider
+ *
+ * @class widget.EntityCBSearchProvider
+ * @extends widget.CBSearchProvider
+ */
+exp.EntityCBSearchProvider = declare([exp.CBSearchProvider], {
+
+ /**
+ * Entity
+ * @property {IPA.entity|string}
+ */
+ entity: null,
+
+ /**
+ * Entity param to select as value
+ * @property {string}
+ */
+ param: null,
+
+ /**
+ * Filter for RPC search
+ * @property {Object}
+ */
+ filter_options: {},
+
+ /**
+ * @inheritDoc
+ * @protected
+ */
+ _search: function(filter) {
+ var def = new Deferred();
+ var command = this.create_search_command(filter);
+ command.on_success = lang.hitch(this, function(data, text_status, xhr) {
+ var options = this.on_success(data, text_status, xhr);
+ def.resolve(options);
+ });
+ command.on_error = lang.hitch(this, function(xhr, text_status, error_thrown) {
+ var options = this.on_error(xhr, text_status, error_thrown);
+ def.reject(error_thrown);
+ });
+ command.execute();
+ return def;
+ },
+
+ /** @protected */
+ create_search_command: function(filter) {
+ return rpc.command({
+ entity: this.entity.name,
+ method: 'find',
+ args: [filter],
+ options: this.filter_options
+ });
+ },
+
+ /** @protected */
+ on_success: function(data, text_status, xhr) {
+
+ var adapter = builder.build('adapter', 'adapter', { context: this });
+ var options = [];
+ var entries = data.result.result;
+
+ for (var i=0; i<data.result.count; i++) {
+ var entry = entries[i];
+ var values = adapter.load(entry);
+ var value = values[0];
+ options.push(value);
+ }
+
+ return options;
+ },
+
+ /** @protected */
+ on_error: function(xhr, text_status, error_thrown) {
+ },
+
+ constructor: function(spec) {
+ this.entity = reg.entity.get(spec.entity);
+ this.param = spec.param || (this.entity ? this.entity.metadata.primary_key : null);
+ this.filter_options = spec.filter_options;
+ }
+});
+
+
+/**
+ * Uses multiple search providers and combines their result
+ * @class widget.JointCBSearchProvider
+ * @extends widget.CBSearchProvider
+ */
+exp.JointCBSearchProvider = declare([exp.CBSearchProvider], {
+
+ /**
+ * @property {widget.CBSearchProvider[]}
+ */
+ providers: [],
+
+ /**
+ * @inheritDoc
+ * @protected
+ */
+ _search: function(filter) {
+
+ var def = new Deferred();
+ var defs = [];
+ for (var i=0, l=this.providers.length; i<l; i++) {
+ defs.push(this.providers[i].search(filter));
+ }
+ all(defs).then(function(results) {
+ var res = [];
+ for (var j=0, k=results.length; j<k; j++) {
+ res.push.apply(res, results[j]);
+ }
+ def.resolve(res);
+ } , function(errors) {
+ def.reject(errors);
+ });
+ return def;
+ },
+
+ /**
+ * Constructor
+ *
+ * Spec options:
+ *
+ * * providers - list of spec or search providers
+ */
+ constructor: function(spec) {
+ this.providers = builder.build(null, spec.providers || [], {}, {
+ $ctor: exp.CBSearchProvider
+ });
+ }
+});
+
+
+
+/**
* Combobox widget
*
* Widget which allows to select a value from a predefined set or write custom
@@ -3584,13 +3769,16 @@ IPA.combobox_widget = function(spec) {
var that = IPA.text_widget(spec);
- that.editable = spec.editable;
+ that.editable = spec.editable === undefined ? false : spec.editable;
that.searchable = spec.searchable;
that.size = spec.size || 5;
that.empty_option = spec.empty_option === undefined ? true : spec.empty_option;
that.options = spec.options || [];
that.z_index = spec.z_index ? spec.z_index + 9000000 : 9000000;
that.base_css_class = that.base_css_class + ' combobox-widget';
+ that.search_provider = builder.build(null, spec.search_provider, {}, {
+ $ctor: exp.CBSearchProvider
+ });
that.create = function(container) {
that.widget_create(container);
@@ -3876,8 +4064,15 @@ IPA.combobox_widget = function(spec) {
that.search = function(filter, on_success, on_error) {
- that.recreate_options();
- if (on_success) on_success.call(this);
+ if (!that.search_provider) {
+ that.set_options(that.options);
+ return;
+ }
+ if (on_success) on.once(that.search_provider, 'success', on_success);
+ if (on_error) on.once(that.search_provider, 'error', on_error);
+ when(that.search_provider.search(filter), function(options) {
+ that.set_options(options);
+ });
};
that.set_options = function(options) {
@@ -4025,8 +4220,6 @@ IPA.combobox_widget = function(spec) {
* 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
*
@@ -4040,58 +4233,13 @@ IPA.entity_select_widget = function(spec) {
spec = spec || {};
spec.searchable = spec.searchable === undefined ? true : spec.searchable;
-
+ spec.search_provider = spec.search_provider || {
+ $ctor: exp.EntityCBSearchProvider,
+ entity: spec.other_entity,
+ param: spec.other_field,
+ filter_options: spec.filter_options || {}
+ };
var that = IPA.combobox_widget(spec);
-
- that.other_entity = IPA.get_entity(spec.other_entity);
- that.other_field = spec.other_field;
-
- that.options = spec.options || [];
- that.filter_options = spec.filter_options || {};
-
- that.create_search_command = function(filter) {
- return rpc.command({
- entity: that.other_entity.name,
- method: 'find',
- args: [filter],
- options: that.filter_options
- });
- };
-
- that.search = function(filter, on_success, on_error) {
-
- that.on_search_success = on_success;
-
- var command = that.create_search_command(filter);
- command.on_success = that.search_success;
- command.on_error = on_error;
-
- command.execute();
- };
-
- that.search_success = function(data, text_status, xhr) {
-
- var adapter = builder.build('adapter', 'adapter', { context: that });
-
- //get options
- var options = [];
-
- var entries = data.result.result;
- for (var i=0; i<data.result.count; i++) {
- var entry = entries[i];
- var values = adapter.load(entry, that.other_field);
- var value = values[0];
-
- options.push(value);
- }
-
- that.set_options(options);
-
- if (that.on_search_success) that.on_search_success.call(this, data, text_status, xhr);
- };
-
- that.entity_select_set_options = that.set_options;
-
return that;
};
--
1.9.3
_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel